in src/main/java/com/amazonaws/encryptionsdk/internal/EncryptionHandler.java [78:210]
public EncryptionHandler(
int frameSize, EncryptionMaterialsHandler result, CommitmentPolicy commitmentPolicy)
throws AwsCryptoException {
Utils.assertNonNull(result, "result");
Utils.assertNonNull(commitmentPolicy, "commitmentPolicy");
this.encryptionMaterials_ = result;
Map<String, String> encryptionContext = result.getEncryptionContext();
List<String> reqKeys = result.getRequiredEncryptionContextKeys();
Map<Boolean, Map<String, String>> partitionedEncryptionContext =
encryptionContext.entrySet().stream()
.collect(
Collectors.partitioningBy(
entry -> reqKeys.contains(entry.getKey()),
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
storedEncryptionContext_ = partitionedEncryptionContext.get(false);
reqEncryptionContext_ = partitionedEncryptionContext.get(true);
if (!commitmentPolicy.algorithmAllowedForEncrypt(result.getAlgorithm())) {
if (commitmentPolicy == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
throw new AwsCryptoException(
"Configuration conflict. Cannot encrypt due to CommitmentPolicy "
+ commitmentPolicy
+ " requiring only non-committed messages. Algorithm ID was "
+ result.getAlgorithm()
+ ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
} else {
throw new AwsCryptoException(
"Configuration conflict. Cannot encrypt due to CommitmentPolicy "
+ commitmentPolicy
+ " requiring only committed messages. Algorithm ID was "
+ result.getAlgorithm()
+ ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
}
}
this.cryptoAlgo_ = result.getAlgorithm();
this.masterKeys_ = result.getMasterKeys();
this.keyBlobs_ = result.getEncryptedDataKeys();
this.trailingSignaturePrivateKey_ = result.getTrailingSignatureKey();
if (keyBlobs_.isEmpty()) {
throw new IllegalArgumentException("No encrypted data keys in materials result");
}
if (trailingSignaturePrivateKey_ != null) {
try {
TrailingSignatureAlgorithm algorithm =
TrailingSignatureAlgorithm.forCryptoAlgorithm(cryptoAlgo_);
trailingDigest_ = MessageDigest.getInstance(algorithm.getMessageDigestAlgorithm());
trailingSig_ = Signature.getInstance(algorithm.getRawSignatureAlgorithm());
trailingSig_.initSign(trailingSignaturePrivateKey_, Utils.getSecureRandom());
} catch (final GeneralSecurityException ex) {
throw new AwsCryptoException(ex);
}
} else {
trailingDigest_ = null;
trailingSig_ = null;
}
// set default values
version_ = cryptoAlgo_.getMessageFormatVersion();
type_ = CIPHERTEXT_TYPE;
nonceLen_ = cryptoAlgo_.getNonceLen();
ContentType contentType;
if (frameSize > 0) {
contentType = ContentType.FRAME;
} else if (frameSize == 0) {
contentType = ContentType.SINGLEBLOCK;
} else {
throw Utils.cannotBeNegative("Frame size");
}
// Construct the headers
// Included here rather than as a sub-routine so we can set final variables.
// This way we can avoid calculating the keys more times than we need.
//
// AAD: MUST be the serialization of the encryption context in the encryption materials, and
// this serialization MUST NOT contain any key value pairs listed in the encryption material's
// required encryption context keys.
final byte[] storedEncryptionContextBytes =
EncryptionContextSerializer.serialize(storedEncryptionContext_);
final CiphertextHeaders unsignedHeaders =
new CiphertextHeaders(
type_, cryptoAlgo_, storedEncryptionContextBytes, keyBlobs_, contentType, frameSize);
// We use a deterministic IV of zero for the header authentication.
unsignedHeaders.setHeaderNonce(new byte[nonceLen_]);
// If using a committing crypto algorithm, we also need to calculate the commitment value along
// with the key derivation
if (cryptoAlgo_.isCommitting()) {
final CommittedKey committedKey =
CommittedKey.generate(
cryptoAlgo_, result.getCleartextDataKey(), unsignedHeaders.getMessageId());
unsignedHeaders.setSuiteData(committedKey.getCommitment());
encryptionKey_ = committedKey.getKey();
} else {
try {
encryptionKey_ =
cryptoAlgo_.getEncryptionKeyFromDataKey(result.getCleartextDataKey(), unsignedHeaders);
} catch (final InvalidKeyException ex) {
throw new AwsCryptoException(ex);
}
}
// The authenticated only encryption context is all encryption context key-value pairs where the
// key exists in Required Encryption Context Keys. It is then serialized according to the
// message header Key Value Pairs.
final byte[] reqEncryptionContextBytes =
EncryptionContextSerializer.serialize(reqEncryptionContext_);
ciphertextHeaders_ = signCiphertextHeaders(unsignedHeaders, reqEncryptionContextBytes);
ciphertextHeaderBytes_ = ciphertextHeaders_.toByteArray();
byte[] messageId_ = ciphertextHeaders_.getMessageId();
switch (contentType) {
case FRAME:
contentCryptoHandler_ =
new FrameEncryptionHandler(
encryptionKey_, nonceLen_, cryptoAlgo_, messageId_, frameSize);
break;
case SINGLEBLOCK:
contentCryptoHandler_ =
new BlockEncryptionHandler(encryptionKey_, nonceLen_, cryptoAlgo_, messageId_);
break;
default:
// should never get here because a valid content type is always
// set above based on the frame size.
throw new AwsCryptoException("Unknown content type.");
}
}