// 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.InsecureSecretKeyAccess; import com.google.crypto.tink.Key; import com.google.crypto.tink.Parameters; import com.google.crypto.tink.TinkProtoParametersFormat; import com.google.crypto.tink.internal.MutableKeyCreationRegistry; import com.google.crypto.tink.internal.MutablePrimitiveRegistry; import com.google.crypto.tink.internal.MutableSerializationRegistry; import com.google.crypto.tink.internal.ProtoKeySerialization; import com.google.crypto.tink.proto.KeyData.KeyMaterialType; import com.google.crypto.tink.proto.KeyTemplate; import com.google.crypto.tink.proto.OutputPrefixType; import com.google.protobuf.ByteString; 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: * *
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)
throws GeneralSecurityException {
if (!isSupportedDekKeyType(dekTemplate.getTypeUrl())) {
throw new IllegalArgumentException(
"Unsupported DEK key type: "
+ dekTemplate.getTypeUrl()
+ ". Only Tink AEAD key types are supported.");
}
this.typeUrlForParsing = dekTemplate.getTypeUrl();
this.parametersForNewKeys = getRawParameters(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 { // This serializes the parameters, changes output prefix to raw, and parses it again. // It would be better to reject the parameters immediately if it was a non-raw object, but // this might break someone, so we keep as is. 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 { Key key = MutableKeyCreationRegistry.globalInstance() .createKey(parametersForNewKeys, /* idRequirement= */ null); ProtoKeySerialization serialization = MutableSerializationRegistry.globalInstance() .serializeKey(key, ProtoKeySerialization.class, InsecureSecretKeyAccess.get()); byte[] dek = serialization.getValue().toByteArray(); // Wrap it with remote. byte[] encryptedDek = remote.encrypt(dek, EMPTY_AAD); if (encryptedDek.length > MAX_LENGTH_ENCRYPTED_DEK) { throw new GeneralSecurityException("length of encrypted DEK too large"); } // Use DEK to encrypt plaintext. Aead aead = MutablePrimitiveRegistry.globalInstance().getPrimitive(key, 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 > MAX_LENGTH_ENCRYPTED_DEK || encryptedDekSize > (ciphertext.length - LENGTH_ENCRYPTED_DEK)) { throw new GeneralSecurityException("length of encrypted DEK too large"); } 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. ProtoKeySerialization serialization = ProtoKeySerialization.create( typeUrlForParsing, ByteString.copyFrom(dek), KeyMaterialType.SYMMETRIC, OutputPrefixType.RAW, /* idRequirement= */ null); Key key = MutableSerializationRegistry.globalInstance() .parseKey(serialization, InsecureSecretKeyAccess.get()); Aead aead = MutablePrimitiveRegistry.globalInstance().getPrimitive(key, 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(); } }