All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.crypto.tink.aead.KmsEnvelopeAead Maven / Gradle / Ivy

// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package com.google.crypto.tink.aead; // instead of subtle, because it depends on KeyTemplate.

import com.google.crypto.tink.Aead;
import com.google.crypto.tink.Registry;
import com.google.crypto.tink.TinkProtoParametersFormat;
import com.google.crypto.tink.proto.KeyTemplate;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * This primitive implements 
 * envelope encryption.
 *
 * 

In envelope encryption, a user generates a data encryption key (DEK) locally, encrypts data * with the DEK, sends the DEK to a KMS to be encrypted (with a key managed by KMS), and then stores * the encrypted DEK with the encrypted data. At a later point, a user can retrieve the encrypted * data and the encyrpted DEK, use the KMS to decrypt the DEK, and use the decrypted DEK to decrypt * the data. * *

The ciphertext structure is as follows: * *

    *
  • Length of the encrypted DEK: 4 bytes. *
  • Encrypted DEK: variable length that is equal to the value specified in the last 4 bytes. *
  • AEAD payload: variable length. *
*/ public final class KmsEnvelopeAead implements Aead { private static final byte[] EMPTY_AAD = new byte[0]; private final KeyTemplate dekTemplate; private final Aead remote; private static final int LENGTH_ENCRYPTED_DEK = 4; private static Set listSupportedDekKeyTypes() { HashSet dekKeyTypeUrls = new HashSet<>(); dekKeyTypeUrls.add("type.googleapis.com/google.crypto.tink.AesGcmKey"); dekKeyTypeUrls.add("type.googleapis.com/google.crypto.tink.ChaCha20Poly1305Key"); dekKeyTypeUrls.add("type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key"); dekKeyTypeUrls.add("type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey"); dekKeyTypeUrls.add("type.googleapis.com/google.crypto.tink.AesGcmSivKey"); dekKeyTypeUrls.add("type.googleapis.com/google.crypto.tink.AesEaxKey"); return Collections.unmodifiableSet(dekKeyTypeUrls); } private static final Set supportedDekKeyTypes = listSupportedDekKeyTypes(); public static boolean isSupportedDekKeyType(String dekKeyTypeUrl) { return supportedDekKeyTypes.contains(dekKeyTypeUrl); } /** * Creates a new KmsEnvelopeAead. * *

This function should be avoided. Instead, if you use this with one of the predefined key * templates, call create with the corresponding parameters object. * *

For example, if you use: * *

Aead aead = new KmsEnvelopeAead(AeadKeyTemplates.AES128_GCM, remote) you should * replace this with: * *

Aead aead = KmsEnvelopeAead.create(PredefinedAeadParameters.AES128_GCM, remote) * * @deprecated Instead, call {@code KmsEnvelopeAead.create} as explained above. */ @Deprecated public KmsEnvelopeAead(KeyTemplate dekTemplate, Aead remote) { if (!isSupportedDekKeyType(dekTemplate.getTypeUrl())) { throw new IllegalArgumentException( "Unsupported DEK key type: " + dekTemplate.getTypeUrl() + ". Only Tink AEAD key types are supported."); } this.dekTemplate = dekTemplate; this.remote = remote; } /** * Creates a new instance of Tink's KMS Envelope AEAD. * *

{@code dekParameters} must be any of these Tink AEAD parameters (any other will be * rejected): {@link AesGcmParameters}, {@link ChaCha20Poly1305Parameters}, {@link * XChaCha20Poly1305Parameters}, {@link AesCtrHmacAeadParameters}, {@link AesGcmSivParameters}, or * {@link AesEaxParameters}. */ public static Aead create(AeadParameters dekParameters, Aead remote) throws GeneralSecurityException { KeyTemplate dekTemplate; try { dekTemplate = KeyTemplate.parseFrom( TinkProtoParametersFormat.serialize(dekParameters), ExtensionRegistryLite.getEmptyRegistry()); } catch (InvalidProtocolBufferException e) { throw new GeneralSecurityException(e); } return new KmsEnvelopeAead(dekTemplate, remote); } @Override public byte[] encrypt(final byte[] plaintext, final byte[] associatedData) throws GeneralSecurityException { // Generate a new DEK. byte[] dek = Registry.newKeyData(dekTemplate).getValue().toByteArray(); // Wrap it with remote. byte[] encryptedDek = remote.encrypt(dek, EMPTY_AAD); // Use DEK to encrypt plaintext. Aead aead = Registry.getPrimitive(dekTemplate.getTypeUrl(), dek, Aead.class); byte[] payload = aead.encrypt(plaintext, associatedData); // Build ciphertext protobuf and return result. return buildCiphertext(encryptedDek, payload); } @Override public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData) throws GeneralSecurityException { try { ByteBuffer buffer = ByteBuffer.wrap(ciphertext); int encryptedDekSize = buffer.getInt(); if (encryptedDekSize <= 0 || encryptedDekSize > (ciphertext.length - LENGTH_ENCRYPTED_DEK)) { throw new GeneralSecurityException("invalid ciphertext"); } byte[] encryptedDek = new byte[encryptedDekSize]; buffer.get(encryptedDek, 0, encryptedDekSize); byte[] payload = new byte[buffer.remaining()]; buffer.get(payload, 0, buffer.remaining()); // Use remote to decrypt encryptedDek. byte[] dek = remote.decrypt(encryptedDek, EMPTY_AAD); // Use DEK to decrypt payload. Aead aead = Registry.getPrimitive(dekTemplate.getTypeUrl(), dek, Aead.class); return aead.decrypt(payload, associatedData); } catch (IndexOutOfBoundsException | BufferUnderflowException | NegativeArraySizeException e) { throw new GeneralSecurityException("invalid ciphertext", e); } } private byte[] buildCiphertext(final byte[] encryptedDek, final byte[] payload) { return ByteBuffer.allocate(LENGTH_ENCRYPTED_DEK + encryptedDek.length + payload.length) .putInt(encryptedDek.length) .put(encryptedDek) .put(payload) .array(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy