diff --git a/.gitmodules b/.gitmodules
index eb6b6a564..b90e3bf80 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "src/test/resources/aws-encryption-sdk-test-vectors"]
path = src/test/resources/aws-encryption-sdk-test-vectors
- url = https://github.com/awslabs/private-aws-encryption-sdk-test-vectors-staging.git
+ url = https://github.com/awslabs/aws-encryption-sdk-test-vectors.git
[submodule "aws-encryption-sdk-specification"]
path = aws-encryption-sdk-specification
- url = https://github.com/awslabs/private-aws-encryption-sdk-specification-staging.git
+ url = https://github.com/awslabs/aws-encryption-sdk-specification.git
diff --git a/aws-encryption-sdk-specification b/aws-encryption-sdk-specification
index ef3420d0f..c35fbd91b 160000
--- a/aws-encryption-sdk-specification
+++ b/aws-encryption-sdk-specification
@@ -1 +1 @@
-Subproject commit ef3420d0fa8740c4a98f2e9e976d75be185473e4
+Subproject commit c35fbd91b28303d69813119088c44b5006395eb4
diff --git a/buildspec.yml b/buildspec.yml
index 365eb003e..1fc7d652a 100644
--- a/buildspec.yml
+++ b/buildspec.yml
@@ -23,3 +23,8 @@ batch:
env:
env:
image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
+ - identifier: static_analysis
+ buildspec: codebuild/static-analysis.yml
+ env:
+ env:
+ image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
diff --git a/codebuild/compliance.yml b/codebuild/static-analysis.yml
similarity index 65%
rename from codebuild/compliance.yml
rename to codebuild/static-analysis.yml
index fe25a9c37..798c1af3c 100644
--- a/codebuild/compliance.yml
+++ b/codebuild/static-analysis.yml
@@ -4,6 +4,8 @@ phases:
install:
runtime-versions:
nodejs: 12
+ java: corretto11
build:
commands:
+ - mvn com.coveo:fmt-maven-plugin:check
- ./util/test-conditions.sh
diff --git a/pom.xml b/pom.xml
index e8e4147f2..5a96f2025 100644
--- a/pom.xml
+++ b/pom.xml
@@ -199,6 +199,16 @@
+
+
+ com.coveo
+ fmt-maven-plugin
+ 2.10
+
+
+
+
+
diff --git a/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java b/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
index c16b6ed53..5268935b3 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/AwsCrypto.java
@@ -3,898 +3,904 @@
package com.amazonaws.encryptionsdk;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.Map;
-
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
import com.amazonaws.encryptionsdk.internal.*;
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Map;
/**
* Provides the primary entry-point to the AWS Encryption SDK. All encryption and decryption
- * operations should start here. Most people will want to use either
- * {@link #encryptData(MasterKeyProvider, byte[], Map)} and
- * {@link #decryptData(MasterKeyProvider, byte[])} to encrypt/decrypt things.
- *
- *
- * The core concepts (and classes) in this SDK are:
+ * operations should start here. Most people will want to use either {@link
+ * #encryptData(MasterKeyProvider, byte[], Map)} and {@link #decryptData(MasterKeyProvider, byte[])}
+ * to encrypt/decrypt things.
+ *
+ *
The core concepts (and classes) in this SDK are:
+ *
*
- * - {@link AwsCrypto}
- *
- {@link DataKey}
- *
- {@link MasterKey}
- *
- {@link MasterKeyProvider}
+ *
- {@link AwsCrypto}
+ *
- {@link DataKey}
+ *
- {@link MasterKey}
+ *
- {@link MasterKeyProvider}
*
*
- *
- * {@link AwsCrypto} provides the primary way to encrypt/decrypt data. It can operate on
+ *
{@link AwsCrypto} provides the primary way to encrypt/decrypt data. It can operate on
* byte-arrays, streams, or {@link java.lang.String Strings}. This data is encrypted using the
* specifed {@link CryptoAlgorithm} and a {@link DataKey} which is unique to each encrypted message.
* This {@code DataKey} is then encrypted using one (or more) {@link MasterKey MasterKeys}. The
* process is reversed on decryption with the code selecting a copy of the {@code DataKey} protected
* by a usable {@code MasterKey}, decrypting the {@code DataKey}, and then decrypted the message.
*
- *
- * The main way to get a {@code MasterKey} is through the use of a {@link MasterKeyProvider}. This
- * provides a common interface for the AwsEncryptionSdk to find and retrieve {@code MasterKeys}.
- * (Some {@code MasterKeys} can also be constructed directly.)
+ *
The main way to get a {@code MasterKey} is through the use of a {@link MasterKeyProvider}.
+ * This provides a common interface for the AwsEncryptionSdk to find and retrieve {@code
+ * MasterKeys}. (Some {@code MasterKeys} can also be constructed directly.)
*
- *
- * {@code AwsCrypto} uses the {@code MasterKeyProvider} to determine which {@code MasterKeys} should
- * be used to encrypt the {@code DataKeys} by calling
- * {@link MasterKeyProvider#getMasterKeysForEncryption(MasterKeyRequest)} . When more than one
- * {@code MasterKey} is returned, the first {@code MasterKeys} is used to create the
- * {@code DataKeys} by calling {@link MasterKey#generateDataKey(CryptoAlgorithm,java.util.Map)} .
- * All of the other {@code MasterKeys} are then used to re-encrypt that {@code DataKey} with
- * {@link MasterKey#encryptDataKey(CryptoAlgorithm,java.util.Map,DataKey)} . This list of
- * {@link EncryptedDataKey EncryptedDataKeys} (the same {@code DataKey} possibly encrypted multiple
- * times) is stored in the {@link com.amazonaws.encryptionsdk.model.CiphertextHeaders}.
+ *
{@code AwsCrypto} uses the {@code MasterKeyProvider} to determine which {@code MasterKeys}
+ * should be used to encrypt the {@code DataKeys} by calling {@link
+ * MasterKeyProvider#getMasterKeysForEncryption(MasterKeyRequest)} . When more than one {@code
+ * MasterKey} is returned, the first {@code MasterKeys} is used to create the {@code DataKeys} by
+ * calling {@link MasterKey#generateDataKey(CryptoAlgorithm,java.util.Map)} . All of the other
+ * {@code MasterKeys} are then used to re-encrypt that {@code DataKey} with {@link
+ * MasterKey#encryptDataKey(CryptoAlgorithm,java.util.Map,DataKey)} . This list of {@link
+ * EncryptedDataKey EncryptedDataKeys} (the same {@code DataKey} possibly encrypted multiple times)
+ * is stored in the {@link com.amazonaws.encryptionsdk.model.CiphertextHeaders}.
*
- *
- * {@code AwsCrypto} also uses the {@code MasterKeyProvider} to decrypt one of the
- * {@link EncryptedDataKey EncryptedDataKeys} from the header to retrieve the actual {@code DataKey}
+ *
{@code AwsCrypto} also uses the {@code MasterKeyProvider} to decrypt one of the {@link
+ * EncryptedDataKey EncryptedDataKeys} from the header to retrieve the actual {@code DataKey}
* necessary to decrypt the message.
*
- *
- * Any place a {@code MasterKeyProvider} is used, a {@link MasterKey} can be used instead. The
+ *
Any place a {@code MasterKeyProvider} is used, a {@link MasterKey} can be used instead. The
* {@code MasterKey} will behave as a {@code MasterKeyProvider} which is only capable of providing
* itself. This is often useful when only one {@code MasterKey} is being used.
*
- *
- * Note regarding the use of generics: This library makes heavy use of generics to provide type
+ *
Note regarding the use of generics: This library makes heavy use of generics to provide type
* safety to advanced developers. The great majority of users should be able to just use the
* provided type parameters or the {@code ?} wildcard.
*/
@SuppressWarnings("WeakerAccess") // this is a public API
public class AwsCrypto {
- private static final Map EMPTY_MAP = Collections.emptyMap();
-
- // These are volatile because we allow unsynchronized writes via our setters,
- // and without setting volatile we could see strange results.
- // E.g. copying these to a local might give different values on subsequent reads from the local.
- // By setting them volatile we ensure that proper memory barriers are applied
- // to ensure things behave in a sensible manner.
- private volatile CryptoAlgorithm encryptionAlgorithm_ = null;
- private volatile int encryptionFrameSize_ = getDefaultFrameSize();
-
- private static final CommitmentPolicy DEFAULT_COMMITMENT_POLICY = CommitmentPolicy.RequireEncryptRequireDecrypt;
- private final CommitmentPolicy commitmentPolicy_;
-
- /**
- * The maximum number of encrypted data keys to unwrap (resp. wrap) on decrypt (resp. encrypt), if positive.
- * If zero, do not limit EDKs.
- */
- private final int maxEncryptedDataKeys_;
-
- private AwsCrypto(Builder builder) {
- commitmentPolicy_ = builder.commitmentPolicy_ == null ? DEFAULT_COMMITMENT_POLICY : builder.commitmentPolicy_;
- if (builder.encryptionAlgorithm_ != null && !commitmentPolicy_.algorithmAllowedForEncrypt(builder.encryptionAlgorithm_)) {
- if (commitmentPolicy_ == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
- throw new AwsCryptoException("Configuration conflict. Cannot encrypt due to CommitmentPolicy " +
- commitmentPolicy_ + " requiring only non-committed messages. Algorithm ID was " +
- builder.encryptionAlgorithm_ +
- ". 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 " +
- builder.encryptionAlgorithm_ +
- ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
- }
- }
- encryptionAlgorithm_ = builder.encryptionAlgorithm_;
- encryptionFrameSize_ = builder.encryptionFrameSize_;
- maxEncryptedDataKeys_ = builder.maxEncryptedDataKeys_;
- }
-
- public static class Builder {
- private CryptoAlgorithm encryptionAlgorithm_;
- private int encryptionFrameSize_ = getDefaultFrameSize();
- private CommitmentPolicy commitmentPolicy_;
- private int maxEncryptedDataKeys_ = CiphertextHeaders.NO_MAX_ENCRYPTED_DATA_KEYS;
-
- private Builder() {}
-
- private Builder(final AwsCrypto client) {
- encryptionAlgorithm_ = client.encryptionAlgorithm_;
- encryptionFrameSize_ = client.encryptionFrameSize_;
- commitmentPolicy_ = client.commitmentPolicy_;
- maxEncryptedDataKeys_ = client.maxEncryptedDataKeys_;
- }
-
- /**
- * Sets the {@link CryptoAlgorithm} to encrypt with.
- * The Aws Crypto client will use the last crypto algorithm set with
- * either {@link AwsCrypto.Builder#withEncryptionAlgorithm(CryptoAlgorithm)} or
- * {@link #setEncryptionAlgorithm(CryptoAlgorithm)} to encrypt with.
- *
- * @param encryptionAlgorithm The {@link CryptoAlgorithm}
- * @return The Builder, for method chaining
- */
- public Builder withEncryptionAlgorithm(CryptoAlgorithm encryptionAlgorithm) {
- this.encryptionAlgorithm_ = encryptionAlgorithm;
- return this;
- }
-
- /**
- * Sets the frame size of the encrypted messages that the Aws Crypto client produces.
- * The Aws Crypto client will use the last frame size set with
- * either {@link AwsCrypto.Builder#withEncryptionFrameSize(int)} or
- * {@link #setEncryptionFrameSize(int)}.
- *
- * @param frameSize The frame size to produce encrypted messages with.
- * @return The Builder, for method chaining
- */
- public Builder withEncryptionFrameSize(int frameSize) {
- this.encryptionFrameSize_ = frameSize;
- return this;
- }
-
- /**
- * Sets the {@link CommitmentPolicy} of this Aws Crypto client.
- *
- * @param commitmentPolicy The commitment policy to enforce during encryption and decryption
- * @return The Builder, for method chaining
- */
- public Builder withCommitmentPolicy(CommitmentPolicy commitmentPolicy) {
- Utils.assertNonNull(commitmentPolicy, "commitmentPolicy");
- this.commitmentPolicy_ = commitmentPolicy;
- return this;
- }
-
- /**
- * Sets the maximum number of encrypted data keys that this Aws Crypto client will wrap when
- * encrypting, or unwrap when decrypting, a single message.
- *
- * @param maxEncryptedDataKeys The maximum number of encrypted data keys; must be positive
- * @return The Builder, for method chaining
- */
- public Builder withMaxEncryptedDataKeys(int maxEncryptedDataKeys) {
- if (maxEncryptedDataKeys < 1) {
- throw new IllegalArgumentException("maxEncryptedDataKeys must be positive");
- }
- this.maxEncryptedDataKeys_ = maxEncryptedDataKeys;
- return this;
- }
-
- public AwsCrypto build() {
- return new AwsCrypto(this);
- }
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public Builder toBuilder() {
- return new Builder(this);
- }
-
- public static AwsCrypto standard() {
- return AwsCrypto.builder().build();
- }
-
- /**
- * Returns the frame size to use for encryption when none is explicitly selected. Currently it
- * is 4096.
- */
- public static int getDefaultFrameSize() {
- return 4096;
- }
-
- /**
- * Sets the {@link CryptoAlgorithm} to use when encrypting data. This has no impact on
- * decryption.
- */
- public void setEncryptionAlgorithm(final CryptoAlgorithm alg) {
- if (!commitmentPolicy_.algorithmAllowedForEncrypt(alg)) {
- if (commitmentPolicy_ == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
- throw new AwsCryptoException("Configuration conflict. Cannot encrypt due to CommitmentPolicy " +
- commitmentPolicy_ + " requiring only non-committed messages. Algorithm ID was " +
- alg + ". 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 " +
- alg + ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
- }
- }
- encryptionAlgorithm_ = alg;
- }
-
- public CryptoAlgorithm getEncryptionAlgorithm() {
- return encryptionAlgorithm_;
- }
-
- /**
- * Sets the framing size to use when encrypting data. This has no impact on decryption.
- * If {@code frameSize} is 0, then framing is disabled and the entire plaintext will be encrypted
- * in a single block.
- *
- * Note that during encryption arrays of this size will be allocated. Using extremely large frame sizes may pose
- * compatibility issues when the decryptor is running on 32-bit systems. Additionally, Java VM limits may set a
- * platform-specific upper bound to frame sizes.
- */
- public void setEncryptionFrameSize(final int frameSize) {
- if (frameSize < 0) {
- throw new IllegalArgumentException("frameSize must be non-negative");
- }
-
- encryptionFrameSize_ = frameSize;
- }
-
- public int getEncryptionFrameSize() {
- return encryptionFrameSize_;
- }
-
- /**
- * Returns the best estimate for the output length of encrypting a plaintext with the provided
- * {@code plaintextSize} and {@code encryptionContext}. The actual ciphertext may be shorter.
- *
- * This method is equivalent to calling {@link #estimateCiphertextSize(CryptoMaterialsManager, int, Map)} with a
- * {@link DefaultCryptoMaterialsManager} based on the given provider.
- */
- public > long estimateCiphertextSize(
- final MasterKeyProvider provider,
- final int plaintextSize,
- final Map encryptionContext
- ) {
- return estimateCiphertextSize(new DefaultCryptoMaterialsManager(provider), plaintextSize, encryptionContext);
- }
-
- /**
- * Returns the best estimate for the output length of encrypting a plaintext with the provided
- * {@code plaintextSize} and {@code encryptionContext}. The actual ciphertext may be shorter.
- */
- public long estimateCiphertextSize(
- CryptoMaterialsManager materialsManager,
- final int plaintextSize,
- final Map encryptionContext
- ) {
- EncryptionMaterialsRequest request = EncryptionMaterialsRequest.newBuilder()
- .setContext(encryptionContext)
- .setRequestedAlgorithm(getEncryptionAlgorithm())
- // We're not actually encrypting any data, so don't consume any bytes from the cache's limits. We do need to
- // pass /something/ though, or the cache will be bypassed (as it'll assume this is a streaming encrypt of
- // unknown size).
- .setPlaintextSize(0)
- .setCommitmentPolicy(commitmentPolicy_)
- .build();
-
- final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
- getEncryptionFrameSize(),
- checkAlgorithm(materialsManager.getMaterialsForEncrypt(request)),
- commitmentPolicy_
- );
-
- return cryptoHandler.estimateOutputSize(plaintextSize);
- }
-
- /**
- * Returns the equivalent to calling
- * {@link #estimateCiphertextSize(MasterKeyProvider, int, Map)} with an empty
- * {@code encryptionContext}.
- */
- public > long estimateCiphertextSize(
- final MasterKeyProvider provider,
- final int plaintextSize
- ) {
- return estimateCiphertextSize(provider, plaintextSize, EMPTY_MAP);
- }
-
- /**
- * Returns the equivalent to calling
- * {@link #estimateCiphertextSize(CryptoMaterialsManager, int, Map)} with an empty
- * {@code encryptionContext}.
- */
- public long estimateCiphertextSize(
- final CryptoMaterialsManager materialsManager,
- final int plaintextSize
- ) {
- return estimateCiphertextSize(materialsManager, plaintextSize, EMPTY_MAP);
- }
-
- /**
- * Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
- * DataKeys} that are in turn protected by {@link MasterKey MasterKeys} provided by
- * {@code provider}.
- *
- * This method is equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)} using a
- * {@link DefaultCryptoMaterialsManager} based on the given provider.
- */
- public > CryptoResult encryptData(
- final MasterKeyProvider provider,
- final byte[] plaintext,
- final Map encryptionContext
- ) {
- //noinspection unchecked
- return (CryptoResult)
- encryptData(new DefaultCryptoMaterialsManager(provider), plaintext, encryptionContext);
- }
-
- /**
- * Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
- * DataKeys} that are in turn protected by the given CryptoMaterialsProvider.
- */
- public CryptoResult encryptData(
- CryptoMaterialsManager materialsManager,
- final byte[] plaintext,
- final Map encryptionContext
- ) {
- EncryptionMaterialsRequest request = EncryptionMaterialsRequest.newBuilder()
- .setContext(encryptionContext)
- .setRequestedAlgorithm(getEncryptionAlgorithm())
- .setPlaintext(plaintext)
- .setCommitmentPolicy(commitmentPolicy_)
- .build();
-
- EncryptionMaterials encryptionMaterials = checkMaxEncryptedDataKeys(checkAlgorithm(materialsManager.getMaterialsForEncrypt(request)));
- final MessageCryptoHandler cryptoHandler = new EncryptionHandler(
- getEncryptionFrameSize(),
- encryptionMaterials,
- commitmentPolicy_
- );
-
- final int outSizeEstimate = cryptoHandler.estimateOutputSize(plaintext.length);
- final byte[] out = new byte[outSizeEstimate];
- int outLen = cryptoHandler.processBytes(plaintext, 0, plaintext.length, out, 0).getBytesWritten();
- outLen += cryptoHandler.doFinal(out, outLen);
-
- final byte[] outBytes = Utils.truncate(out, outLen);
-
- //noinspection unchecked
- return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
- }
-
- /**
- * Returns the equivalent to calling {@link #encryptData(MasterKeyProvider, byte[], Map)} with
- * an empty {@code encryptionContext}.
- */
- public > CryptoResult encryptData(final MasterKeyProvider provider,
- final byte[] plaintext) {
- return encryptData(provider, plaintext, EMPTY_MAP);
- }
-
- /**
- * Returns the equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)} with
- * an empty {@code encryptionContext}.
- */
- public CryptoResult encryptData(
- final CryptoMaterialsManager materialsManager,
- final byte[] plaintext
- ) {
- return encryptData(materialsManager, plaintext, EMPTY_MAP);
- }
-
- /**
- * Calls {@link #encryptData(MasterKeyProvider, byte[], Map)} on the UTF-8 encoded bytes of
- * {@code plaintext} and base64 encodes the result.
- * @deprecated Use the {@link #encryptData(MasterKeyProvider, byte[], Map)} and
- * {@link #decryptData(MasterKeyProvider, byte[])} APIs instead. {@code encryptString} and {@code decryptString}
- * work as expected if you use them together. However, to work with other language implementations of the AWS
- * Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
- * {@code decryptString}. These deprecated APIs will be removed in the future.
- */
- @Deprecated
- public > CryptoResult encryptString(
- final MasterKeyProvider provider,
- final String plaintext,
- final Map encryptionContext
- ) {
- //noinspection unchecked
- return (CryptoResult)
- encryptString(new DefaultCryptoMaterialsManager(provider), plaintext, encryptionContext);
- }
-
- /**
- * Calls {@link #encryptData(CryptoMaterialsManager, byte[], Map)} on the UTF-8 encoded bytes of
- * {@code plaintext} and base64 encodes the result.
- * @deprecated Use the {@link #encryptData(CryptoMaterialsManager, byte[], Map)} and
- * {@link #decryptData(CryptoMaterialsManager, byte[])} APIs instead. {@code encryptString} and {@code decryptString}
- * work as expected if you use them together. However, to work with other language implementations of the AWS
- * Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
- * {@code decryptString}. These deprecated APIs will be removed in the future.
- */
- @Deprecated
- public CryptoResult encryptString(
- CryptoMaterialsManager materialsManager,
- final String plaintext,
- final Map encryptionContext
- ) {
- final CryptoResult ctBytes = encryptData(
- materialsManager,
- plaintext.getBytes(StandardCharsets.UTF_8),
- encryptionContext
- );
- return new CryptoResult<>(Utils.encodeBase64String(ctBytes.getResult()),
- ctBytes.getMasterKeys(), ctBytes.getHeaders());
- }
-
- /**
- * Returns the equivalent to calling {@link #encryptString(MasterKeyProvider, String, Map)} with
- * an empty {@code encryptionContext}.
- * @deprecated Use the {@link #encryptData(MasterKeyProvider, byte[])} and
- * {@link #decryptData(MasterKeyProvider, byte[])} APIs instead. {@code encryptString} and {@code decryptString}
- * work as expected if you use them together. However, to work with other language implementations of the AWS
- * Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
- * {@code decryptString}. These deprecated APIs will be removed in the future.
- */
- @Deprecated
- public > CryptoResult encryptString(final MasterKeyProvider provider,
- final String plaintext) {
- return encryptString(provider, plaintext, EMPTY_MAP);
- }
-
- /**
- * Returns the equivalent to calling {@link #encryptString(CryptoMaterialsManager, String, Map)} with
- * an empty {@code encryptionContext}.
- * @deprecated Use the {@link #encryptData(CryptoMaterialsManager, byte[])} and
- * {@link #decryptData(CryptoMaterialsManager, byte[])} APIs instead. {@code encryptString} and {@code decryptString}
- * work as expected if you use them together. However, to work with other language implementations of the AWS
- * Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
- * {@code decryptString}. These deprecated APIs will be removed in the future.
- */
- @Deprecated
- public CryptoResult encryptString(
- final CryptoMaterialsManager materialsManager,
- final String plaintext
- ) {
- return encryptString(materialsManager, plaintext, EMPTY_MAP);
- }
-
- /**
- * Decrypts the provided {@code ciphertext} by requesting that the {@code provider} unwrap any
- * usable {@link DataKey} in the ciphertext and then decrypts the ciphertext using that
- * {@code DataKey}.
- */
- public > CryptoResult decryptData(final MasterKeyProvider provider,
- final byte[] ciphertext) {
- return decryptData(Utils.assertNonNull(provider, "provider"), new
- ParsedCiphertext(ciphertext, maxEncryptedDataKeys_));
- }
-
- /**
- * Decrypts the provided ciphertext by delegating to the provided materialsManager to obtain the decrypted
- * {@link DataKey}.
- *
- * @param materialsManager the {@link CryptoMaterialsManager} to use for decryption operations.
- * @param ciphertext the ciphertext to attempt to decrypt.
- * @return the {@link CryptoResult} with the decrypted data.
- */
- public CryptoResult decryptData(
- final CryptoMaterialsManager materialsManager,
- final byte[] ciphertext
- ) {
- return decryptData(Utils.assertNonNull(materialsManager, "materialsManager"),
- new ParsedCiphertext(ciphertext, maxEncryptedDataKeys_));
- }
-
- /**
- * @see #decryptData(MasterKeyProvider, byte[])
- */
- @SuppressWarnings("unchecked")
- public > CryptoResult decryptData(
- final MasterKeyProvider provider, final ParsedCiphertext ciphertext) {
- Utils.assertNonNull(provider, "provider");
- return (CryptoResult) decryptData(new DefaultCryptoMaterialsManager(provider), ciphertext);
- }
-
- /**
- * @see #decryptData(CryptoMaterialsManager, byte[])
- */
- public CryptoResult decryptData(
- final CryptoMaterialsManager materialsManager,
- final ParsedCiphertext ciphertext
- ) {
- Utils.assertNonNull(materialsManager, "materialsManager");
-
- final MessageCryptoHandler cryptoHandler =
- DecryptionHandler.create(materialsManager, ciphertext, commitmentPolicy_,
- SignaturePolicy.AllowEncryptAllowDecrypt, maxEncryptedDataKeys_);
-
- final byte[] ciphertextBytes = ciphertext.getCiphertext();
- final int contentLen = ciphertextBytes.length - ciphertext.getOffset();
- final int outSizeEstimate = cryptoHandler.estimateOutputSize(contentLen);
- final byte[] out = new byte[outSizeEstimate];
- final ProcessingSummary processed = cryptoHandler.processBytes(ciphertextBytes, ciphertext.getOffset(),
- contentLen, out,
- 0);
- if (processed.getBytesProcessed() != contentLen) {
- throw new BadCiphertextException("Unable to process entire ciphertext. May have trailing data.");
- }
- int outLen = processed.getBytesWritten();
- outLen += cryptoHandler.doFinal(out, outLen);
-
- final byte[] outBytes = Utils.truncate(out, outLen);
-
- //noinspection unchecked
- return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
- }
-
- /**
- * Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
- * UTF-8 encoded string.
- *
- * @see #decryptData(MasterKeyProvider, byte[])
- * @deprecated Use the {@link #decryptData(MasterKeyProvider, byte[])} and
- * {@link #encryptData(MasterKeyProvider, byte[], Map)} APIs instead. {@code encryptString} and {@code decryptString}
- * work as expected if you use them together. However, to work with other language implementations of the AWS
- * Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
- * {@code decryptString}. These deprecated APIs will be removed in the future.
- */
- @Deprecated
- @SuppressWarnings("unchecked")
- public > CryptoResult decryptString(
- final MasterKeyProvider provider,
- final String ciphertext
- ) {
- return (CryptoResult) decryptString(new DefaultCryptoMaterialsManager(provider), ciphertext);
- }
-
- /**
- * Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
- * UTF-8 encoded string.
- *
- * @see #decryptData(CryptoMaterialsManager, byte[])
- * @deprecated Use the {@link #decryptData(CryptoMaterialsManager, byte[])} and
- * {@link #encryptData(CryptoMaterialsManager, byte[], Map)} APIs instead. {@code encryptString} and {@code decryptString}
- * work as expected if you use them together. However, to work with other language implementations of the AWS
- * Encryption SDK, you need to base64-decode the output of {@code encryptString} and base64-encode the input to
- * {@code decryptString}. These deprecated APIs will be removed in the future.
- */
- @Deprecated
- public CryptoResult decryptString(final CryptoMaterialsManager provider,
- final String ciphertext) {
- Utils.assertNonNull(provider, "provider");
- final byte[] ciphertextBytes;
- try {
- ciphertextBytes = Utils.decodeBase64String(Utils.assertNonNull(ciphertext, "ciphertext"));
- } catch (final IllegalArgumentException ex) {
- throw new BadCiphertextException("Invalid base 64", ex);
- }
- final CryptoResult ptBytes = decryptData(provider, ciphertextBytes);
- //noinspection unchecked
- return new CryptoResult(
- new String(ptBytes.getResult(), StandardCharsets.UTF_8),
- ptBytes.getMasterKeys(), ptBytes.getHeaders());
- }
-
- /**
- * Returns a {@link CryptoOutputStream} which encrypts the data prior to passing it onto the
- * underlying {@link OutputStream}.
- *
- * @see #encryptData(MasterKeyProvider, byte[], Map)
- * @see javax.crypto.CipherOutputStream
- */
- public > CryptoOutputStream createEncryptingStream(
- final MasterKeyProvider provider,
- final OutputStream os,
- final Map encryptionContext
- ) {
- //noinspection unchecked
- return (CryptoOutputStream)
- createEncryptingStream(new DefaultCryptoMaterialsManager(provider), os, encryptionContext);
- }
-
- /**
- * Returns a {@link CryptoOutputStream} which encrypts the data prior to passing it onto the
- * underlying {@link OutputStream}.
- *
- * @see #encryptData(MasterKeyProvider, byte[], Map)
- * @see javax.crypto.CipherOutputStream
- */
- public CryptoOutputStream> createEncryptingStream(
- final CryptoMaterialsManager materialsManager,
- final OutputStream os,
- final Map encryptionContext
- ) {
- return new CryptoOutputStream<>(os, getEncryptingStreamHandler(materialsManager, encryptionContext));
- }
-
- /**
- * Returns the equivalent to calling
- * {@link #createEncryptingStream(MasterKeyProvider, OutputStream, Map)} with an empty
- * {@code encryptionContext}.
- */
- public > CryptoOutputStream createEncryptingStream(
- final MasterKeyProvider provider,
- final OutputStream os) {
- return createEncryptingStream(provider, os, EMPTY_MAP);
- }
-
- /**
- * Returns the equivalent to calling
- * {@link #createEncryptingStream(CryptoMaterialsManager, OutputStream, Map)} with an empty
- * {@code encryptionContext}.
- */
- public CryptoOutputStream> createEncryptingStream(
- final CryptoMaterialsManager materialsManager,
- final OutputStream os
- ) {
- return createEncryptingStream(materialsManager, os, EMPTY_MAP);
- }
-
- /**
- * Returns a {@link CryptoInputStream} which encrypts the data after reading it from the
- * underlying {@link InputStream}.
+ private static final Map EMPTY_MAP = Collections.emptyMap();
+
+ // These are volatile because we allow unsynchronized writes via our setters,
+ // and without setting volatile we could see strange results.
+ // E.g. copying these to a local might give different values on subsequent reads from the local.
+ // By setting them volatile we ensure that proper memory barriers are applied
+ // to ensure things behave in a sensible manner.
+ private volatile CryptoAlgorithm encryptionAlgorithm_ = null;
+ private volatile int encryptionFrameSize_ = getDefaultFrameSize();
+
+ private static final CommitmentPolicy DEFAULT_COMMITMENT_POLICY =
+ CommitmentPolicy.RequireEncryptRequireDecrypt;
+ private final CommitmentPolicy commitmentPolicy_;
+
+ /**
+ * The maximum number of encrypted data keys to unwrap (resp. wrap) on decrypt (resp. encrypt), if
+ * positive. If zero, do not limit EDKs.
+ */
+ private final int maxEncryptedDataKeys_;
+
+ private AwsCrypto(Builder builder) {
+ commitmentPolicy_ =
+ builder.commitmentPolicy_ == null ? DEFAULT_COMMITMENT_POLICY : builder.commitmentPolicy_;
+ if (builder.encryptionAlgorithm_ != null
+ && !commitmentPolicy_.algorithmAllowedForEncrypt(builder.encryptionAlgorithm_)) {
+ if (commitmentPolicy_ == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
+ throw new AwsCryptoException(
+ "Configuration conflict. Cannot encrypt due to CommitmentPolicy "
+ + commitmentPolicy_
+ + " requiring only non-committed messages. Algorithm ID was "
+ + builder.encryptionAlgorithm_
+ + ". 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 "
+ + builder.encryptionAlgorithm_
+ + ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
+ }
+ }
+ encryptionAlgorithm_ = builder.encryptionAlgorithm_;
+ encryptionFrameSize_ = builder.encryptionFrameSize_;
+ maxEncryptedDataKeys_ = builder.maxEncryptedDataKeys_;
+ }
+
+ public static class Builder {
+ private CryptoAlgorithm encryptionAlgorithm_;
+ private int encryptionFrameSize_ = getDefaultFrameSize();
+ private CommitmentPolicy commitmentPolicy_;
+ private int maxEncryptedDataKeys_ = CiphertextHeaders.NO_MAX_ENCRYPTED_DATA_KEYS;
+
+ private Builder() {}
+
+ private Builder(final AwsCrypto client) {
+ encryptionAlgorithm_ = client.encryptionAlgorithm_;
+ encryptionFrameSize_ = client.encryptionFrameSize_;
+ commitmentPolicy_ = client.commitmentPolicy_;
+ maxEncryptedDataKeys_ = client.maxEncryptedDataKeys_;
+ }
+
+ /**
+ * Sets the {@link CryptoAlgorithm} to encrypt with. The Aws Crypto client will use the last
+ * crypto algorithm set with either {@link
+ * AwsCrypto.Builder#withEncryptionAlgorithm(CryptoAlgorithm)} or {@link
+ * #setEncryptionAlgorithm(CryptoAlgorithm)} to encrypt with.
*
- * @see #encryptData(MasterKeyProvider, byte[], Map)
- * @see javax.crypto.CipherInputStream
+ * @param encryptionAlgorithm The {@link CryptoAlgorithm}
+ * @return The Builder, for method chaining
*/
- public > CryptoInputStream createEncryptingStream(
- final MasterKeyProvider provider,
- final InputStream is,
- final Map encryptionContext
- ) {
- //noinspection unchecked
- return (CryptoInputStream)
- createEncryptingStream(new DefaultCryptoMaterialsManager(provider), is, encryptionContext);
+ public Builder withEncryptionAlgorithm(CryptoAlgorithm encryptionAlgorithm) {
+ this.encryptionAlgorithm_ = encryptionAlgorithm;
+ return this;
}
/**
- * Returns a {@link CryptoInputStream} which encrypts the data after reading it from the
- * underlying {@link InputStream}.
+ * Sets the frame size of the encrypted messages that the Aws Crypto client produces. The Aws
+ * Crypto client will use the last frame size set with either {@link
+ * AwsCrypto.Builder#withEncryptionFrameSize(int)} or {@link #setEncryptionFrameSize(int)}.
*
- * @see #encryptData(MasterKeyProvider, byte[], Map)
- * @see javax.crypto.CipherInputStream
+ * @param frameSize The frame size to produce encrypted messages with.
+ * @return The Builder, for method chaining
*/
- public CryptoInputStream> createEncryptingStream(
- CryptoMaterialsManager materialsManager,
- final InputStream is,
- final Map encryptionContext
- ) {
- final MessageCryptoHandler cryptoHandler = getEncryptingStreamHandler(materialsManager, encryptionContext);
-
- return new CryptoInputStream<>(is, cryptoHandler);
+ public Builder withEncryptionFrameSize(int frameSize) {
+ this.encryptionFrameSize_ = frameSize;
+ return this;
}
/**
- * Returns the equivalent to calling
- * {@link #createEncryptingStream(MasterKeyProvider, InputStream, Map)} with an empty
- * {@code encryptionContext}.
- */
- public > CryptoInputStream createEncryptingStream(
- final MasterKeyProvider provider,
- final InputStream is
- ) {
- return createEncryptingStream(provider, is, EMPTY_MAP);
- }
-
- /**
- * Returns the equivalent to calling
- * {@link #createEncryptingStream(CryptoMaterialsManager, InputStream, Map)} with an empty
- * {@code encryptionContext}.
- */
- public CryptoInputStream> createEncryptingStream(
- final CryptoMaterialsManager materialsManager,
- final InputStream is
- ) {
- return createEncryptingStream(materialsManager, is, EMPTY_MAP);
- }
-
- /**
- * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
- * underlying {@link OutputStream}. This version only accepts unsigned messages.
- *
- * @see #decryptData(MasterKeyProvider, byte[])
- * @see javax.crypto.CipherOutputStream
- */
- public > CryptoOutputStream createUnsignedMessageDecryptingStream(
- final MasterKeyProvider provider, final OutputStream os) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptForbidDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoOutputStream(os, cryptoHandler);
- }
-
- /**
- * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
- * underlying {@link InputStream}. This version only accepts unsigned messages.
- *
- * @see #decryptData(MasterKeyProvider, byte[])
- * @see javax.crypto.CipherInputStream
- */
- public > CryptoInputStream createUnsignedMessageDecryptingStream(
- final MasterKeyProvider provider, final InputStream is) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptForbidDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoInputStream(is, cryptoHandler);
- }
-
- /**
- * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
- * underlying {@link OutputStream}. This version only accepts unsigned messages.
+ * Sets the {@link CommitmentPolicy} of this Aws Crypto client.
*
- * @see #decryptData(CryptoMaterialsManager, byte[])
- * @see javax.crypto.CipherOutputStream
+ * @param commitmentPolicy The commitment policy to enforce during encryption and decryption
+ * @return The Builder, for method chaining
*/
- public CryptoOutputStream> createUnsignedMessageDecryptingStream(
- final CryptoMaterialsManager materialsManager, final OutputStream os
- ) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptForbidDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoOutputStream(os, cryptoHandler);
+ public Builder withCommitmentPolicy(CommitmentPolicy commitmentPolicy) {
+ Utils.assertNonNull(commitmentPolicy, "commitmentPolicy");
+ this.commitmentPolicy_ = commitmentPolicy;
+ return this;
}
/**
- * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
- * underlying {@link InputStream}. This version only accepts unsigned messages.
+ * Sets the maximum number of encrypted data keys that this Aws Crypto client will wrap when
+ * encrypting, or unwrap when decrypting, a single message.
*
- * @see #encryptData(CryptoMaterialsManager, byte[], Map)
- * @see javax.crypto.CipherInputStream
- */
- public CryptoInputStream> createUnsignedMessageDecryptingStream(
- final CryptoMaterialsManager materialsManager, final InputStream is
- ) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptForbidDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoInputStream(is, cryptoHandler);
- }
-
- /**
- * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
- * underlying {@link OutputStream}.
- *
- * Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
- * after the decrypted plaintext has been released to the underlying {@link OutputStream}! This behavior can
- * be avoided by using the non-streaming #decryptData(MasterKeyProvider, byte[]) method instead, or
- * #createUnsignedMessageDecryptingStream(MasterKeyProvider, OutputStream) if you do not need to decrypt
- * signed messages.
- *
- * @see #decryptData(MasterKeyProvider, byte[])
- * @see #createUnsignedMessageDecryptingStream(MasterKeyProvider, OutputStream)
- * @see javax.crypto.CipherOutputStream
- */
- public > CryptoOutputStream createDecryptingStream(
- final MasterKeyProvider provider, final OutputStream os) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptAllowDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoOutputStream(os, cryptoHandler);
- }
-
- /**
- * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
- * underlying {@link InputStream}.
- *
- * Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
- * after the decrypted plaintext has been produced from the {@link InputStream}! This behavior can
- * be avoided by using the non-streaming #decryptData(MasterKeyProvider, byte[]) method instead, or
- * #createUnsignedMessageDecryptingStream(MasterKeyProvider, InputStream) if you do not need to decrypt
- * signed messages.
- *
- * @see #decryptData(MasterKeyProvider, byte[])
- * @see #createUnsignedMessageDecryptingStream(MasterKeyProvider, InputStream)
- * @see javax.crypto.CipherInputStream
- */
- public > CryptoInputStream createDecryptingStream(
- final MasterKeyProvider provider, final InputStream is) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(provider,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptAllowDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoInputStream(is, cryptoHandler);
- }
-
- /**
- * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
- * underlying {@link OutputStream}.
- *
- * Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
- * after the decrypted plaintext has been released to the underlying {@link OutputStream}! This behavior can
- * be avoided by using the non-streaming #decryptData(CryptoMaterialsManager, byte[]) method instead, or
- * #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, OutputStream) if you do not need to decrypt
- * signed messages.
- *
- * @see #decryptData(CryptoMaterialsManager, byte[])
- * @see #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, OutputStream)
- * @see javax.crypto.CipherOutputStream
- */
- public CryptoOutputStream> createDecryptingStream(
- final CryptoMaterialsManager materialsManager, final OutputStream os
- ) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptAllowDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoOutputStream(os, cryptoHandler);
- }
-
- /**
- * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
- * underlying {@link InputStream}.
- *
- * Note that if the encrypted message includes a trailing signature, by necessity it cannot be verified until
- * after the decrypted plaintext has been produced from the {@link InputStream}! This behavior can
- * be avoided by using the non-streaming #decryptData(CryptoMaterialsManager, byte[]) method instead, or
- * #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, InputStream) if you do not need to decrypt
- * signed messages.
- *
- * @see #decryptData(CryptoMaterialsManager, byte[])
- * @see #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, InputStream)
- * @see javax.crypto.CipherInputStream
- */
- public CryptoInputStream> createDecryptingStream(
- final CryptoMaterialsManager materialsManager, final InputStream is
- ) {
- final MessageCryptoHandler cryptoHandler = DecryptionHandler.create(materialsManager,
- commitmentPolicy_,
- SignaturePolicy.AllowEncryptAllowDecrypt,
- maxEncryptedDataKeys_);
- return new CryptoInputStream(is, cryptoHandler);
- }
-
- private MessageCryptoHandler getEncryptingStreamHandler(
- CryptoMaterialsManager materialsManager, Map encryptionContext
- ) {
- Utils.assertNonNull(materialsManager, "materialsManager");
- Utils.assertNonNull(encryptionContext, "encryptionContext");
-
- EncryptionMaterialsRequest.Builder requestBuilder = EncryptionMaterialsRequest.newBuilder()
- .setContext(encryptionContext)
- .setRequestedAlgorithm(getEncryptionAlgorithm())
- .setCommitmentPolicy(commitmentPolicy_);
-
- return new LazyMessageCryptoHandler(info -> {
- // Hopefully we know the input size now, so we can pass it along to the CMM.
- if (info.getMaxInputSize() != -1) {
- requestBuilder.setPlaintextSize(info.getMaxInputSize());
- }
-
- return new EncryptionHandler(
- getEncryptionFrameSize(),
- checkMaxEncryptedDataKeys(checkAlgorithm(materialsManager.getMaterialsForEncrypt(requestBuilder.build()))),
- commitmentPolicy_
- );
+ * @param maxEncryptedDataKeys The maximum number of encrypted data keys; must be positive
+ * @return The Builder, for method chaining
+ */
+ public Builder withMaxEncryptedDataKeys(int maxEncryptedDataKeys) {
+ if (maxEncryptedDataKeys < 1) {
+ throw new IllegalArgumentException("maxEncryptedDataKeys must be positive");
+ }
+ this.maxEncryptedDataKeys_ = maxEncryptedDataKeys;
+ return this;
+ }
+
+ public AwsCrypto build() {
+ return new AwsCrypto(this);
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ public static AwsCrypto standard() {
+ return AwsCrypto.builder().build();
+ }
+
+ /**
+ * Returns the frame size to use for encryption when none is explicitly selected. Currently it is
+ * 4096.
+ */
+ public static int getDefaultFrameSize() {
+ return 4096;
+ }
+
+ /**
+ * Sets the {@link CryptoAlgorithm} to use when encrypting data. This has no impact on
+ * decryption.
+ */
+ public void setEncryptionAlgorithm(final CryptoAlgorithm alg) {
+ if (!commitmentPolicy_.algorithmAllowedForEncrypt(alg)) {
+ if (commitmentPolicy_ == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
+ throw new AwsCryptoException(
+ "Configuration conflict. Cannot encrypt due to CommitmentPolicy "
+ + commitmentPolicy_
+ + " requiring only non-committed messages. Algorithm ID was "
+ + alg
+ + ". 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 "
+ + alg
+ + ". See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/troubleshooting-migration.html");
+ }
+ }
+ encryptionAlgorithm_ = alg;
+ }
+
+ public CryptoAlgorithm getEncryptionAlgorithm() {
+ return encryptionAlgorithm_;
+ }
+
+ /**
+ * Sets the framing size to use when encrypting data. This has no impact on decryption.
+ * If {@code frameSize} is 0, then framing is disabled and the entire plaintext will be encrypted
+ * in a single block.
+ *
+ * Note that during encryption arrays of this size will be allocated. Using extremely large
+ * frame sizes may pose compatibility issues when the decryptor is running on 32-bit systems.
+ * Additionally, Java VM limits may set a platform-specific upper bound to frame sizes.
+ */
+ public void setEncryptionFrameSize(final int frameSize) {
+ if (frameSize < 0) {
+ throw new IllegalArgumentException("frameSize must be non-negative");
+ }
+
+ encryptionFrameSize_ = frameSize;
+ }
+
+ public int getEncryptionFrameSize() {
+ return encryptionFrameSize_;
+ }
+
+ /**
+ * Returns the best estimate for the output length of encrypting a plaintext with the provided
+ * {@code plaintextSize} and {@code encryptionContext}. The actual ciphertext may be shorter.
+ *
+ *
This method is equivalent to calling {@link #estimateCiphertextSize(CryptoMaterialsManager,
+ * int, Map)} with a {@link DefaultCryptoMaterialsManager} based on the given provider.
+ */
+ public > long estimateCiphertextSize(
+ final MasterKeyProvider provider,
+ final int plaintextSize,
+ final Map encryptionContext) {
+ return estimateCiphertextSize(
+ new DefaultCryptoMaterialsManager(provider), plaintextSize, encryptionContext);
+ }
+
+ /**
+ * Returns the best estimate for the output length of encrypting a plaintext with the provided
+ * {@code plaintextSize} and {@code encryptionContext}. The actual ciphertext may be shorter.
+ */
+ public long estimateCiphertextSize(
+ CryptoMaterialsManager materialsManager,
+ final int plaintextSize,
+ final Map encryptionContext) {
+ EncryptionMaterialsRequest request =
+ EncryptionMaterialsRequest.newBuilder()
+ .setContext(encryptionContext)
+ .setRequestedAlgorithm(getEncryptionAlgorithm())
+ // We're not actually encrypting any data, so don't consume any bytes from the cache's
+ // limits. We do need to
+ // pass /something/ though, or the cache will be bypassed (as it'll assume this is a
+ // streaming encrypt of
+ // unknown size).
+ .setPlaintextSize(0)
+ .setCommitmentPolicy(commitmentPolicy_)
+ .build();
+
+ final MessageCryptoHandler cryptoHandler =
+ new EncryptionHandler(
+ getEncryptionFrameSize(),
+ checkAlgorithm(materialsManager.getMaterialsForEncrypt(request)),
+ commitmentPolicy_);
+
+ return cryptoHandler.estimateOutputSize(plaintextSize);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #estimateCiphertextSize(MasterKeyProvider, int, Map)}
+ * with an empty {@code encryptionContext}.
+ */
+ public > long estimateCiphertextSize(
+ final MasterKeyProvider provider, final int plaintextSize) {
+ return estimateCiphertextSize(provider, plaintextSize, EMPTY_MAP);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #estimateCiphertextSize(CryptoMaterialsManager, int,
+ * Map)} with an empty {@code encryptionContext}.
+ */
+ public long estimateCiphertextSize(
+ final CryptoMaterialsManager materialsManager, final int plaintextSize) {
+ return estimateCiphertextSize(materialsManager, plaintextSize, EMPTY_MAP);
+ }
+
+ /**
+ * Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
+ * DataKeys} that are in turn protected by {@link MasterKey MasterKeys} provided by {@code
+ * provider}.
+ *
+ * This method is equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[],
+ * Map)} using a {@link DefaultCryptoMaterialsManager} based on the given provider.
+ */
+ public > CryptoResult encryptData(
+ final MasterKeyProvider provider,
+ final byte[] plaintext,
+ final Map encryptionContext) {
+ //noinspection unchecked
+ return (CryptoResult)
+ encryptData(new DefaultCryptoMaterialsManager(provider), plaintext, encryptionContext);
+ }
+
+ /**
+ * Returns an encrypted form of {@code plaintext} that has been protected with {@link DataKey
+ * DataKeys} that are in turn protected by the given CryptoMaterialsProvider.
+ */
+ public CryptoResult encryptData(
+ CryptoMaterialsManager materialsManager,
+ final byte[] plaintext,
+ final Map encryptionContext) {
+ EncryptionMaterialsRequest request =
+ EncryptionMaterialsRequest.newBuilder()
+ .setContext(encryptionContext)
+ .setRequestedAlgorithm(getEncryptionAlgorithm())
+ .setPlaintext(plaintext)
+ .setCommitmentPolicy(commitmentPolicy_)
+ .build();
+
+ EncryptionMaterials encryptionMaterials =
+ checkMaxEncryptedDataKeys(checkAlgorithm(materialsManager.getMaterialsForEncrypt(request)));
+ final MessageCryptoHandler cryptoHandler =
+ new EncryptionHandler(getEncryptionFrameSize(), encryptionMaterials, commitmentPolicy_);
+
+ final int outSizeEstimate = cryptoHandler.estimateOutputSize(plaintext.length);
+ final byte[] out = new byte[outSizeEstimate];
+ int outLen =
+ cryptoHandler.processBytes(plaintext, 0, plaintext.length, out, 0).getBytesWritten();
+ outLen += cryptoHandler.doFinal(out, outLen);
+
+ final byte[] outBytes = Utils.truncate(out, outLen);
+
+ //noinspection unchecked
+ return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #encryptData(MasterKeyProvider, byte[], Map)} with an
+ * empty {@code encryptionContext}.
+ */
+ public > CryptoResult encryptData(
+ final MasterKeyProvider provider, final byte[] plaintext) {
+ return encryptData(provider, plaintext, EMPTY_MAP);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #encryptData(CryptoMaterialsManager, byte[], Map)}
+ * with an empty {@code encryptionContext}.
+ */
+ public CryptoResult encryptData(
+ final CryptoMaterialsManager materialsManager, final byte[] plaintext) {
+ return encryptData(materialsManager, plaintext, EMPTY_MAP);
+ }
+
+ /**
+ * Calls {@link #encryptData(MasterKeyProvider, byte[], Map)} on the UTF-8 encoded bytes of {@code
+ * plaintext} and base64 encodes the result.
+ *
+ * @deprecated Use the {@link #encryptData(MasterKeyProvider, byte[], Map)} and {@link
+ * #decryptData(MasterKeyProvider, byte[])} APIs instead. {@code encryptString} and {@code
+ * decryptString} work as expected if you use them together. However, to work with other
+ * language implementations of the AWS Encryption SDK, you need to base64-decode the output of
+ * {@code encryptString} and base64-encode the input to {@code decryptString}. These
+ * deprecated APIs will be removed in the future.
+ */
+ @Deprecated
+ public > CryptoResult encryptString(
+ final MasterKeyProvider provider,
+ final String plaintext,
+ final Map encryptionContext) {
+ //noinspection unchecked
+ return (CryptoResult)
+ encryptString(new DefaultCryptoMaterialsManager(provider), plaintext, encryptionContext);
+ }
+
+ /**
+ * Calls {@link #encryptData(CryptoMaterialsManager, byte[], Map)} on the UTF-8 encoded bytes of
+ * {@code plaintext} and base64 encodes the result.
+ *
+ * @deprecated Use the {@link #encryptData(CryptoMaterialsManager, byte[], Map)} and {@link
+ * #decryptData(CryptoMaterialsManager, byte[])} APIs instead. {@code encryptString} and
+ * {@code decryptString} work as expected if you use them together. However, to work with
+ * other language implementations of the AWS Encryption SDK, you need to base64-decode the
+ * output of {@code encryptString} and base64-encode the input to {@code decryptString}. These
+ * deprecated APIs will be removed in the future.
+ */
+ @Deprecated
+ public CryptoResult encryptString(
+ CryptoMaterialsManager materialsManager,
+ final String plaintext,
+ final Map encryptionContext) {
+ final CryptoResult ctBytes =
+ encryptData(
+ materialsManager, plaintext.getBytes(StandardCharsets.UTF_8), encryptionContext);
+ return new CryptoResult<>(
+ Utils.encodeBase64String(ctBytes.getResult()),
+ ctBytes.getMasterKeys(),
+ ctBytes.getHeaders());
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #encryptString(MasterKeyProvider, String, Map)} with
+ * an empty {@code encryptionContext}.
+ *
+ * @deprecated Use the {@link #encryptData(MasterKeyProvider, byte[])} and {@link
+ * #decryptData(MasterKeyProvider, byte[])} APIs instead. {@code encryptString} and {@code
+ * decryptString} work as expected if you use them together. However, to work with other
+ * language implementations of the AWS Encryption SDK, you need to base64-decode the output of
+ * {@code encryptString} and base64-encode the input to {@code decryptString}. These
+ * deprecated APIs will be removed in the future.
+ */
+ @Deprecated
+ public > CryptoResult encryptString(
+ final MasterKeyProvider provider, final String plaintext) {
+ return encryptString(provider, plaintext, EMPTY_MAP);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #encryptString(CryptoMaterialsManager, String, Map)}
+ * with an empty {@code encryptionContext}.
+ *
+ * @deprecated Use the {@link #encryptData(CryptoMaterialsManager, byte[])} and {@link
+ * #decryptData(CryptoMaterialsManager, byte[])} APIs instead. {@code encryptString} and
+ * {@code decryptString} work as expected if you use them together. However, to work with
+ * other language implementations of the AWS Encryption SDK, you need to base64-decode the
+ * output of {@code encryptString} and base64-encode the input to {@code decryptString}. These
+ * deprecated APIs will be removed in the future.
+ */
+ @Deprecated
+ public CryptoResult encryptString(
+ final CryptoMaterialsManager materialsManager, final String plaintext) {
+ return encryptString(materialsManager, plaintext, EMPTY_MAP);
+ }
+
+ /**
+ * Decrypts the provided {@code ciphertext} by requesting that the {@code provider} unwrap any
+ * usable {@link DataKey} in the ciphertext and then decrypts the ciphertext using that {@code
+ * DataKey}.
+ */
+ public > CryptoResult decryptData(
+ final MasterKeyProvider provider, final byte[] ciphertext) {
+ return decryptData(
+ Utils.assertNonNull(provider, "provider"),
+ new ParsedCiphertext(ciphertext, maxEncryptedDataKeys_));
+ }
+
+ /**
+ * Decrypts the provided ciphertext by delegating to the provided materialsManager to obtain the
+ * decrypted {@link DataKey}.
+ *
+ * @param materialsManager the {@link CryptoMaterialsManager} to use for decryption operations.
+ * @param ciphertext the ciphertext to attempt to decrypt.
+ * @return the {@link CryptoResult} with the decrypted data.
+ */
+ public CryptoResult decryptData(
+ final CryptoMaterialsManager materialsManager, final byte[] ciphertext) {
+ return decryptData(
+ Utils.assertNonNull(materialsManager, "materialsManager"),
+ new ParsedCiphertext(ciphertext, maxEncryptedDataKeys_));
+ }
+
+ /** @see #decryptData(MasterKeyProvider, byte[]) */
+ @SuppressWarnings("unchecked")
+ public > CryptoResult decryptData(
+ final MasterKeyProvider provider, final ParsedCiphertext ciphertext) {
+ Utils.assertNonNull(provider, "provider");
+ return (CryptoResult)
+ decryptData(new DefaultCryptoMaterialsManager(provider), ciphertext);
+ }
+
+ /** @see #decryptData(CryptoMaterialsManager, byte[]) */
+ public CryptoResult decryptData(
+ final CryptoMaterialsManager materialsManager, final ParsedCiphertext ciphertext) {
+ Utils.assertNonNull(materialsManager, "materialsManager");
+
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ materialsManager,
+ ciphertext,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptAllowDecrypt,
+ maxEncryptedDataKeys_);
+
+ final byte[] ciphertextBytes = ciphertext.getCiphertext();
+ final int contentLen = ciphertextBytes.length - ciphertext.getOffset();
+ final int outSizeEstimate = cryptoHandler.estimateOutputSize(contentLen);
+ final byte[] out = new byte[outSizeEstimate];
+ final ProcessingSummary processed =
+ cryptoHandler.processBytes(ciphertextBytes, ciphertext.getOffset(), contentLen, out, 0);
+ if (processed.getBytesProcessed() != contentLen) {
+ throw new BadCiphertextException(
+ "Unable to process entire ciphertext. May have trailing data.");
+ }
+ int outLen = processed.getBytesWritten();
+ outLen += cryptoHandler.doFinal(out, outLen);
+
+ final byte[] outBytes = Utils.truncate(out, outLen);
+
+ //noinspection unchecked
+ return new CryptoResult(outBytes, cryptoHandler.getMasterKeys(), cryptoHandler.getHeaders());
+ }
+
+ /**
+ * Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
+ * UTF-8 encoded string.
+ *
+ * @see #decryptData(MasterKeyProvider, byte[])
+ * @deprecated Use the {@link #decryptData(MasterKeyProvider, byte[])} and {@link
+ * #encryptData(MasterKeyProvider, byte[], Map)} APIs instead. {@code encryptString} and
+ * {@code decryptString} work as expected if you use them together. However, to work with
+ * other language implementations of the AWS Encryption SDK, you need to base64-decode the
+ * output of {@code encryptString} and base64-encode the input to {@code decryptString}. These
+ * deprecated APIs will be removed in the future.
+ */
+ @Deprecated
+ @SuppressWarnings("unchecked")
+ public > CryptoResult decryptString(
+ final MasterKeyProvider provider, final String ciphertext) {
+ return (CryptoResult)
+ decryptString(new DefaultCryptoMaterialsManager(provider), ciphertext);
+ }
+
+ /**
+ * Base64 decodes the {@code ciphertext} prior to decryption and then treats the results as a
+ * UTF-8 encoded string.
+ *
+ * @see #decryptData(CryptoMaterialsManager, byte[])
+ * @deprecated Use the {@link #decryptData(CryptoMaterialsManager, byte[])} and {@link
+ * #encryptData(CryptoMaterialsManager, byte[], Map)} APIs instead. {@code encryptString} and
+ * {@code decryptString} work as expected if you use them together. However, to work with
+ * other language implementations of the AWS Encryption SDK, you need to base64-decode the
+ * output of {@code encryptString} and base64-encode the input to {@code decryptString}. These
+ * deprecated APIs will be removed in the future.
+ */
+ @Deprecated
+ public CryptoResult decryptString(
+ final CryptoMaterialsManager provider, final String ciphertext) {
+ Utils.assertNonNull(provider, "provider");
+ final byte[] ciphertextBytes;
+ try {
+ ciphertextBytes = Utils.decodeBase64String(Utils.assertNonNull(ciphertext, "ciphertext"));
+ } catch (final IllegalArgumentException ex) {
+ throw new BadCiphertextException("Invalid base 64", ex);
+ }
+ final CryptoResult ptBytes = decryptData(provider, ciphertextBytes);
+ //noinspection unchecked
+ return new CryptoResult(
+ new String(ptBytes.getResult(), StandardCharsets.UTF_8),
+ ptBytes.getMasterKeys(),
+ ptBytes.getHeaders());
+ }
+
+ /**
+ * Returns a {@link CryptoOutputStream} which encrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * @see #encryptData(MasterKeyProvider, byte[], Map)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public > CryptoOutputStream createEncryptingStream(
+ final MasterKeyProvider provider,
+ final OutputStream os,
+ final Map encryptionContext) {
+ //noinspection unchecked
+ return (CryptoOutputStream)
+ createEncryptingStream(new DefaultCryptoMaterialsManager(provider), os, encryptionContext);
+ }
+
+ /**
+ * Returns a {@link CryptoOutputStream} which encrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * @see #encryptData(MasterKeyProvider, byte[], Map)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public CryptoOutputStream> createEncryptingStream(
+ final CryptoMaterialsManager materialsManager,
+ final OutputStream os,
+ final Map encryptionContext) {
+ return new CryptoOutputStream<>(
+ os, getEncryptingStreamHandler(materialsManager, encryptionContext));
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #createEncryptingStream(MasterKeyProvider,
+ * OutputStream, Map)} with an empty {@code encryptionContext}.
+ */
+ public > CryptoOutputStream createEncryptingStream(
+ final MasterKeyProvider provider, final OutputStream os) {
+ return createEncryptingStream(provider, os, EMPTY_MAP);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #createEncryptingStream(CryptoMaterialsManager,
+ * OutputStream, Map)} with an empty {@code encryptionContext}.
+ */
+ public CryptoOutputStream> createEncryptingStream(
+ final CryptoMaterialsManager materialsManager, final OutputStream os) {
+ return createEncryptingStream(materialsManager, os, EMPTY_MAP);
+ }
+
+ /**
+ * Returns a {@link CryptoInputStream} which encrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * @see #encryptData(MasterKeyProvider, byte[], Map)
+ * @see javax.crypto.CipherInputStream
+ */
+ public > CryptoInputStream createEncryptingStream(
+ final MasterKeyProvider provider,
+ final InputStream is,
+ final Map encryptionContext) {
+ //noinspection unchecked
+ return (CryptoInputStream)
+ createEncryptingStream(new DefaultCryptoMaterialsManager(provider), is, encryptionContext);
+ }
+
+ /**
+ * Returns a {@link CryptoInputStream} which encrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * @see #encryptData(MasterKeyProvider, byte[], Map)
+ * @see javax.crypto.CipherInputStream
+ */
+ public CryptoInputStream> createEncryptingStream(
+ CryptoMaterialsManager materialsManager,
+ final InputStream is,
+ final Map encryptionContext) {
+ final MessageCryptoHandler cryptoHandler =
+ getEncryptingStreamHandler(materialsManager, encryptionContext);
+
+ return new CryptoInputStream<>(is, cryptoHandler);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #createEncryptingStream(MasterKeyProvider,
+ * InputStream, Map)} with an empty {@code encryptionContext}.
+ */
+ public > CryptoInputStream createEncryptingStream(
+ final MasterKeyProvider provider, final InputStream is) {
+ return createEncryptingStream(provider, is, EMPTY_MAP);
+ }
+
+ /**
+ * Returns the equivalent to calling {@link #createEncryptingStream(CryptoMaterialsManager,
+ * InputStream, Map)} with an empty {@code encryptionContext}.
+ */
+ public CryptoInputStream> createEncryptingStream(
+ final CryptoMaterialsManager materialsManager, final InputStream is) {
+ return createEncryptingStream(materialsManager, is, EMPTY_MAP);
+ }
+
+ /**
+ * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}. This version only accepts unsigned messages.
+ *
+ * @see #decryptData(MasterKeyProvider, byte[])
+ * @see javax.crypto.CipherOutputStream
+ */
+ public > CryptoOutputStream createUnsignedMessageDecryptingStream(
+ final MasterKeyProvider provider, final OutputStream os) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ provider,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptForbidDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoOutputStream(os, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
+ * underlying {@link InputStream}. This version only accepts unsigned messages.
+ *
+ * @see #decryptData(MasterKeyProvider, byte[])
+ * @see javax.crypto.CipherInputStream
+ */
+ public > CryptoInputStream createUnsignedMessageDecryptingStream(
+ final MasterKeyProvider provider, final InputStream is) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ provider,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptForbidDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoInputStream(is, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}. This version only accepts unsigned messages.
+ *
+ * @see #decryptData(CryptoMaterialsManager, byte[])
+ * @see javax.crypto.CipherOutputStream
+ */
+ public CryptoOutputStream> createUnsignedMessageDecryptingStream(
+ final CryptoMaterialsManager materialsManager, final OutputStream os) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ materialsManager,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptForbidDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoOutputStream(os, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
+ * underlying {@link InputStream}. This version only accepts unsigned messages.
+ *
+ * @see #encryptData(CryptoMaterialsManager, byte[], Map)
+ * @see javax.crypto.CipherInputStream
+ */
+ public CryptoInputStream> createUnsignedMessageDecryptingStream(
+ final CryptoMaterialsManager materialsManager, final InputStream is) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ materialsManager,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptForbidDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoInputStream(is, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * Note that if the encrypted message includes a trailing signature, by necessity it cannot be
+ * verified until after the decrypted plaintext has been released to the underlying {@link
+ * OutputStream}! This behavior can be avoided by using the non-streaming
+ * #decryptData(MasterKeyProvider, byte[]) method instead, or
+ * #createUnsignedMessageDecryptingStream(MasterKeyProvider, OutputStream) if you do not need to
+ * decrypt signed messages.
+ *
+ * @see #decryptData(MasterKeyProvider, byte[])
+ * @see #createUnsignedMessageDecryptingStream(MasterKeyProvider, OutputStream)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public > CryptoOutputStream createDecryptingStream(
+ final MasterKeyProvider provider, final OutputStream os) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ provider,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptAllowDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoOutputStream(os, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ * Note that if the encrypted message includes a trailing signature, by necessity it cannot be
+ * verified until after the decrypted plaintext has been produced from the {@link InputStream}!
+ * This behavior can be avoided by using the non-streaming #decryptData(MasterKeyProvider, byte[])
+ * method instead, or #createUnsignedMessageDecryptingStream(MasterKeyProvider, InputStream) if
+ * you do not need to decrypt signed messages.
+ *
+ * @see #decryptData(MasterKeyProvider, byte[])
+ * @see #createUnsignedMessageDecryptingStream(MasterKeyProvider, InputStream)
+ * @see javax.crypto.CipherInputStream
+ */
+ public > CryptoInputStream createDecryptingStream(
+ final MasterKeyProvider provider, final InputStream is) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ provider,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptAllowDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoInputStream(is, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoOutputStream} which decrypts the data prior to passing it onto the
+ * underlying {@link OutputStream}.
+ *
+ * Note that if the encrypted message includes a trailing signature, by necessity it cannot be
+ * verified until after the decrypted plaintext has been released to the underlying {@link
+ * OutputStream}! This behavior can be avoided by using the non-streaming
+ * #decryptData(CryptoMaterialsManager, byte[]) method instead, or
+ * #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, OutputStream) if you do not need
+ * to decrypt signed messages.
+ *
+ * @see #decryptData(CryptoMaterialsManager, byte[])
+ * @see #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, OutputStream)
+ * @see javax.crypto.CipherOutputStream
+ */
+ public CryptoOutputStream> createDecryptingStream(
+ final CryptoMaterialsManager materialsManager, final OutputStream os) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ materialsManager,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptAllowDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoOutputStream(os, cryptoHandler);
+ }
+
+ /**
+ * Returns a {@link CryptoInputStream} which decrypts the data after reading it from the
+ * underlying {@link InputStream}.
+ *
+ *
Note that if the encrypted message includes a trailing signature, by necessity it cannot be
+ * verified until after the decrypted plaintext has been produced from the {@link InputStream}!
+ * This behavior can be avoided by using the non-streaming #decryptData(CryptoMaterialsManager,
+ * byte[]) method instead, or #createUnsignedMessageDecryptingStream(CryptoMaterialsManager,
+ * InputStream) if you do not need to decrypt signed messages.
+ *
+ * @see #decryptData(CryptoMaterialsManager, byte[])
+ * @see #createUnsignedMessageDecryptingStream(CryptoMaterialsManager, InputStream)
+ * @see javax.crypto.CipherInputStream
+ */
+ public CryptoInputStream> createDecryptingStream(
+ final CryptoMaterialsManager materialsManager, final InputStream is) {
+ final MessageCryptoHandler cryptoHandler =
+ DecryptionHandler.create(
+ materialsManager,
+ commitmentPolicy_,
+ SignaturePolicy.AllowEncryptAllowDecrypt,
+ maxEncryptedDataKeys_);
+ return new CryptoInputStream(is, cryptoHandler);
+ }
+
+ private MessageCryptoHandler getEncryptingStreamHandler(
+ CryptoMaterialsManager materialsManager, Map encryptionContext) {
+ Utils.assertNonNull(materialsManager, "materialsManager");
+ Utils.assertNonNull(encryptionContext, "encryptionContext");
+
+ EncryptionMaterialsRequest.Builder requestBuilder =
+ EncryptionMaterialsRequest.newBuilder()
+ .setContext(encryptionContext)
+ .setRequestedAlgorithm(getEncryptionAlgorithm())
+ .setCommitmentPolicy(commitmentPolicy_);
+
+ return new LazyMessageCryptoHandler(
+ info -> {
+ // Hopefully we know the input size now, so we can pass it along to the CMM.
+ if (info.getMaxInputSize() != -1) {
+ requestBuilder.setPlaintextSize(info.getMaxInputSize());
+ }
+
+ return new EncryptionHandler(
+ getEncryptionFrameSize(),
+ checkMaxEncryptedDataKeys(
+ checkAlgorithm(materialsManager.getMaterialsForEncrypt(requestBuilder.build()))),
+ commitmentPolicy_);
});
- }
+ }
- private EncryptionMaterials checkAlgorithm(EncryptionMaterials result) {
- if (encryptionAlgorithm_ != null && result.getAlgorithm() != encryptionAlgorithm_) {
- throw new AwsCryptoException(
- String.format("Materials manager ignored requested algorithm; algorithm %s was set on AwsCrypto " +
- "but %s was selected", encryptionAlgorithm_, result.getAlgorithm())
- );
- }
-
- return result;
+ private EncryptionMaterials checkAlgorithm(EncryptionMaterials result) {
+ if (encryptionAlgorithm_ != null && result.getAlgorithm() != encryptionAlgorithm_) {
+ throw new AwsCryptoException(
+ String.format(
+ "Materials manager ignored requested algorithm; algorithm %s was set on AwsCrypto "
+ + "but %s was selected",
+ encryptionAlgorithm_, result.getAlgorithm()));
}
- private EncryptionMaterials checkMaxEncryptedDataKeys(EncryptionMaterials materials) {
- if (maxEncryptedDataKeys_ > 0 && materials.getEncryptedDataKeys().size() > maxEncryptedDataKeys_) {
- throw new AwsCryptoException("Encrypted data keys exceed maxEncryptedDataKeys");
- }
- return materials;
+ return result;
+ }
+
+ private EncryptionMaterials checkMaxEncryptedDataKeys(EncryptionMaterials materials) {
+ if (maxEncryptedDataKeys_ > 0
+ && materials.getEncryptedDataKeys().size() > maxEncryptedDataKeys_) {
+ throw new AwsCryptoException("Encrypted data keys exceed maxEncryptedDataKeys");
}
+ return materials;
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CommitmentPolicy.java b/src/main/java/com/amazonaws/encryptionsdk/CommitmentPolicy.java
index 295512e2c..d17c078b4 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CommitmentPolicy.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CommitmentPolicy.java
@@ -3,34 +3,34 @@
package com.amazonaws.encryptionsdk;
-import com.amazonaws.encryptionsdk.CryptoAlgorithm;
-
public enum CommitmentPolicy {
- ForbidEncryptAllowDecrypt,
- RequireEncryptAllowDecrypt,
- RequireEncryptRequireDecrypt;
+ ForbidEncryptAllowDecrypt,
+ RequireEncryptAllowDecrypt,
+ RequireEncryptRequireDecrypt;
- public boolean algorithmAllowedForEncrypt(CryptoAlgorithm algorithm) {
- switch (this) {
- case ForbidEncryptAllowDecrypt:
- return !algorithm.isCommitting();
- case RequireEncryptAllowDecrypt:
- case RequireEncryptRequireDecrypt:
- return algorithm.isCommitting();
- default:
- throw new UnsupportedOperationException("Support for commitment policy " + this + " not yet built.");
- }
+ public boolean algorithmAllowedForEncrypt(CryptoAlgorithm algorithm) {
+ switch (this) {
+ case ForbidEncryptAllowDecrypt:
+ return !algorithm.isCommitting();
+ case RequireEncryptAllowDecrypt:
+ case RequireEncryptRequireDecrypt:
+ return algorithm.isCommitting();
+ default:
+ throw new UnsupportedOperationException(
+ "Support for commitment policy " + this + " not yet built.");
}
+ }
- public boolean algorithmAllowedForDecrypt(CryptoAlgorithm algorithm) {
- switch (this) {
- case ForbidEncryptAllowDecrypt:
- case RequireEncryptAllowDecrypt:
- return true;
- case RequireEncryptRequireDecrypt:
- return algorithm.isCommitting();
- default:
- throw new UnsupportedOperationException("Support for commitment policy " + this + " not yet built.");
- }
+ public boolean algorithmAllowedForDecrypt(CryptoAlgorithm algorithm) {
+ switch (this) {
+ case ForbidEncryptAllowDecrypt:
+ case RequireEncryptAllowDecrypt:
+ return true;
+ case RequireEncryptRequireDecrypt:
+ return algorithm.isCommitting();
+ default:
+ throw new UnsupportedOperationException(
+ "Support for commitment policy " + this + " not yet built.");
}
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java
index 2ee8bec6c..821718652 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java
@@ -3,6 +3,11 @@
package com.amazonaws.encryptionsdk;
+import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
+import com.amazonaws.encryptionsdk.internal.CommittedKey;
+import com.amazonaws.encryptionsdk.internal.Constants;
+import com.amazonaws.encryptionsdk.internal.HmacKeyDerivationFunction;
+import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.InvalidKeyException;
@@ -11,437 +16,515 @@
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
-
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
-import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
-import com.amazonaws.encryptionsdk.internal.HmacKeyDerivationFunction;
-
-import com.amazonaws.encryptionsdk.internal.Constants;
-import com.amazonaws.encryptionsdk.internal.CommittedKey;
-import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
-
/**
* Describes the cryptographic algorithms available for use in this library.
*
- *
- * Format: CryptoAlgorithm(block size, nonce length, tag length, max content length, key algo, key
- * length, short value representing this algorithm, trailing signature alg, trailing signature
+ *
Format: CryptoAlgorithm(block size, nonce length, tag length, max content length, key algo,
+ * key length, short value representing this algorithm, trailing signature alg, trailing signature
* length)
*/
public enum CryptoAlgorithm {
- /**
- * AES-GCM 128
- */
- ALG_AES_128_GCM_IV12_TAG16_NO_KDF(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 16, 0x0014, "AES", 16, false),
- /**
- * AES-GCM 192
- */
- ALG_AES_192_GCM_IV12_TAG16_NO_KDF(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 24, 0x0046, "AES", 24, false),
- /**
- * AES-GCM 256
- */
- ALG_AES_256_GCM_IV12_TAG16_NO_KDF(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0078, "AES", 32, false),
- /**
- * AES-GCM 128 with HKDF-SHA256
- */
- ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 16, 0x0114, "HkdfSHA256",
- 16, true),
- /**
- * AES-GCM 192
- */
- ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA256(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 24, 0x0146, "HkdfSHA256",
- 24, true),
- /**
- * AES-GCM 256
- */
- ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0178, "HkdfSHA256",
- 32, true),
-
- /**
- * AES-GCM 128 with ECDSA (SHA256 with the secp256r1 curve)
- */
- ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 16, 0x0214,
- "HkdfSHA256", 16,
- true, "SHA256withECDSA", 71),
- /**
- * AES-GCM 192 with ECDSA (SHA384 with the secp384r1 curve)
- */
- ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 24, 0x0346,
- "HkdfSHA384", 24,
- true, "SHA384withECDSA", 103),
- /**
- * AES-GCM 256 with ECDSA (SHA384 with the secp384r1 curve)
- */
- ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384(1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0378,
- "HkdfSHA384", 32,
- true, "SHA384withECDSA", 103),
- /**
- * AES-GCM 256 with key commitment
- * Note: 1.7.0 of this library only supports decryption of using this crypto algorithm and does not support encryption with this algorithm
- */
- ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY(2, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0478,
- "HkdfSHA512", 32, true, null, 0, "HkdfSHA512", 32, 32, 32),
- /**
- * AES-GCM 256 with ECDSA (SHA384 with the secp384r1 curve) and key commitment
- * Note: 1.7.0 of this library only supports decryption of using this crypto algorithm and does not support encryption with this algorithm
- */
- ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384(2, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0578,
- "HkdfSHA512", 32, true, "SHA384withECDSA", 103, "HkdfSHA512", 32, 32, 32);
-
- private final byte messageFormatVersion_;
- private final int blockSizeBits_;
- private final byte nonceLenBytes_;
- private final int tagLenBytes_;
- private final long maxContentLen_;
- private final String keyAlgo_;
- private final int keyLenBytes_;
- private final short value_;
- private final String trailingSigAlgo_;
- private final short trailingSigLen_;
- private final String dataKeyAlgo_;
- private final int dataKeyLen_;
- private final boolean safeToCache_;
- private final String keyCommitmentAlgo_;
- private final int commitmentLength_;
- private final int commitmentNonceLength_;
- private final int suiteDataLength_;
-
- private static final byte VERSION_1 = (byte) 1;
- private static final byte VERSION_2 = (byte) 2;
- private static final int VERSION_1_MESSAGE_ID_LEN = 16;
- private static final int VERSION_2_MESSAGE_ID_LEN = 32;
-
- /*
- * Create a mapping between the CiphertextType object and its byte value representation. Make
- * this a static method so the map is created when the object is created. This enables fast
- * lookups of the CryptoAlgorithm given its short value representation and message format version.
- */
- private static final Map ID_MAPPING = new HashMap<>();
-
- static {
- for (final CryptoAlgorithm s : EnumSet.allOf(CryptoAlgorithm.class)) {
- ID_MAPPING.put(fieldsToLookupKey(s.messageFormatVersion_, s.value_), s);
- }
- }
-
- CryptoAlgorithm(
- final int messageFormatVersion,
- final int blockSizeBits, final int nonceLenBytes, final int tagLenBytes,
- final long maxContentLen, final String keyAlgo, final int keyLenBytes, final int value,
- final String dataKeyAlgo, final int dataKeyLen, boolean safeToCache
- ) {
- this(messageFormatVersion, blockSizeBits, nonceLenBytes, tagLenBytes,
- maxContentLen, keyAlgo, keyLenBytes, value,
- dataKeyAlgo, dataKeyLen, safeToCache, null, 0);
-
- }
-
- CryptoAlgorithm(
- final int messageFormatVersion,
- final int blockSizeBits, final int nonceLenBytes, final int tagLenBytes,
- final long maxContentLen, final String keyAlgo, final int keyLenBytes, final int value,
- final String dataKeyAlgo, final int dataKeyLen,
- boolean safeToCache, final String trailingSignatureAlgo, final int trailingSignatureLength
- ) {
- this(messageFormatVersion, blockSizeBits, nonceLenBytes, tagLenBytes,
- maxContentLen, keyAlgo, keyLenBytes, value,
- dataKeyAlgo, dataKeyLen, safeToCache, trailingSignatureAlgo, trailingSignatureLength,
- null, 0, 0, 0);
- }
-
- CryptoAlgorithm(
- final int messageFormatVersion,
- final int blockSizeBits, final int nonceLenBytes, final int tagLenBytes,
- final long maxContentLen, final String keyAlgo, final int keyLenBytes, final int value,
- final String dataKeyAlgo, final int dataKeyLen,
- boolean safeToCache, final String trailingSignatureAlgo, final int trailingSignatureLength,
- final String keyCommitmentAlgo, final int commitmentLength, final int commitmentNonceLength,
- final int suiteDataLength
- ) {
- if ((messageFormatVersion & 0xFF) != messageFormatVersion) {
- throw new IllegalArgumentException("Invalid messageFormatVersion: " + messageFormatVersion);
- }
- // All non-null key commitment algs must be the same as the kdf alg
- if (keyCommitmentAlgo != null && !keyCommitmentAlgo.equals(dataKeyAlgo)) {
- throw new IllegalArgumentException("Invalid keyCommitmentAlgo " + keyCommitmentAlgo +
- ". Must be equal to dataKeyAlgo " + dataKeyAlgo + ".");
- }
- messageFormatVersion_ = (byte) (messageFormatVersion & 0xFF);
- blockSizeBits_ = blockSizeBits;
- nonceLenBytes_ = (byte) nonceLenBytes;
- tagLenBytes_ = tagLenBytes;
- keyAlgo_ = keyAlgo;
- keyLenBytes_ = keyLenBytes;
- maxContentLen_ = maxContentLen;
- safeToCache_ = safeToCache;
- if (value > Short.MAX_VALUE || value < Short.MIN_VALUE) {
- throw new IllegalArgumentException("Invalid value " + value);
- }
- value_ = (short) value;
- dataKeyAlgo_ = dataKeyAlgo;
- dataKeyLen_ = dataKeyLen;
- trailingSigAlgo_ = trailingSignatureAlgo;
- if (trailingSignatureLength > Short.MAX_VALUE || trailingSignatureLength < 0) {
- throw new IllegalArgumentException("Invalid value " + trailingSignatureLength);
- }
- trailingSigLen_ = (short) trailingSignatureLength;
- keyCommitmentAlgo_ = keyCommitmentAlgo;
- commitmentLength_ = commitmentLength;
- commitmentNonceLength_ = commitmentNonceLength;
- suiteDataLength_ = suiteDataLength;
- }
-
- private static int fieldsToLookupKey(final byte messageFormatVersion, final short algorithmId) {
- // We pack the message format version and algorithm id into a single value.
- // Since the algorithm ID is a short and thus 16 bits long, we'll just
- // left shift the message format version by that amount.
- // The message format version is 8 bits, so this totals 24 bits and fits
- // within a standard 32 bit integer.
- return (messageFormatVersion << 16) | algorithmId;
- }
-
- /**
- * Returns the CryptoAlgorithm object that matches the given value
- * assuming a message format version of 1.
- *
- * @param value
- * the value of the object
- * @return the CryptoAlgorithm object that matches the given value, null if no match is found.
- * @deprecated See {@link #deserialize(byte, short)}
- */
- public static CryptoAlgorithm deserialize(final byte messageFormatVersion, final short value) {
- return ID_MAPPING.get(fieldsToLookupKey(messageFormatVersion, value));
- }
-
- /**
- * Returns the length of the message Id in the header for this algorithm.
- */
- public int getMessageIdLength() {
- // For now this is a derived value rather than stored explicitly
- switch (messageFormatVersion_) {
- case VERSION_1:
- return VERSION_1_MESSAGE_ID_LEN;
- case VERSION_2:
- return VERSION_2_MESSAGE_ID_LEN;
- default:
- throw new UnsupportedOperationException("Support for version " + messageFormatVersion_ + " not yet built.");
- }
- }
-
- /**
- * Returns the header nonce to use with this algorithm.
- * null indicates that the header nonce is not a parameter of the algorithm,
- * and is instead stored as part of the message header.
- */
- public byte[] getHeaderNonce() {
- // For now this is a derived value rather than stored explicitly
- switch (messageFormatVersion_) {
- case VERSION_1:
- return null;
- case VERSION_2:
- // V2 explicitly uses an IV of 0 in the header
- return new byte[nonceLenBytes_];
- default:
- throw new UnsupportedOperationException("Support for version " + messageFormatVersion_ + " not yet built.");
- }
+ /** AES-GCM 128 */
+ ALG_AES_128_GCM_IV12_TAG16_NO_KDF(
+ 1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 16, 0x0014, "AES", 16, false),
+ /** AES-GCM 192 */
+ ALG_AES_192_GCM_IV12_TAG16_NO_KDF(
+ 1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 24, 0x0046, "AES", 24, false),
+ /** AES-GCM 256 */
+ ALG_AES_256_GCM_IV12_TAG16_NO_KDF(
+ 1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0078, "AES", 32, false),
+ /** AES-GCM 128 with HKDF-SHA256 */
+ ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256(
+ 1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 16, 0x0114, "HkdfSHA256", 16, true),
+ /** AES-GCM 192 */
+ ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA256(
+ 1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 24, 0x0146, "HkdfSHA256", 24, true),
+ /** AES-GCM 256 */
+ ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256(
+ 1, 128, 12, 16, Constants.GCM_MAX_CONTENT_LEN, "AES", 32, 0x0178, "HkdfSHA256", 32, true),
+
+ /** AES-GCM 128 with ECDSA (SHA256 with the secp256r1 curve) */
+ ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256(
+ 1,
+ 128,
+ 12,
+ 16,
+ Constants.GCM_MAX_CONTENT_LEN,
+ "AES",
+ 16,
+ 0x0214,
+ "HkdfSHA256",
+ 16,
+ true,
+ "SHA256withECDSA",
+ 71),
+ /** AES-GCM 192 with ECDSA (SHA384 with the secp384r1 curve) */
+ ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384(
+ 1,
+ 128,
+ 12,
+ 16,
+ Constants.GCM_MAX_CONTENT_LEN,
+ "AES",
+ 24,
+ 0x0346,
+ "HkdfSHA384",
+ 24,
+ true,
+ "SHA384withECDSA",
+ 103),
+ /** AES-GCM 256 with ECDSA (SHA384 with the secp384r1 curve) */
+ ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384(
+ 1,
+ 128,
+ 12,
+ 16,
+ Constants.GCM_MAX_CONTENT_LEN,
+ "AES",
+ 32,
+ 0x0378,
+ "HkdfSHA384",
+ 32,
+ true,
+ "SHA384withECDSA",
+ 103),
+ /**
+ * AES-GCM 256 with key commitment Note: 1.7.0 of this library only supports decryption of using
+ * this crypto algorithm and does not support encryption with this algorithm
+ */
+ ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY(
+ 2,
+ 128,
+ 12,
+ 16,
+ Constants.GCM_MAX_CONTENT_LEN,
+ "AES",
+ 32,
+ 0x0478,
+ "HkdfSHA512",
+ 32,
+ true,
+ null,
+ 0,
+ "HkdfSHA512",
+ 32,
+ 32,
+ 32),
+ /**
+ * AES-GCM 256 with ECDSA (SHA384 with the secp384r1 curve) and key commitment Note: 1.7.0 of this
+ * library only supports decryption of using this crypto algorithm and does not support encryption
+ * with this algorithm
+ */
+ ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384(
+ 2,
+ 128,
+ 12,
+ 16,
+ Constants.GCM_MAX_CONTENT_LEN,
+ "AES",
+ 32,
+ 0x0578,
+ "HkdfSHA512",
+ 32,
+ true,
+ "SHA384withECDSA",
+ 103,
+ "HkdfSHA512",
+ 32,
+ 32,
+ 32);
+
+ private final byte messageFormatVersion_;
+ private final int blockSizeBits_;
+ private final byte nonceLenBytes_;
+ private final int tagLenBytes_;
+ private final long maxContentLen_;
+ private final String keyAlgo_;
+ private final int keyLenBytes_;
+ private final short value_;
+ private final String trailingSigAlgo_;
+ private final short trailingSigLen_;
+ private final String dataKeyAlgo_;
+ private final int dataKeyLen_;
+ private final boolean safeToCache_;
+ private final String keyCommitmentAlgo_;
+ private final int commitmentLength_;
+ private final int commitmentNonceLength_;
+ private final int suiteDataLength_;
+
+ private static final byte VERSION_1 = (byte) 1;
+ private static final byte VERSION_2 = (byte) 2;
+ private static final int VERSION_1_MESSAGE_ID_LEN = 16;
+ private static final int VERSION_2_MESSAGE_ID_LEN = 32;
+
+ /*
+ * Create a mapping between the CiphertextType object and its byte value representation. Make
+ * this a static method so the map is created when the object is created. This enables fast
+ * lookups of the CryptoAlgorithm given its short value representation and message format version.
+ */
+ private static final Map ID_MAPPING = new HashMap<>();
+
+ static {
+ for (final CryptoAlgorithm s : EnumSet.allOf(CryptoAlgorithm.class)) {
+ ID_MAPPING.put(fieldsToLookupKey(s.messageFormatVersion_, s.value_), s);
}
-
- /**
- * Returns the message format version associated with this algorithm suite.
- */
- public byte getMessageFormatVersion() {
- return messageFormatVersion_;
+ }
+
+ CryptoAlgorithm(
+ final int messageFormatVersion,
+ final int blockSizeBits,
+ final int nonceLenBytes,
+ final int tagLenBytes,
+ final long maxContentLen,
+ final String keyAlgo,
+ final int keyLenBytes,
+ final int value,
+ final String dataKeyAlgo,
+ final int dataKeyLen,
+ boolean safeToCache) {
+ this(
+ messageFormatVersion,
+ blockSizeBits,
+ nonceLenBytes,
+ tagLenBytes,
+ maxContentLen,
+ keyAlgo,
+ keyLenBytes,
+ value,
+ dataKeyAlgo,
+ dataKeyLen,
+ safeToCache,
+ null,
+ 0);
+ }
+
+ CryptoAlgorithm(
+ final int messageFormatVersion,
+ final int blockSizeBits,
+ final int nonceLenBytes,
+ final int tagLenBytes,
+ final long maxContentLen,
+ final String keyAlgo,
+ final int keyLenBytes,
+ final int value,
+ final String dataKeyAlgo,
+ final int dataKeyLen,
+ boolean safeToCache,
+ final String trailingSignatureAlgo,
+ final int trailingSignatureLength) {
+ this(
+ messageFormatVersion,
+ blockSizeBits,
+ nonceLenBytes,
+ tagLenBytes,
+ maxContentLen,
+ keyAlgo,
+ keyLenBytes,
+ value,
+ dataKeyAlgo,
+ dataKeyLen,
+ safeToCache,
+ trailingSignatureAlgo,
+ trailingSignatureLength,
+ null,
+ 0,
+ 0,
+ 0);
+ }
+
+ CryptoAlgorithm(
+ final int messageFormatVersion,
+ final int blockSizeBits,
+ final int nonceLenBytes,
+ final int tagLenBytes,
+ final long maxContentLen,
+ final String keyAlgo,
+ final int keyLenBytes,
+ final int value,
+ final String dataKeyAlgo,
+ final int dataKeyLen,
+ boolean safeToCache,
+ final String trailingSignatureAlgo,
+ final int trailingSignatureLength,
+ final String keyCommitmentAlgo,
+ final int commitmentLength,
+ final int commitmentNonceLength,
+ final int suiteDataLength) {
+ if ((messageFormatVersion & 0xFF) != messageFormatVersion) {
+ throw new IllegalArgumentException("Invalid messageFormatVersion: " + messageFormatVersion);
}
-
- /**
- * Returns the block size of this algorithm in bytes.
- */
- public int getBlockSize() {
- return blockSizeBits_ / 8;
+ // All non-null key commitment algs must be the same as the kdf alg
+ if (keyCommitmentAlgo != null && !keyCommitmentAlgo.equals(dataKeyAlgo)) {
+ throw new IllegalArgumentException(
+ "Invalid keyCommitmentAlgo "
+ + keyCommitmentAlgo
+ + ". Must be equal to dataKeyAlgo "
+ + dataKeyAlgo
+ + ".");
}
-
- /**
- * Returns the nonce length used in this algorithm in bytes.
- */
- public byte getNonceLen() {
- return nonceLenBytes_;
+ messageFormatVersion_ = (byte) (messageFormatVersion & 0xFF);
+ blockSizeBits_ = blockSizeBits;
+ nonceLenBytes_ = (byte) nonceLenBytes;
+ tagLenBytes_ = tagLenBytes;
+ keyAlgo_ = keyAlgo;
+ keyLenBytes_ = keyLenBytes;
+ maxContentLen_ = maxContentLen;
+ safeToCache_ = safeToCache;
+ if (value > Short.MAX_VALUE || value < Short.MIN_VALUE) {
+ throw new IllegalArgumentException("Invalid value " + value);
}
-
- /**
- * Returns the tag length used in this algorithm in bytes.
- */
- public int getTagLen() {
- return tagLenBytes_;
- }
-
- /**
- * Returns the maximum content length in bytes that can be processed under a single data key in
- * this algorithm.
- */
- public long getMaxContentLen() {
- return maxContentLen_;
+ value_ = (short) value;
+ dataKeyAlgo_ = dataKeyAlgo;
+ dataKeyLen_ = dataKeyLen;
+ trailingSigAlgo_ = trailingSignatureAlgo;
+ if (trailingSignatureLength > Short.MAX_VALUE || trailingSignatureLength < 0) {
+ throw new IllegalArgumentException("Invalid value " + trailingSignatureLength);
}
-
- /**
- * Returns the algorithm used for encrypting the plaintext data.
- */
- public String getKeyAlgo() {
- return keyAlgo_;
+ trailingSigLen_ = (short) trailingSignatureLength;
+ keyCommitmentAlgo_ = keyCommitmentAlgo;
+ commitmentLength_ = commitmentLength;
+ commitmentNonceLength_ = commitmentNonceLength;
+ suiteDataLength_ = suiteDataLength;
+ }
+
+ private static int fieldsToLookupKey(final byte messageFormatVersion, final short algorithmId) {
+ // We pack the message format version and algorithm id into a single value.
+ // Since the algorithm ID is a short and thus 16 bits long, we'll just
+ // left shift the message format version by that amount.
+ // The message format version is 8 bits, so this totals 24 bits and fits
+ // within a standard 32 bit integer.
+ return (messageFormatVersion << 16) | algorithmId;
+ }
+
+ /**
+ * Returns the CryptoAlgorithm object that matches the given value assuming a message format
+ * version of 1.
+ *
+ * @param value the value of the object
+ * @return the CryptoAlgorithm object that matches the given value, null if no match is found.
+ * @deprecated See {@link #deserialize(byte, short)}
+ */
+ public static CryptoAlgorithm deserialize(final byte messageFormatVersion, final short value) {
+ return ID_MAPPING.get(fieldsToLookupKey(messageFormatVersion, value));
+ }
+
+ /** Returns the length of the message Id in the header for this algorithm. */
+ public int getMessageIdLength() {
+ // For now this is a derived value rather than stored explicitly
+ switch (messageFormatVersion_) {
+ case VERSION_1:
+ return VERSION_1_MESSAGE_ID_LEN;
+ case VERSION_2:
+ return VERSION_2_MESSAGE_ID_LEN;
+ default:
+ throw new UnsupportedOperationException(
+ "Support for version " + messageFormatVersion_ + " not yet built.");
}
-
- /**
- * Returns the length of the key used in this algorithm in bytes.
- */
- public int getKeyLength() {
- return keyLenBytes_;
+ }
+
+ /**
+ * Returns the header nonce to use with this algorithm. null indicates that the header nonce is
+ * not a parameter of the algorithm, and is instead stored as part of the message header.
+ */
+ public byte[] getHeaderNonce() {
+ // For now this is a derived value rather than stored explicitly
+ switch (messageFormatVersion_) {
+ case VERSION_1:
+ return null;
+ case VERSION_2:
+ // V2 explicitly uses an IV of 0 in the header
+ return new byte[nonceLenBytes_];
+ default:
+ throw new UnsupportedOperationException(
+ "Support for version " + messageFormatVersion_ + " not yet built.");
}
-
- /**
- * Returns the value used to encode this algorithm in the ciphertext.
- */
- public short getValue() {
- return value_;
+ }
+
+ /** Returns the message format version associated with this algorithm suite. */
+ public byte getMessageFormatVersion() {
+ return messageFormatVersion_;
+ }
+
+ /** Returns the block size of this algorithm in bytes. */
+ public int getBlockSize() {
+ return blockSizeBits_ / 8;
+ }
+
+ /** Returns the nonce length used in this algorithm in bytes. */
+ public byte getNonceLen() {
+ return nonceLenBytes_;
+ }
+
+ /** Returns the tag length used in this algorithm in bytes. */
+ public int getTagLen() {
+ return tagLenBytes_;
+ }
+
+ /**
+ * Returns the maximum content length in bytes that can be processed under a single data key in
+ * this algorithm.
+ */
+ public long getMaxContentLen() {
+ return maxContentLen_;
+ }
+
+ /** Returns the algorithm used for encrypting the plaintext data. */
+ public String getKeyAlgo() {
+ return keyAlgo_;
+ }
+
+ /** Returns the length of the key used in this algorithm in bytes. */
+ public int getKeyLength() {
+ return keyLenBytes_;
+ }
+
+ /** Returns the value used to encode this algorithm in the ciphertext. */
+ public short getValue() {
+ return value_;
+ }
+
+ /** Returns the algorithm associated with the data key. */
+ public String getDataKeyAlgo() {
+ return dataKeyAlgo_;
+ }
+
+ /** Returns the length of the data key in bytes. */
+ public int getDataKeyLength() {
+ return dataKeyLen_;
+ }
+
+ /** Returns the algorithm used to calculate the trailing signature */
+ public String getTrailingSignatureAlgo() {
+ return trailingSigAlgo_;
+ }
+
+ /**
+ * Returns whether data keys used with this crypto algorithm can safely be cached and reused for a
+ * different message. If this returns false, reuse of data keys is likely to result in severe
+ * cryptographic weaknesses, potentially even with only a single such use.
+ */
+ public boolean isSafeToCache() {
+ return safeToCache_;
+ }
+
+ /**
+ * Returns the length of the trailing signature generated by this algorithm. The actual trailing
+ * signature may be shorter than this.
+ */
+ public short getTrailingSignatureLength() {
+ return trailingSigLen_;
+ }
+
+ public String getKeyCommitmentAlgo_() {
+ return keyCommitmentAlgo_;
+ }
+
+ /**
+ * Returns a derived value of whether a commitment value is generated with the key in order to
+ * ensure key commitment.
+ */
+ public boolean isCommitting() {
+ return keyCommitmentAlgo_ != null;
+ }
+
+ public int getCommitmentLength() {
+ return commitmentLength_;
+ }
+
+ public int getCommitmentNonceLength() {
+ return commitmentNonceLength_;
+ }
+
+ public int getSuiteDataLength() {
+ return suiteDataLength_;
+ }
+
+ public SecretKey getEncryptionKeyFromDataKey(
+ final SecretKey dataKey, final CiphertextHeaders headers) throws InvalidKeyException {
+ if (!dataKey.getAlgorithm().equalsIgnoreCase(getDataKeyAlgo())) {
+ throw new InvalidKeyException(
+ "DataKey of incorrect algorithm. Expected "
+ + getDataKeyAlgo()
+ + " but was "
+ + dataKey.getAlgorithm());
}
- /**
- * Returns the algorithm associated with the data key.
- */
- public String getDataKeyAlgo() {
- return dataKeyAlgo_;
+ // We perform key derivation differently depending on the message format version
+ switch (messageFormatVersion_) {
+ case VERSION_1:
+ return getNonCommittedEncryptionKey(dataKey, headers);
+ case VERSION_2:
+ return getCommittedEncryptionKey(dataKey, headers);
+ default:
+ throw new UnsupportedOperationException(
+ "Support for message format version " + messageFormatVersion_ + " not yet built.");
}
-
- /**
- * Returns the length of the data key in bytes.
- */
- public int getDataKeyLength() {
- return dataKeyLen_;
+ }
+
+ private SecretKey getCommittedEncryptionKey(
+ final SecretKey dataKey, final CiphertextHeaders headers) throws InvalidKeyException {
+ final CommittedKey committedKey = CommittedKey.generate(this, dataKey, headers.getMessageId());
+ if (!MessageDigest.isEqual(committedKey.getCommitment(), headers.getSuiteData())) {
+ throw new BadCiphertextException(
+ "Key commitment validation failed. Key identity does not match the "
+ + "identity asserted in the message. Halting processing of this message.");
}
-
- /**
- * Returns the algorithm used to calculate the trailing signature
- */
- public String getTrailingSignatureAlgo() {
- return trailingSigAlgo_;
+ return committedKey.getKey();
+ }
+
+ private SecretKey getNonCommittedEncryptionKey(
+ final SecretKey dataKey, final CiphertextHeaders headers) throws InvalidKeyException {
+ final String macAlgorithm;
+
+ switch (this) {
+ case ALG_AES_128_GCM_IV12_TAG16_NO_KDF:
+ case ALG_AES_192_GCM_IV12_TAG16_NO_KDF:
+ case ALG_AES_256_GCM_IV12_TAG16_NO_KDF:
+ return dataKey;
+ case ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256:
+ case ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA256:
+ case ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256:
+ case ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256:
+ macAlgorithm = "HmacSHA256";
+ break;
+ case ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384:
+ case ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384:
+ macAlgorithm = "HmacSHA384";
+ break;
+ default:
+ throw new UnsupportedOperationException("Support for " + this + " not yet built.");
}
-
- /**
- * Returns whether data keys used with this crypto algorithm can safely be cached and reused for a different
- * message. If this returns false, reuse of data keys is likely to result in severe cryptographic weaknesses,
- * potentially even with only a single such use.
- */
- public boolean isSafeToCache() {
- return safeToCache_;
+ if (!dataKey.getFormat().equalsIgnoreCase("RAW")) {
+ throw new InvalidKeyException(
+ "Currently only RAW format keys are supported for HKDF algorithms. Actual format was "
+ + dataKey.getFormat());
}
-
- /**
- * Returns the length of the trailing signature generated by this algorithm. The actual trailing
- * signature may be shorter than this.
- */
- public short getTrailingSignatureLength() {
- return trailingSigLen_;
+ final byte[] messageId = headers.getMessageId();
+ final ByteBuffer info = ByteBuffer.allocate(messageId.length + 2);
+ info.order(ByteOrder.BIG_ENDIAN);
+ info.putShort(getValue());
+ info.put(messageId);
+
+ final byte[] rawDataKey = dataKey.getEncoded();
+ if (rawDataKey.length != getDataKeyLength()) {
+ throw new InvalidKeyException(
+ "DataKey of incorrect length. Expected "
+ + getDataKeyLength()
+ + " but was "
+ + rawDataKey.length);
}
- public String getKeyCommitmentAlgo_() {
- return keyCommitmentAlgo_;
+ final HmacKeyDerivationFunction hkdf;
+ try {
+ hkdf = HmacKeyDerivationFunction.getInstance(macAlgorithm);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
}
- /**
- * Returns a derived value of whether a commitment value is generated with the key in order to ensure key commitment.
- */
- public boolean isCommitting() {
- return keyCommitmentAlgo_ != null;
- }
-
- public int getCommitmentLength() {
- return commitmentLength_;
- }
-
- public int getCommitmentNonceLength() {
- return commitmentNonceLength_;
- }
-
- public int getSuiteDataLength() {
- return suiteDataLength_;
- }
-
- public SecretKey getEncryptionKeyFromDataKey(final SecretKey dataKey, final CiphertextHeaders headers)
- throws InvalidKeyException {
- if (!dataKey.getAlgorithm().equalsIgnoreCase(getDataKeyAlgo())) {
- throw new InvalidKeyException("DataKey of incorrect algorithm. Expected " + getDataKeyAlgo() + " but was "
- + dataKey.getAlgorithm());
- }
-
- // We perform key derivation differently depending on the message format version
- switch (messageFormatVersion_) {
- case VERSION_1:
- return getNonCommittedEncryptionKey(dataKey, headers);
- case VERSION_2:
- return getCommittedEncryptionKey(dataKey, headers);
- default:
- throw new UnsupportedOperationException("Support for message format version " + messageFormatVersion_ +
- " not yet built.");
- }
- }
-
- private SecretKey getCommittedEncryptionKey(final SecretKey dataKey, final CiphertextHeaders headers)
- throws InvalidKeyException {
- final CommittedKey committedKey = CommittedKey.generate(this, dataKey, headers.getMessageId());
- if (!MessageDigest.isEqual(committedKey.getCommitment(), headers.getSuiteData())) {
- throw new BadCiphertextException("Key commitment validation failed. Key identity does not match the " +
- "identity asserted in the message. Halting processing of this message.");
- }
- return committedKey.getKey();
- }
-
- private SecretKey getNonCommittedEncryptionKey(final SecretKey dataKey, final CiphertextHeaders headers)
- throws InvalidKeyException {
- final String macAlgorithm;
-
- switch (this) {
- case ALG_AES_128_GCM_IV12_TAG16_NO_KDF:
- case ALG_AES_192_GCM_IV12_TAG16_NO_KDF:
- case ALG_AES_256_GCM_IV12_TAG16_NO_KDF:
- return dataKey;
- case ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256:
- case ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA256:
- case ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256:
- case ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256:
- macAlgorithm = "HmacSHA256";
- break;
- case ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384:
- case ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384:
- macAlgorithm = "HmacSHA384";
- break;
- default:
- throw new UnsupportedOperationException("Support for " + this + " not yet built.");
- }
- if (!dataKey.getFormat().equalsIgnoreCase("RAW")) {
- throw new InvalidKeyException(
- "Currently only RAW format keys are supported for HKDF algorithms. Actual format was "
- + dataKey.getFormat());
- }
- final byte[] messageId = headers.getMessageId();
- final ByteBuffer info = ByteBuffer.allocate(messageId.length + 2);
- info.order(ByteOrder.BIG_ENDIAN);
- info.putShort(getValue());
- info.put(messageId);
-
- final byte[] rawDataKey = dataKey.getEncoded();
- if (rawDataKey.length != getDataKeyLength()) {
- throw new InvalidKeyException("DataKey of incorrect length. Expected " + getDataKeyLength() + " but was "
- + rawDataKey.length);
- }
-
- final HmacKeyDerivationFunction hkdf;
- try {
- hkdf = HmacKeyDerivationFunction.getInstance(macAlgorithm);
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException(e);
- }
-
- hkdf.init(rawDataKey);
- return new SecretKeySpec(hkdf.deriveKey(info.array(), getKeyLength()), getKeyAlgo());
- }
+ hkdf.init(rawDataKey);
+ return new SecretKeySpec(hkdf.deriveKey(info.array(), getKeyLength()), getKeyAlgo());
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java
index c71dca1e9..33ff8bd5e 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoInputStream.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -15,245 +15,239 @@
import static com.amazonaws.encryptionsdk.internal.Utils.assertNonNull;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
import com.amazonaws.encryptionsdk.internal.MessageCryptoHandler;
import com.amazonaws.encryptionsdk.internal.Utils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
/**
* A CryptoInputStream is a subclass of java.io.InputStream. It performs cryptographic
* transformation of the bytes passing through it.
*
- *
- * The CryptoInputStream wraps a provided InputStream object and performs cryptographic
+ *
The CryptoInputStream wraps a provided InputStream object and performs cryptographic
* transformation of the bytes read from the wrapped InputStream. It uses the cryptography handler
* provided during construction to invoke methods that perform the cryptographic transformations.
*
- *
- * In short, reading from the CryptoInputStream returns bytes that are the cryptographic
+ *
In short, reading from the CryptoInputStream returns bytes that are the cryptographic
* transformations of the bytes read from the wrapped InputStream.
*
- *
- * For example, if the cryptography handler provides methods for decryption, the CryptoInputStream
- * will read ciphertext bytes from the wrapped InputStream, decrypt, and return them as plaintext
- * bytes.
+ *
For example, if the cryptography handler provides methods for decryption, the
+ * CryptoInputStream will read ciphertext bytes from the wrapped InputStream, decrypt, and return
+ * them as plaintext bytes.
*
- *
- * This class adheres strictly to the semantics, especially the failure semantics, of its ancestor
- * class java.io.InputStream. This class overrides all the methods specified in its ancestor class.
+ *
This class adheres strictly to the semantics, especially the failure semantics, of its
+ * ancestor class java.io.InputStream. This class overrides all the methods specified in its
+ * ancestor class.
*
- *
- * To instantiate an instance of this class, please see {@link AwsCrypto}.
+ *
To instantiate an instance of this class, please see {@link AwsCrypto}.
*
- * @param
- * The type of {@link MasterKey}s used to manipulate the data.
+ * @param The type of {@link MasterKey}s used to manipulate the data.
*/
public class CryptoInputStream> extends InputStream {
- private static final int MAX_READ_LEN = 4096;
-
- private byte[] outBytes_ = new byte[0];
- private int outStart_;
- private int outEnd_;
- private final InputStream inputStream_;
- private final MessageCryptoHandler cryptoHandler_;
- private boolean hasFinalCalled_;
- private boolean hasProcessBytesCalled_;
-
- /**
- * Constructs a CryptoInputStream that wraps the provided InputStream object. It performs
- * cryptographic transformation of the bytes read from the wrapped InputStream using the methods
- * provided in the provided CryptoHandler implementation.
- *
- * @param inputStream
- * the inputStream object to be wrapped.
- * @param cryptoHandler
- * the cryptoHandler implementation that provides the methods to use in performing
- * cryptographic transformation of the bytes read from the inputStream.
- */
- CryptoInputStream(final InputStream inputStream, final MessageCryptoHandler cryptoHandler) {
- inputStream_ = Utils.assertNonNull(inputStream, "inputStream");
- cryptoHandler_ = Utils.assertNonNull(cryptoHandler, "cryptoHandler");
- }
-
- /**
- * Fill the output bytes by reading from the wrapped InputStream and processing it through the
- * crypto handler.
- *
- * @return the number of bytes processed and returned by the crypto handler.
- */
- private int fillOutBytes() throws IOException, BadCiphertextException {
- final byte[] inputStreamBytes = new byte[MAX_READ_LEN];
-
- final int readLen = inputStream_.read(inputStreamBytes);
-
- outStart_ = 0;
-
- int processedLen;
- if (readLen < 0) {
- // Mark end of stream until doFinal returns something.
- processedLen = -1;
-
- if (!hasFinalCalled_) {
- int outOffset = 0;
- int outLen = 0;
-
- // Handle the case where processBytes() was never called before.
- // This happens with an empty file where the end of stream is
- // reached on the first read attempt. In this case,
- // processBytes() must be called so the header bytes are written
- // during encryption.
- if (!hasProcessBytesCalled_) {
- outBytes_ = new byte[cryptoHandler_.estimateOutputSize(0)];
- outLen += cryptoHandler_.processBytes(inputStreamBytes, 0, 0, outBytes_, outOffset)
- .getBytesWritten();
- outOffset += outLen;
- } else {
- outBytes_ = new byte[cryptoHandler_.estimateFinalOutputSize()];
- }
-
- // Get final bytes.
- outLen += cryptoHandler_.doFinal(outBytes_, outOffset);
- processedLen = outLen;
- hasFinalCalled_ = true;
- }
+ private static final int MAX_READ_LEN = 4096;
+
+ private byte[] outBytes_ = new byte[0];
+ private int outStart_;
+ private int outEnd_;
+ private final InputStream inputStream_;
+ private final MessageCryptoHandler cryptoHandler_;
+ private boolean hasFinalCalled_;
+ private boolean hasProcessBytesCalled_;
+
+ /**
+ * Constructs a CryptoInputStream that wraps the provided InputStream object. It performs
+ * cryptographic transformation of the bytes read from the wrapped InputStream using the methods
+ * provided in the provided CryptoHandler implementation.
+ *
+ * @param inputStream the inputStream object to be wrapped.
+ * @param cryptoHandler the cryptoHandler implementation that provides the methods to use in
+ * performing cryptographic transformation of the bytes read from the inputStream.
+ */
+ CryptoInputStream(final InputStream inputStream, final MessageCryptoHandler cryptoHandler) {
+ inputStream_ = Utils.assertNonNull(inputStream, "inputStream");
+ cryptoHandler_ = Utils.assertNonNull(cryptoHandler, "cryptoHandler");
+ }
+
+ /**
+ * Fill the output bytes by reading from the wrapped InputStream and processing it through the
+ * crypto handler.
+ *
+ * @return the number of bytes processed and returned by the crypto handler.
+ */
+ private int fillOutBytes() throws IOException, BadCiphertextException {
+ final byte[] inputStreamBytes = new byte[MAX_READ_LEN];
+
+ final int readLen = inputStream_.read(inputStreamBytes);
+
+ outStart_ = 0;
+
+ int processedLen;
+ if (readLen < 0) {
+ // Mark end of stream until doFinal returns something.
+ processedLen = -1;
+
+ if (!hasFinalCalled_) {
+ int outOffset = 0;
+ int outLen = 0;
+
+ // Handle the case where processBytes() was never called before.
+ // This happens with an empty file where the end of stream is
+ // reached on the first read attempt. In this case,
+ // processBytes() must be called so the header bytes are written
+ // during encryption.
+ if (!hasProcessBytesCalled_) {
+ outBytes_ = new byte[cryptoHandler_.estimateOutputSize(0)];
+ outLen +=
+ cryptoHandler_
+ .processBytes(inputStreamBytes, 0, 0, outBytes_, outOffset)
+ .getBytesWritten();
+ outOffset += outLen;
} else {
- // process the read bytes.
- outBytes_ = new byte[cryptoHandler_.estimatePartialOutputSize(readLen)];
- processedLen = cryptoHandler_.processBytes(inputStreamBytes, 0, readLen, outBytes_, outStart_)
- .getBytesWritten();
- hasProcessBytesCalled_ = true;
+ outBytes_ = new byte[cryptoHandler_.estimateFinalOutputSize()];
}
- outEnd_ = processedLen;
- return processedLen;
+ // Get final bytes.
+ outLen += cryptoHandler_.doFinal(outBytes_, outOffset);
+ processedLen = outLen;
+ hasFinalCalled_ = true;
+ }
+ } else {
+ // process the read bytes.
+ outBytes_ = new byte[cryptoHandler_.estimatePartialOutputSize(readLen)];
+ processedLen =
+ cryptoHandler_
+ .processBytes(inputStreamBytes, 0, readLen, outBytes_, outStart_)
+ .getBytesWritten();
+ hasProcessBytesCalled_ = true;
}
- /**
- * {@inheritDoc}
- *
- * @throws BadCiphertextException
- * This is thrown only during decryption if b contains invalid or corrupt
- * ciphertext.
- */
- @Override
- public int read(final byte[] b, final int off, final int len) throws IllegalArgumentException, IOException,
- BadCiphertextException {
- assertNonNull(b, "b");
-
- if (len < 0 || off < 0) {
- throw new IllegalArgumentException(String.format("Invalid values for offset: %d and length: %d", off, len));
- }
-
- if (b.length == 0 || len == 0) {
- return 0;
- }
-
- // fill the output bytes if there aren't any left to return.
- if ((outEnd_ - outStart_) <= 0) {
- int newBytesLen = 0;
-
- // Block until a byte is read or end of stream in the underlying
- // stream is reached.
- while (newBytesLen == 0) {
- newBytesLen = fillOutBytes();
- }
- if (newBytesLen < 0) {
- return -1;
- }
- }
-
- final int copyLen = Math.min((outEnd_ - outStart_), len);
- System.arraycopy(outBytes_, outStart_, b, off, copyLen);
- outStart_ += copyLen;
-
- return copyLen;
+ outEnd_ = processedLen;
+ return processedLen;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException This is thrown only during decryption if b contains invalid or
+ * corrupt ciphertext.
+ */
+ @Override
+ public int read(final byte[] b, final int off, final int len)
+ throws IllegalArgumentException, IOException, BadCiphertextException {
+ assertNonNull(b, "b");
+
+ if (len < 0 || off < 0) {
+ throw new IllegalArgumentException(
+ String.format("Invalid values for offset: %d and length: %d", off, len));
}
- /**
- * {@inheritDoc}
- *
- * @throws BadCiphertextException
- * This is thrown only during decryption if b contains invalid or corrupt
- * ciphertext.
- */
- @Override
- public int read(final byte[] b) throws IllegalArgumentException, IOException, BadCiphertextException {
- return read(b, 0, b.length);
+ if (b.length == 0 || len == 0) {
+ return 0;
}
- /**
- * {@inheritDoc}
- *
- * @throws BadCiphertextException
- * if b contains invalid or corrupt ciphertext. This is thrown only during
- * decryption.
- */
- @Override
- public int read() throws IOException, BadCiphertextException {
- final byte[] bArray = new byte[1];
- int result = 0;
-
- while (result == 0) {
- result = read(bArray, 0, 1);
- }
-
- if (result > 0) {
- return (bArray[0] & 0xFF);
- } else {
- return result;
- }
+ // fill the output bytes if there aren't any left to return.
+ if ((outEnd_ - outStart_) <= 0) {
+ int newBytesLen = 0;
+
+ // Block until a byte is read or end of stream in the underlying
+ // stream is reached.
+ while (newBytesLen == 0) {
+ newBytesLen = fillOutBytes();
+ }
+ if (newBytesLen < 0) {
+ return -1;
+ }
}
- @Override
- public void close() throws IOException {
- inputStream_.close();
+ final int copyLen = Math.min((outEnd_ - outStart_), len);
+ System.arraycopy(outBytes_, outStart_, b, off, copyLen);
+ outStart_ += copyLen;
+
+ return copyLen;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException This is thrown only during decryption if b contains invalid or
+ * corrupt ciphertext.
+ */
+ @Override
+ public int read(final byte[] b)
+ throws IllegalArgumentException, IOException, BadCiphertextException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException if b contains invalid or corrupt ciphertext. This is thrown only
+ * during decryption.
+ */
+ @Override
+ public int read() throws IOException, BadCiphertextException {
+ final byte[] bArray = new byte[1];
+ int result = 0;
+
+ while (result == 0) {
+ result = read(bArray, 0, 1);
}
- /**
- * Returns metadata associated with the performed cryptographic operation.
- */
- @Override
- public int available() throws IOException {
- return (outBytes_.length + inputStream_.available());
+ if (result > 0) {
+ return (bArray[0] & 0xFF);
+ } else {
+ return result;
}
-
- /**
- * Sets an upper bound on the size of the input data. This method should be called before reading any data from the
- * stream. If this method is not called prior to reading any data, performance may be reduced (notably, it will not
- * be possible to cache data keys when encrypting).
- *
- * Among other things, this size is used to enforce limits configured on the {@link CachingCryptoMaterialsManager}.
- *
- * If the input size set here is exceeded, an exception will be thrown, and the encyption or decryption will fail.
- *
- * If this method is called multiple times, the smallest bound will be used.
- *
- * @param size Maximum input size.
- */
- public void setMaxInputLength(long size) {
- cryptoHandler_.setMaxInputLength(size);
- }
-
- /**
- * Returns the result of the cryptographic operations including associate metadata.
- *
- * @throws IOException
- * @throws BadCiphertextException
- */
- public CryptoResult, K> getCryptoResult() throws BadCiphertextException, IOException {
- while (!cryptoHandler_.getHeaders().isComplete()) {
- if (fillOutBytes() == -1) {
- throw new BadCiphertextException("No CiphertextHeaders found.");
- }
- }
- //noinspection unchecked
- return new CryptoResult<>(
- this,
- (List) cryptoHandler_.getMasterKeys(),
- cryptoHandler_.getHeaders());
+ }
+
+ @Override
+ public void close() throws IOException {
+ inputStream_.close();
+ }
+
+ /** Returns metadata associated with the performed cryptographic operation. */
+ @Override
+ public int available() throws IOException {
+ return (outBytes_.length + inputStream_.available());
+ }
+
+ /**
+ * Sets an upper bound on the size of the input data. This method should be called before reading
+ * any data from the stream. If this method is not called prior to reading any data, performance
+ * may be reduced (notably, it will not be possible to cache data keys when encrypting).
+ *
+ * Among other things, this size is used to enforce limits configured on the {@link
+ * CachingCryptoMaterialsManager}.
+ *
+ *
If the input size set here is exceeded, an exception will be thrown, and the encyption or
+ * decryption will fail.
+ *
+ *
If this method is called multiple times, the smallest bound will be used.
+ *
+ * @param size Maximum input size.
+ */
+ public void setMaxInputLength(long size) {
+ cryptoHandler_.setMaxInputLength(size);
+ }
+
+ /**
+ * Returns the result of the cryptographic operations including associate metadata.
+ *
+ * @throws IOException
+ * @throws BadCiphertextException
+ */
+ public CryptoResult, K> getCryptoResult()
+ throws BadCiphertextException, IOException {
+ while (!cryptoHandler_.getHeaders().isComplete()) {
+ if (fillOutBytes() == -1) {
+ throw new BadCiphertextException("No CiphertextHeaders found.");
+ }
}
+ //noinspection unchecked
+ return new CryptoResult<>(
+ this, (List) cryptoHandler_.getMasterKeys(), cryptoHandler_.getHeaders());
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoMaterialsManager.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoMaterialsManager.java
index 6b29b2358..38bcb1b22 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoMaterialsManager.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoMaterialsManager.java
@@ -1,29 +1,29 @@
package com.amazonaws.encryptionsdk;
-import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
/**
- * The crypto materials manager is responsible for preparing the cryptographic materials needed to process a request -
- * notably, preparing the cleartext data key and (if applicable) trailing signature keys on both encrypt and decrypt.
+ * The crypto materials manager is responsible for preparing the cryptographic materials needed to
+ * process a request - notably, preparing the cleartext data key and (if applicable) trailing
+ * signature keys on both encrypt and decrypt.
*/
public interface CryptoMaterialsManager {
- /**
- * Prepares materials for an encrypt request. The resulting materials result must have a cleartext data key and
- * (if applicable for the crypto algorithm in use) a trailing signature key.
- *
- * The encryption context returned may be different from the one passed in the materials request, and will be
- * serialized (in cleartext) within the encrypted message.
- *
- * @see EncryptionMaterials
- * @see EncryptionMaterialsRequest
- *
- * @param request
- * @return
- */
- EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request);
+ /**
+ * Prepares materials for an encrypt request. The resulting materials result must have a cleartext
+ * data key and (if applicable for the crypto algorithm in use) a trailing signature key.
+ *
+ * The encryption context returned may be different from the one passed in the materials
+ * request, and will be serialized (in cleartext) within the encrypted message.
+ *
+ * @see EncryptionMaterials
+ * @see EncryptionMaterialsRequest
+ * @param request
+ * @return
+ */
+ EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request);
- DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request);
+ DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request);
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java
index 1dbee7c46..69526d800 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoOutputStream.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -13,174 +13,159 @@
package com.amazonaws.encryptionsdk;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.List;
-
import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
import com.amazonaws.encryptionsdk.internal.MessageCryptoHandler;
import com.amazonaws.encryptionsdk.internal.Utils;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
/**
* A CryptoOutputStream is a subclass of java.io.OutputStream. It performs cryptographic
* transformation of the bytes passing through it.
- *
- *
- * The CryptoOutputStream wraps a provided OutputStream object and performs cryptographic
+ *
+ *
The CryptoOutputStream wraps a provided OutputStream object and performs cryptographic
* transformation of the bytes written to it. The transformed bytes are then written to the wrapped
* OutputStream. It uses the cryptography handler provided during construction to invoke methods
* that perform the cryptographic transformations.
- *
- *
- * In short, writing to the CryptoOutputStream results in those bytes being cryptographically
+ *
+ *
In short, writing to the CryptoOutputStream results in those bytes being cryptographically
* transformed and written to the wrapped OutputStream.
- *
- *
- * For example, if the crypto handler provides methods for decryption, the CryptoOutputStream will
- * decrypt the provided ciphertext bytes and write the plaintext bytes to the wrapped OutputStream.
- *
- *
- * This class adheres strictly to the semantics, especially the failure semantics, of its ancestor
- * class java.io.OutputStream. This class overrides all the methods specified in its ancestor class.
- *
- *
- * To instantiate an instance of this class, please see {@link AwsCrypto}.
- *
- * @param
- * The type of {@link MasterKey}s used to manipulate the data.
+ *
+ * For example, if the crypto handler provides methods for decryption, the CryptoOutputStream
+ * will decrypt the provided ciphertext bytes and write the plaintext bytes to the wrapped
+ * OutputStream.
+ *
+ *
This class adheres strictly to the semantics, especially the failure semantics, of its
+ * ancestor class java.io.OutputStream. This class overrides all the methods specified in its
+ * ancestor class.
+ *
+ *
To instantiate an instance of this class, please see {@link AwsCrypto}.
+ *
+ * @param The type of {@link MasterKey}s used to manipulate the data.
*/
public class CryptoOutputStream> extends OutputStream {
- private final OutputStream outputStream_;
-
- private final MessageCryptoHandler cryptoHandler_;
-
- /**
- * Constructs a CryptoOutputStream that wraps the provided OutputStream object. It performs
- * cryptographic transformation of the bytes written to it using the methods provided in the
- * provided CryptoHandler implementation. The transformed bytes are then written to the wrapped
- * OutputStream.
- *
- * @param outputStream
- * the outputStream object to be wrapped.
- * @param cryptoHandler
- * the cryptoHandler implementation that provides the methods to use in performing
- * cryptographic transformation of the bytes written to this stream.
- */
- CryptoOutputStream(final OutputStream outputStream, final MessageCryptoHandler cryptoHandler) {
- outputStream_ = Utils.assertNonNull(outputStream, "outputStream");
- cryptoHandler_ = Utils.assertNonNull(cryptoHandler, "cryptoHandler");
+ private final OutputStream outputStream_;
+
+ private final MessageCryptoHandler cryptoHandler_;
+
+ /**
+ * Constructs a CryptoOutputStream that wraps the provided OutputStream object. It performs
+ * cryptographic transformation of the bytes written to it using the methods provided in the
+ * provided CryptoHandler implementation. The transformed bytes are then written to the wrapped
+ * OutputStream.
+ *
+ * @param outputStream the outputStream object to be wrapped.
+ * @param cryptoHandler the cryptoHandler implementation that provides the methods to use in
+ * performing cryptographic transformation of the bytes written to this stream.
+ */
+ CryptoOutputStream(final OutputStream outputStream, final MessageCryptoHandler cryptoHandler) {
+ outputStream_ = Utils.assertNonNull(outputStream, "outputStream");
+ cryptoHandler_ = Utils.assertNonNull(cryptoHandler, "cryptoHandler");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException This is thrown only during decryption if b contains invalid or
+ * corrupt ciphertext.
+ */
+ @Override
+ public void write(final byte[] b)
+ throws IllegalArgumentException, IOException, BadCiphertextException {
+ if (b == null) {
+ throw new IllegalArgumentException("b cannot be null");
}
-
- /**
- * {@inheritDoc}
- *
- * @throws BadCiphertextException
- * This is thrown only during decryption if b contains invalid or corrupt
- * ciphertext.
- */
- @Override
- public void write(final byte[] b) throws IllegalArgumentException, IOException, BadCiphertextException {
- if (b == null) {
- throw new IllegalArgumentException("b cannot be null");
- }
- write(b, 0, b.length);
+ write(b, 0, b.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException This is thrown only during decryption if b contains invalid or
+ * corrupt ciphertext.
+ */
+ @Override
+ public void write(final byte[] b, final int off, final int len)
+ throws IllegalArgumentException, IOException, BadCiphertextException {
+ if (b == null) {
+ throw new IllegalArgumentException("b cannot be null");
}
- /**
- * {@inheritDoc}
- *
- * @throws BadCiphertextException
- * This is thrown only during decryption if b contains invalid or corrupt
- * ciphertext.
- */
- @Override
- public void write(final byte[] b, final int off, final int len) throws IllegalArgumentException, IOException,
- BadCiphertextException {
- if (b == null) {
- throw new IllegalArgumentException("b cannot be null");
- }
-
- if (len < 0 || off < 0) {
- throw new IllegalArgumentException(String.format("Invalid values for offset: %d and length: %d", off, len));
- }
-
- final int outLen = cryptoHandler_.estimatePartialOutputSize(len);
- final byte[] outBytes = new byte[outLen];
-
- int bytesWritten = cryptoHandler_.processBytes(b, off, len, outBytes, 0).getBytesWritten();
- if (bytesWritten > 0) {
- outputStream_.write(outBytes, 0, bytesWritten);
- }
+ if (len < 0 || off < 0) {
+ throw new IllegalArgumentException(
+ String.format("Invalid values for offset: %d and length: %d", off, len));
}
- /**
- * {@inheritDoc}
- *
- * @throws BadCiphertextException
- * This is thrown only during decryption if b contains invalid or corrupt
- * ciphertext.
- */
- @Override
- public void write(int b) throws IOException, BadCiphertextException {
- byte[] bArray = new byte[1];
- bArray[0] = (byte) b;
- write(bArray, 0, 1);
- }
+ final int outLen = cryptoHandler_.estimatePartialOutputSize(len);
+ final byte[] outBytes = new byte[outLen];
- /**
- * Closes this output stream and releases any system resources associated
- * with this stream.
- *
- *
- * This method writes any final bytes to the underlying stream that complete
- * the cyptographic transformation of the written bytes. It also calls close
- * on the wrapped OutputStream.
- *
- * @throws IOException
- * if an I/O error occurs.
- * @throws BadCiphertextException
- * This is thrown only during decryption if b contains invalid
- * or corrupt ciphertext.
- */
- @Override
- public void close() throws IOException, BadCiphertextException {
- final byte[] outBytes = new byte[cryptoHandler_.estimateFinalOutputSize()];
- int finalLen = cryptoHandler_.doFinal(outBytes, 0);
-
- outputStream_.write(outBytes, 0, finalLen);
- outputStream_.close();
+ int bytesWritten = cryptoHandler_.processBytes(b, off, len, outBytes, 0).getBytesWritten();
+ if (bytesWritten > 0) {
+ outputStream_.write(outBytes, 0, bytesWritten);
}
-
- /**
- * Sets an upper bound on the size of the input data. This method should be called before writing any data to the
- * stream. If this method is not called prior to writing data, performance may be reduced (notably, it will not
- * be possible to cache data keys when encrypting).
- *
- * Among other things, this size is used to enforce limits configured on the {@link CachingCryptoMaterialsManager}.
- *
- * If the size set here is exceeded, an exception will be thrown, and the encyption or decryption will fail.
- *
- * If this method is called multiple times, the smallest bound will be used.
- *
- * @param size Maximum input size.
- */
- public void setMaxInputLength(long size) {
- cryptoHandler_.setMaxInputLength(size);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws BadCiphertextException This is thrown only during decryption if b contains invalid or
+ * corrupt ciphertext.
+ */
+ @Override
+ public void write(int b) throws IOException, BadCiphertextException {
+ byte[] bArray = new byte[1];
+ bArray[0] = (byte) b;
+ write(bArray, 0, 1);
+ }
+
+ /**
+ * Closes this output stream and releases any system resources associated with this stream.
+ *
+ *
This method writes any final bytes to the underlying stream that complete the cyptographic
+ * transformation of the written bytes. It also calls close on the wrapped OutputStream.
+ *
+ * @throws IOException if an I/O error occurs.
+ * @throws BadCiphertextException This is thrown only during decryption if b contains invalid or
+ * corrupt ciphertext.
+ */
+ @Override
+ public void close() throws IOException, BadCiphertextException {
+ final byte[] outBytes = new byte[cryptoHandler_.estimateFinalOutputSize()];
+ int finalLen = cryptoHandler_.doFinal(outBytes, 0);
+
+ outputStream_.write(outBytes, 0, finalLen);
+ outputStream_.close();
+ }
+
+ /**
+ * Sets an upper bound on the size of the input data. This method should be called before writing
+ * any data to the stream. If this method is not called prior to writing data, performance may be
+ * reduced (notably, it will not be possible to cache data keys when encrypting).
+ *
+ *
Among other things, this size is used to enforce limits configured on the {@link
+ * CachingCryptoMaterialsManager}.
+ *
+ *
If the size set here is exceeded, an exception will be thrown, and the encyption or
+ * decryption will fail.
+ *
+ *
If this method is called multiple times, the smallest bound will be used.
+ *
+ * @param size Maximum input size.
+ */
+ public void setMaxInputLength(long size) {
+ cryptoHandler_.setMaxInputLength(size);
+ }
+
+ /** Returns the result of the cryptographic operations including associate metadata. */
+ public CryptoResult, K> getCryptoResult() {
+ if (!cryptoHandler_.getHeaders().isComplete()) {
+ throw new IllegalStateException("Ciphertext headers not yet written to stream");
}
- /**
- * Returns the result of the cryptographic operations including associate metadata.
- */
- public CryptoResult, K> getCryptoResult() {
- if (!cryptoHandler_.getHeaders().isComplete()) {
- throw new IllegalStateException("Ciphertext headers not yet written to stream");
- }
-
- //noinspection unchecked
- return new CryptoResult<>(
- this,
- (List) cryptoHandler_.getMasterKeys(),
- cryptoHandler_.getHeaders());
- }
+ //noinspection unchecked
+ return new CryptoResult<>(
+ this, (List) cryptoHandler_.getMasterKeys(), cryptoHandler_.getHeaders());
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java
index 616ed3756..167044132 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoResult.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -13,84 +13,75 @@
package com.amazonaws.encryptionsdk;
+import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
-
/**
- * Represents the result of an operation by {@link AwsCrypto}. It not only captures the
- * {@code result} of the operation but also additional metadata such as the
- * {@code encryptionContext}, {@code algorithm}, {@link MasterKey}(s), and any other information
- * captured in the {@link CiphertextHeaders}.
+ * Represents the result of an operation by {@link AwsCrypto}. It not only captures the {@code
+ * result} of the operation but also additional metadata such as the {@code encryptionContext},
+ * {@code algorithm}, {@link MasterKey}(s), and any other information captured in the {@link
+ * CiphertextHeaders}.
*
- * @param
- * the type of the underlying {@code result}
- * @param
- * the type of the {@link MasterKey}s used in production of this result
+ * @param the type of the underlying {@code result}
+ * @param the type of the {@link MasterKey}s used in production of this result
*/
public class CryptoResult> {
- private final T result_;
- private final List masterKeys_;
- private final Map encryptionContext_;
- private final CiphertextHeaders headers_;
+ private final T result_;
+ private final List masterKeys_;
+ private final Map encryptionContext_;
+ private final CiphertextHeaders headers_;
- /**
- * Note, does not make a defensive copy of any of the data.
- */
- CryptoResult(final T result, final List masterKeys, final CiphertextHeaders headers) {
- result_ = result;
- masterKeys_ = Collections.unmodifiableList(masterKeys);
- headers_ = headers;
- encryptionContext_ = headers_.getEncryptionContextMap();
- }
+ /** Note, does not make a defensive copy of any of the data. */
+ CryptoResult(final T result, final List masterKeys, final CiphertextHeaders headers) {
+ result_ = result;
+ masterKeys_ = Collections.unmodifiableList(masterKeys);
+ headers_ = headers;
+ encryptionContext_ = headers_.getEncryptionContextMap();
+ }
- /**
- * The actual result of the cryptographic operation. This is not a defensive copy and callers
- * should not modify it.
- *
- * @return
- */
- public T getResult() {
- return result_;
- }
+ /**
+ * The actual result of the cryptographic operation. This is not a defensive copy and callers
+ * should not modify it.
+ *
+ * @return
+ */
+ public T getResult() {
+ return result_;
+ }
- /**
- * Returns all relevant {@link MasterKey}s. In the case of encryption, returns all
- * {@code MasterKey}s used to protect the ciphertext. In the case of decryption, returns just
- * the {@code MasterKey} used to decrypt the ciphertext.
- *
- * @return
- */
- public List getMasterKeys() {
- return masterKeys_;
- }
+ /**
+ * Returns all relevant {@link MasterKey}s. In the case of encryption, returns all {@code
+ * MasterKey}s used to protect the ciphertext. In the case of decryption, returns just the {@code
+ * MasterKey} used to decrypt the ciphertext.
+ *
+ * @return
+ */
+ public List getMasterKeys() {
+ return masterKeys_;
+ }
- /**
- * Convenience method for retrieving the keyIds in the results from {@link #getMasterKeys()}.
- */
- public List getMasterKeyIds() {
- final List result = new ArrayList<>(masterKeys_.size());
- for (final MasterKey mk : masterKeys_) {
- result.add(mk.getKeyId());
- }
- return result;
+ /** Convenience method for retrieving the keyIds in the results from {@link #getMasterKeys()}. */
+ public List getMasterKeyIds() {
+ final List result = new ArrayList<>(masterKeys_.size());
+ for (final MasterKey mk : masterKeys_) {
+ result.add(mk.getKeyId());
}
+ return result;
+ }
- public Map getEncryptionContext() {
- return encryptionContext_;
- }
+ public Map getEncryptionContext() {
+ return encryptionContext_;
+ }
- /**
- * Convenience method equivalent to {@link #getHeaders()}.{@code getCryptoAlgoId()}.
- */
- public CryptoAlgorithm getCryptoAlgorithm() {
- return headers_.getCryptoAlgoId();
- }
+ /** Convenience method equivalent to {@link #getHeaders()}.{@code getCryptoAlgoId()}. */
+ public CryptoAlgorithm getCryptoAlgorithm() {
+ return headers_.getCryptoAlgoId();
+ }
- public CiphertextHeaders getHeaders() {
- return headers_;
- }
+ public CiphertextHeaders getHeaders() {
+ return headers_;
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/DataKey.java b/src/main/java/com/amazonaws/encryptionsdk/DataKey.java
index 21d174f00..33e9492fd 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/DataKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/DataKey.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -18,50 +18,48 @@
/**
* Represents both the cleartext and encrypted bytes of a data key.
*
- * @param
- * the type of {@link MasterKey} used to protect this {@code DataKey}.
+ * @param the type of {@link MasterKey} used to protect this {@code DataKey}.
*/
public class DataKey> implements EncryptedDataKey {
- private final byte[] providerInformation_;
- private final byte[] encryptedDataKey_;
- private final SecretKey key_;
- private final M masterKey_;
+ private final byte[] providerInformation_;
+ private final byte[] encryptedDataKey_;
+ private final SecretKey key_;
+ private final M masterKey_;
- public DataKey(final SecretKey key, final byte[] encryptedDataKey, final byte[] providerInformation,
- final M masterKey) {
- super();
- key_ = key;
- encryptedDataKey_ = encryptedDataKey.clone();
- providerInformation_ = providerInformation.clone();
- masterKey_ = masterKey;
- }
+ public DataKey(
+ final SecretKey key,
+ final byte[] encryptedDataKey,
+ final byte[] providerInformation,
+ final M masterKey) {
+ super();
+ key_ = key;
+ encryptedDataKey_ = encryptedDataKey.clone();
+ providerInformation_ = providerInformation.clone();
+ masterKey_ = masterKey;
+ }
- /**
- * Returns the cleartext bytes of the data key.
- */
- public SecretKey getKey() {
- return key_;
- }
+ /** Returns the cleartext bytes of the data key. */
+ public SecretKey getKey() {
+ return key_;
+ }
- @Override
- public String getProviderId() {
- return masterKey_.getProviderId();
- }
+ @Override
+ public String getProviderId() {
+ return masterKey_.getProviderId();
+ }
- @Override
- public byte[] getProviderInformation() {
- return providerInformation_.clone();
- }
+ @Override
+ public byte[] getProviderInformation() {
+ return providerInformation_.clone();
+ }
- @Override
- public byte[] getEncryptedDataKey() {
- return encryptedDataKey_.clone();
- }
+ @Override
+ public byte[] getEncryptedDataKey() {
+ return encryptedDataKey_.clone();
+ }
- /**
- * Returns the {@link MasterKey} used to encrypt this {@link DataKey}.
- */
- public M getMasterKey() {
- return masterKey_;
- }
+ /** Returns the {@link MasterKey} used to encrypt this {@link DataKey}. */
+ public M getMasterKey() {
+ return masterKey_;
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java b/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java
index 43cc78c6f..22980ad2f 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java
@@ -5,157 +5,157 @@
import static com.amazonaws.encryptionsdk.internal.Utils.assertNonNull;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
import com.amazonaws.encryptionsdk.internal.Constants;
-import com.amazonaws.encryptionsdk.internal.Utils;
import com.amazonaws.encryptionsdk.internal.TrailingSignatureAlgorithm;
-import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
+import com.amazonaws.encryptionsdk.internal.Utils;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
+import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
+import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.KeyBlob;
-import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* The default implementation of {@link CryptoMaterialsManager}, used implicitly when passing a
* {@link MasterKeyProvider} to methods in {@link AwsCrypto}.
*
- * This default implementation delegates to a specific {@link MasterKeyProvider} specified at construction time. It also
- * handles generating trailing signature keys when needed, placing them in the encryption context (and extracting them
- * at decrypt time).
+ * This default implementation delegates to a specific {@link MasterKeyProvider} specified at
+ * construction time. It also handles generating trailing signature keys when needed, placing them
+ * in the encryption context (and extracting them at decrypt time).
*/
public class DefaultCryptoMaterialsManager implements CryptoMaterialsManager {
- private final MasterKeyProvider> mkp;
-
- private final CryptoAlgorithm DEFAULT_CRYPTO_ALGORITHM = CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384;
-
- /**
- * @param mkp The master key provider to delegate to
- */
- public DefaultCryptoMaterialsManager(MasterKeyProvider> mkp) {
- Utils.assertNonNull(mkp, "mkp");
- this.mkp = mkp;
+ private final MasterKeyProvider> mkp;
+
+ private final CryptoAlgorithm DEFAULT_CRYPTO_ALGORITHM =
+ CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384;
+
+ /** @param mkp The master key provider to delegate to */
+ public DefaultCryptoMaterialsManager(MasterKeyProvider> mkp) {
+ Utils.assertNonNull(mkp, "mkp");
+ this.mkp = mkp;
+ }
+
+ @Override
+ public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
+ Map context = request.getContext();
+
+ CryptoAlgorithm algo = request.getRequestedAlgorithm();
+ CommitmentPolicy commitmentPolicy = request.getCommitmentPolicy();
+ // Set default according to commitment policy
+ if (algo == null && commitmentPolicy == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
+ algo = CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384;
+ } else if (algo == null) {
+ algo = CryptoAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384;
}
- @Override public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
- Map context = request.getContext();
-
- CryptoAlgorithm algo = request.getRequestedAlgorithm();
- CommitmentPolicy commitmentPolicy = request.getCommitmentPolicy();
- // Set default according to commitment policy
- if (algo == null && commitmentPolicy == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
- algo = CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384;
- } else if (algo == null) {
- algo = CryptoAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384;
- }
-
- KeyPair trailingKeys = null;
- if (algo.getTrailingSignatureLength() > 0) {
- try {
- trailingKeys = generateTrailingSigKeyPair(algo);
- if (context.containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
- throw new IllegalArgumentException("EncryptionContext contains reserved field "
- + Constants.EC_PUBLIC_KEY_FIELD);
- }
- // make mutable
- context = new HashMap<>(context);
- context.put(Constants.EC_PUBLIC_KEY_FIELD, serializeTrailingKeyForEc(algo, trailingKeys));
- } catch (final GeneralSecurityException ex) {
- throw new AwsCryptoException(ex);
- }
+ KeyPair trailingKeys = null;
+ if (algo.getTrailingSignatureLength() > 0) {
+ try {
+ trailingKeys = generateTrailingSigKeyPair(algo);
+ if (context.containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
+ throw new IllegalArgumentException(
+ "EncryptionContext contains reserved field " + Constants.EC_PUBLIC_KEY_FIELD);
}
+ // make mutable
+ context = new HashMap<>(context);
+ context.put(Constants.EC_PUBLIC_KEY_FIELD, serializeTrailingKeyForEc(algo, trailingKeys));
+ } catch (final GeneralSecurityException ex) {
+ throw new AwsCryptoException(ex);
+ }
+ }
- final MasterKeyRequest.Builder mkRequestBuilder = MasterKeyRequest.newBuilder();
- mkRequestBuilder.setEncryptionContext(context);
+ final MasterKeyRequest.Builder mkRequestBuilder = MasterKeyRequest.newBuilder();
+ mkRequestBuilder.setEncryptionContext(context);
- mkRequestBuilder.setStreaming(request.getPlaintextSize() == -1);
- if (request.getPlaintext() != null) {
- mkRequestBuilder.setPlaintext(request.getPlaintext());
- } else {
- mkRequestBuilder.setSize(request.getPlaintextSize());
- }
-
- @SuppressWarnings("unchecked")
- final List mks
- = (List)assertNonNull(mkp, "provider")
- .getMasterKeysForEncryption(mkRequestBuilder.build());
+ mkRequestBuilder.setStreaming(request.getPlaintextSize() == -1);
+ if (request.getPlaintext() != null) {
+ mkRequestBuilder.setPlaintext(request.getPlaintext());
+ } else {
+ mkRequestBuilder.setSize(request.getPlaintextSize());
+ }
- if (mks.isEmpty()) {
- throw new IllegalArgumentException("No master keys provided");
- }
+ @SuppressWarnings("unchecked")
+ final List mks =
+ (List)
+ assertNonNull(mkp, "provider").getMasterKeysForEncryption(mkRequestBuilder.build());
- DataKey> dataKey = mks.get(0).generateDataKey(algo, context);
+ if (mks.isEmpty()) {
+ throw new IllegalArgumentException("No master keys provided");
+ }
- List keyBlobs = new ArrayList<>(mks.size());
- keyBlobs.add(new KeyBlob(dataKey));
+ DataKey> dataKey = mks.get(0).generateDataKey(algo, context);
- for (int i = 1; i < mks.size(); i++) {
- //noinspection unchecked
- keyBlobs.add(new KeyBlob(mks.get(i).encryptDataKey(algo, context, dataKey)));
- }
+ List keyBlobs = new ArrayList<>(mks.size());
+ keyBlobs.add(new KeyBlob(dataKey));
- //noinspection unchecked
- return EncryptionMaterials.newBuilder()
- .setAlgorithm(algo)
- .setCleartextDataKey(dataKey.getKey())
- .setEncryptedDataKeys(keyBlobs)
- .setEncryptionContext(context)
- .setTrailingSignatureKey(trailingKeys == null ? null : trailingKeys.getPrivate())
- .setMasterKeys(mks)
- .build();
+ for (int i = 1; i < mks.size(); i++) {
+ //noinspection unchecked
+ keyBlobs.add(new KeyBlob(mks.get(i).encryptDataKey(algo, context, dataKey)));
}
- @Override public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
- DataKey> dataKey = mkp.decryptDataKey(
- request.getAlgorithm(),
- request.getEncryptedDataKeys(),
- request.getEncryptionContext()
- );
+ //noinspection unchecked
+ return EncryptionMaterials.newBuilder()
+ .setAlgorithm(algo)
+ .setCleartextDataKey(dataKey.getKey())
+ .setEncryptedDataKeys(keyBlobs)
+ .setEncryptionContext(context)
+ .setTrailingSignatureKey(trailingKeys == null ? null : trailingKeys.getPrivate())
+ .setMasterKeys(mks)
+ .build();
+ }
+
+ @Override
+ public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
+ DataKey> dataKey =
+ mkp.decryptDataKey(
+ request.getAlgorithm(), request.getEncryptedDataKeys(), request.getEncryptionContext());
+
+ if (dataKey == null) {
+ throw new CannotUnwrapDataKeyException("Could not decrypt any data keys");
+ }
- if (dataKey == null) {
- throw new CannotUnwrapDataKeyException("Could not decrypt any data keys");
- }
+ PublicKey pubKey = null;
+ if (request.getAlgorithm().getTrailingSignatureLength() > 0) {
+ try {
+ String serializedPubKey = request.getEncryptionContext().get(Constants.EC_PUBLIC_KEY_FIELD);
- PublicKey pubKey = null;
- if (request.getAlgorithm().getTrailingSignatureLength() > 0) {
- try {
- String serializedPubKey = request.getEncryptionContext().get(Constants.EC_PUBLIC_KEY_FIELD);
-
- if (serializedPubKey == null) {
- throw new AwsCryptoException("Missing trailing signature public key");
- }
-
- pubKey = deserializeTrailingKeyFromEc(request.getAlgorithm(), serializedPubKey);
- } catch (final IllegalStateException ex) {
- throw new AwsCryptoException(ex);
- }
- } else if (request.getEncryptionContext().containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
- throw new AwsCryptoException("Trailing signature public key found for non-signed algorithm");
+ if (serializedPubKey == null) {
+ throw new AwsCryptoException("Missing trailing signature public key");
}
- return DecryptionMaterials.newBuilder()
- .setDataKey(dataKey)
- .setTrailingSignatureKey(pubKey)
- .build();
+ pubKey = deserializeTrailingKeyFromEc(request.getAlgorithm(), serializedPubKey);
+ } catch (final IllegalStateException ex) {
+ throw new AwsCryptoException(ex);
+ }
+ } else if (request.getEncryptionContext().containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
+ throw new AwsCryptoException("Trailing signature public key found for non-signed algorithm");
}
- private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey) {
- return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).deserializePublicKey(pubKey);
- }
-
- private static String serializeTrailingKeyForEc(CryptoAlgorithm algo, KeyPair trailingKeys) {
- return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).serializePublicKey(trailingKeys.getPublic());
- }
-
- private static KeyPair generateTrailingSigKeyPair(CryptoAlgorithm algo) throws GeneralSecurityException {
- return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).generateKey();
- }
+ return DecryptionMaterials.newBuilder()
+ .setDataKey(dataKey)
+ .setTrailingSignatureKey(pubKey)
+ .build();
+ }
+
+ private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey) {
+ return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).deserializePublicKey(pubKey);
+ }
+
+ private static String serializeTrailingKeyForEc(CryptoAlgorithm algo, KeyPair trailingKeys) {
+ return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo)
+ .serializePublicKey(trailingKeys.getPublic());
+ }
+
+ private static KeyPair generateTrailingSigKeyPair(CryptoAlgorithm algo)
+ throws GeneralSecurityException {
+ return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).generateKey();
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/EncryptedDataKey.java b/src/main/java/com/amazonaws/encryptionsdk/EncryptedDataKey.java
index 4629a9e07..781d8e93c 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/EncryptedDataKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/EncryptedDataKey.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -13,112 +13,111 @@
package com.amazonaws.encryptionsdk;
-//@ model import java.util.Arrays;
-//@ model import java.nio.charset.StandardCharsets;
+// @ model import java.util.Arrays;
+// @ model import java.nio.charset.StandardCharsets;
-
-//@ nullable_by_default
+// @ nullable_by_default
public interface EncryptedDataKey {
-
- //@// An EncryptedDataKey object abstractly contains 3 pieces of data.
- //@// These are represented by 3 byte arrays:
-
- //@ model public instance byte[] providerId;
- //@ model public instance byte[] providerInformation;
- //@ model public instance byte[] encryptedDataKey;
-
- //@// The fields of an EncryptedDataKey may be populated via deserialization. The
- //@// Encryption SDK design allows the deserialization routine to be called repeatedly,
- //@// each call trying to fill in information that for some reason was not possible
- //@// with the previous call. In some such "intermediate" states, the deserialization
- //@// is incomplete in a way that other methods don't expect. Therefore, those methods
- //@// should not be called in these incomplete intermediate states. The model field
- //@// isDeserializing is true in those incomplete intermediate states, and it is used
- //@// in method specifications.
- //@ public model instance boolean isDeserializing;
- //@// There are some complications surrounding the representations of strings versus
- //@// byte arrays. The serialized form in message headers is always a sequence of
- //@// bytes, but the EncryptedDataKey interface (and some other methods)
- //@// expose the provider ID as if it were a string. Conversions (using UTF-8)
- //@// between byte arrays and strings (which in Java use UTF-16) are not bijections.
- //@// For example, both "\u003f".getBytes() and "\ud800".getBytes() yield a 1-byte
- //@// array containing [0x3f], and calling `new String(..., StandardCharsets.UTF_8)`
- //@// with either the 1-byte array [0x80] or the 3-byte array [0xef,0xbf,0xbd] yields
- //@// the string "\ufffd". Therefore, all we can say about these conversions
- //@// is that a given byte[]-String pair satisfies a conversion relation.
- //@//
- //@// The model functions "ba2s" and "s2ba" are used to specify the conversions
- //@// between byte arrays and strings:
- /*@ public normal_behavior
- @ requires s != null;
- @ ensures \result != null;
- @ function
- @ public model static byte[] s2ba(String s) {
- @ return s.getBytes(StandardCharsets.UTF_8);
- @ }
- @*/
- /*@ public normal_behavior
- @ requires ba != null;
- @ ensures \result != null;
- @ function
- @ public model static String ba2s(byte[] ba) {
- @ return new String(ba, StandardCharsets.UTF_8);
- @ }
- @*/
- //@// The "ba2s" and "s2ba" are given function bodies above, but the verification
- //@// does not rely on these function bodies directly. Instead, the code (in KeyBlob)
- //@// uses "assume" statements when it necessary to connect these functions with
- //@// copies of their bodies that appear in the code. This is a limitation of JML.
- //@//
- //@// One of the properties that holds of "s2ba(s)" is that its result depends not
- //@// on the particular String reference "s" being passed in, but only the contents
- //@// of the string referenced by "s". This property is captured in the following
- //@// lemma:
- /*@ public normal_behavior
- @ requires s != null && t != null && String.equals(s, t);
- @ ensures Arrays.equalArrays(s2ba(s), s2ba(t));
- @ pure
- @ public model static void lemma_s2ba_depends_only_string_contents_only(String s, String t);
- @*/
- //@//
- //@// As a specification convenience, the model function "ba2s2ba" uses the two
- //@// model functions above to convert from a byte array to a String and then back
- //@// to a byte array. As mentioned above, this does not always result in a byte
- //@// array with the original contents. The "assume" statements about the conversion
- //@// functions need to be careful not to assume too much.
- /*@ public normal_behavior
- @ requires ba != null;
- @ ensures \result == s2ba(ba2s(ba));
- @ function
- @ public model static byte[] ba2s2ba(byte[] ba) {
- @ return s2ba(ba2s(ba));
- @ }
- @*/
-
- //@// Here follows 3 methods that access the abstract values of interface properties.
- //@// Something to note about these methods is that each one requires the property
- //@// requested to be known to be non-null. For example, "getProviderId" is only allowed
- //@// to be called when "providerId" is known to be non-null.
-
- //@ public normal_behavior
- //@ requires providerId != null;
- //@ ensures \result != null;
- //@ ensures String.equals(\result, ba2s(providerId));
- //@ pure
- public String getProviderId();
+ // @// An EncryptedDataKey object abstractly contains 3 pieces of data.
+ // @// These are represented by 3 byte arrays:
+
+ // @ model public instance byte[] providerId;
+ // @ model public instance byte[] providerInformation;
+ // @ model public instance byte[] encryptedDataKey;
+
+ // @// The fields of an EncryptedDataKey may be populated via deserialization. The
+ // @// Encryption SDK design allows the deserialization routine to be called repeatedly,
+ // @// each call trying to fill in information that for some reason was not possible
+ // @// with the previous call. In some such "intermediate" states, the deserialization
+ // @// is incomplete in a way that other methods don't expect. Therefore, those methods
+ // @// should not be called in these incomplete intermediate states. The model field
+ // @// isDeserializing is true in those incomplete intermediate states, and it is used
+ // @// in method specifications.
+ // @ public model instance boolean isDeserializing;
+
+ // @// There are some complications surrounding the representations of strings versus
+ // @// byte arrays. The serialized form in message headers is always a sequence of
+ // @// bytes, but the EncryptedDataKey interface (and some other methods)
+ // @// expose the provider ID as if it were a string. Conversions (using UTF-8)
+ // @// between byte arrays and strings (which in Java use UTF-16) are not bijections.
+ // @// For example, both "\u003f".getBytes() and "\ud800".getBytes() yield a 1-byte
+ // @// array containing [0x3f], and calling `new String(..., StandardCharsets.UTF_8)`
+ // @// with either the 1-byte array [0x80] or the 3-byte array [0xef,0xbf,0xbd] yields
+ // @// the string "\ufffd". Therefore, all we can say about these conversions
+ // @// is that a given byte[]-String pair satisfies a conversion relation.
+ // @//
+ // @// The model functions "ba2s" and "s2ba" are used to specify the conversions
+ // @// between byte arrays and strings:
+ /*@ public normal_behavior
+ @ requires s != null;
+ @ ensures \result != null;
+ @ function
+ @ public model static byte[] s2ba(String s) {
+ @ return s.getBytes(StandardCharsets.UTF_8);
+ @ }
+ @*/
+ /*@ public normal_behavior
+ @ requires ba != null;
+ @ ensures \result != null;
+ @ function
+ @ public model static String ba2s(byte[] ba) {
+ @ return new String(ba, StandardCharsets.UTF_8);
+ @ }
+ @*/
+ // @// The "ba2s" and "s2ba" are given function bodies above, but the verification
+ // @// does not rely on these function bodies directly. Instead, the code (in KeyBlob)
+ // @// uses "assume" statements when it necessary to connect these functions with
+ // @// copies of their bodies that appear in the code. This is a limitation of JML.
+ // @//
+ // @// One of the properties that holds of "s2ba(s)" is that its result depends not
+ // @// on the particular String reference "s" being passed in, but only the contents
+ // @// of the string referenced by "s". This property is captured in the following
+ // @// lemma:
+ /*@ public normal_behavior
+ @ requires s != null && t != null && String.equals(s, t);
+ @ ensures Arrays.equalArrays(s2ba(s), s2ba(t));
+ @ pure
+ @ public model static void lemma_s2ba_depends_only_string_contents_only(String s, String t);
+ @*/
+ // @//
+ // @// As a specification convenience, the model function "ba2s2ba" uses the two
+ // @// model functions above to convert from a byte array to a String and then back
+ // @// to a byte array. As mentioned above, this does not always result in a byte
+ // @// array with the original contents. The "assume" statements about the conversion
+ // @// functions need to be careful not to assume too much.
+ /*@ public normal_behavior
+ @ requires ba != null;
+ @ ensures \result == s2ba(ba2s(ba));
+ @ function
+ @ public model static byte[] ba2s2ba(byte[] ba) {
+ @ return s2ba(ba2s(ba));
+ @ }
+ @*/
+
+ // @// Here follows 3 methods that access the abstract values of interface properties.
+ // @// Something to note about these methods is that each one requires the property
+ // @// requested to be known to be non-null. For example, "getProviderId" is only allowed
+ // @// to be called when "providerId" is known to be non-null.
+
+ // @ public normal_behavior
+ // @ requires providerId != null;
+ // @ ensures \result != null;
+ // @ ensures String.equals(\result, ba2s(providerId));
+ // @ pure
+ public String getProviderId();
- //@ public normal_behavior
- //@ requires providerInformation != null;
- //@ ensures \fresh(\result);
- //@ ensures Arrays.equalArrays(providerInformation, \result);
- //@ pure
- public byte[] getProviderInformation();
+ // @ public normal_behavior
+ // @ requires providerInformation != null;
+ // @ ensures \fresh(\result);
+ // @ ensures Arrays.equalArrays(providerInformation, \result);
+ // @ pure
+ public byte[] getProviderInformation();
- //@ public normal_behavior
- //@ requires encryptedDataKey != null;
- //@ ensures \fresh(\result);
- //@ ensures Arrays.equalArrays(encryptedDataKey, \result);
- //@ pure
- public byte[] getEncryptedDataKey();
+ // @ public normal_behavior
+ // @ requires encryptedDataKey != null;
+ // @ ensures \fresh(\result);
+ // @ ensures Arrays.equalArrays(encryptedDataKey, \result);
+ // @ pure
+ public byte[] getEncryptedDataKey();
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java b/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java
index ae64e752e..b61e5640b 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/MasterKey.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -13,120 +13,120 @@
package com.amazonaws.encryptionsdk;
+import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
+import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
-import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
-
/**
* Represents the cryptographic key used to protect the {@link DataKey} (which, in turn, protects
* the data).
*
- * All MasterKeys extend {@link MasterKeyProvider} because they are all capable of providing exactly
- * themselves. This simplifies implementation when only a single {@link MasterKey} is used and/or
- * expected.
+ * All MasterKeys extend {@link MasterKeyProvider} because they are all capable of providing
+ * exactly themselves. This simplifies implementation when only a single {@link MasterKey} is used
+ * and/or expected.
*
- * @param
- * the concrete type of the {@link MasterKey}
+ * @param the concrete type of the {@link MasterKey}
*/
public abstract class MasterKey> extends MasterKeyProvider {
- public abstract String getProviderId();
+ public abstract String getProviderId();
- /**
- * Equivalent to calling {@link #getProviderId()}.
- */
- @Override
- public String getDefaultProviderId() {
- return getProviderId();
- }
+ /** Equivalent to calling {@link #getProviderId()}. */
+ @Override
+ public String getDefaultProviderId() {
+ return getProviderId();
+ }
- public abstract String getKeyId();
+ public abstract String getKeyId();
- /**
- * Generates a new {@link DataKey} which is protected by this {@link MasterKey} for use with
- * {@code algorithm} and associated with the provided {@code encryptionContext}.
- */
- public abstract DataKey generateDataKey(CryptoAlgorithm algorithm, Map encryptionContext);
+ /**
+ * Generates a new {@link DataKey} which is protected by this {@link MasterKey} for use with
+ * {@code algorithm} and associated with the provided {@code encryptionContext}.
+ */
+ public abstract DataKey generateDataKey(
+ CryptoAlgorithm algorithm, Map encryptionContext);
- /**
- * Returns a new copy of the provided {@code dataKey} which is protected by this
- * {@link MasterKey} for use with {@code algorithm} and associated with the provided
- * {@code encryptionContext}.
- */
- public abstract DataKey encryptDataKey(CryptoAlgorithm algorithm, Map encryptionContext,
- DataKey> dataKey);
+ /**
+ * Returns a new copy of the provided {@code dataKey} which is protected by this {@link MasterKey}
+ * for use with {@code algorithm} and associated with the provided {@code encryptionContext}.
+ */
+ public abstract DataKey encryptDataKey(
+ CryptoAlgorithm algorithm, Map encryptionContext, DataKey> dataKey);
- /**
- * Returns {@code true} if and only if {@code provider} equals {@link #getProviderId()}.
- */
- @Override
- public boolean canProvide(final String provider) {
- return getProviderId().equals(provider);
- }
+ /** Returns {@code true} if and only if {@code provider} equals {@link #getProviderId()}. */
+ @Override
+ public boolean canProvide(final String provider) {
+ return getProviderId().equals(provider);
+ }
- /**
- * Returns {@code this} if {@code provider} and {@code keyId} match {@code this}. Otherwise,
- * throws an appropriate exception.
- */
- @SuppressWarnings("unchecked")
- @Override
- public K getMasterKey(final String provider, final String keyId) throws UnsupportedProviderException,
- NoSuchMasterKeyException {
- if (!canProvide(provider)) {
- throw new UnsupportedProviderException("MasterKeys can only provide themselves. Requested "
- + buildName(provider, keyId) + " but only " + toString() + " is available");
- }
- if (!getKeyId().equals(keyId)) {
- throw new NoSuchMasterKeyException("MasterKeys can only provide themselves. Requested "
- + buildName(provider, keyId) + " but only " + toString() + " is available");
- }
- return (K) this;
+ /**
+ * Returns {@code this} if {@code provider} and {@code keyId} match {@code this}. Otherwise,
+ * throws an appropriate exception.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public K getMasterKey(final String provider, final String keyId)
+ throws UnsupportedProviderException, NoSuchMasterKeyException {
+ if (!canProvide(provider)) {
+ throw new UnsupportedProviderException(
+ "MasterKeys can only provide themselves. Requested "
+ + buildName(provider, keyId)
+ + " but only "
+ + toString()
+ + " is available");
}
-
- @Override
- public String toString() {
- return buildName(getProviderId(), getKeyId());
+ if (!getKeyId().equals(keyId)) {
+ throw new NoSuchMasterKeyException(
+ "MasterKeys can only provide themselves. Requested "
+ + buildName(provider, keyId)
+ + " but only "
+ + toString()
+ + " is available");
}
+ return (K) this;
+ }
- /**
- * Returns a list of length {@code 1} containing {@code this}.
- */
- @SuppressWarnings("unchecked")
- @Override
- public List getMasterKeysForEncryption(final MasterKeyRequest request) {
- return (List) Collections.singletonList(this);
- }
+ @Override
+ public String toString() {
+ return buildName(getProviderId(), getKeyId());
+ }
- private static String buildName(final String provider, final String keyId) {
- return String.format("%s://%s", provider, keyId);
- }
+ /** Returns a list of length {@code 1} containing {@code this}. */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List getMasterKeysForEncryption(final MasterKeyRequest request) {
+ return (List) Collections.singletonList(this);
+ }
- /**
- * Two {@link MasterKey}s are equal if they are instances of the exact same class and
- * their values for {@code keyId}, {@code providerId}, and {@code defaultProviderId} are equal.
- */
- @Override
- public boolean equals(final Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (!obj.getClass().equals(getClass())) {
- return false;
- }
- final MasterKey> mk = (MasterKey>) obj;
- return Objects.equals(getKeyId(), mk.getKeyId()) &&
- Objects.equals(getProviderId(), mk.getProviderId()) &&
- Objects.equals(getDefaultProviderId(), mk.getDefaultProviderId());
- }
+ private static String buildName(final String provider, final String keyId) {
+ return String.format("%s://%s", provider, keyId);
+ }
- @Override
- public int hashCode() {
- return Objects.hash(getKeyId(), getProviderId(), getDefaultProviderId());
+ /**
+ * Two {@link MasterKey}s are equal if they are instances of the exact same class and
+ * their values for {@code keyId}, {@code providerId}, and {@code defaultProviderId} are equal.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
}
+ if (!obj.getClass().equals(getClass())) {
+ return false;
+ }
+ final MasterKey> mk = (MasterKey>) obj;
+ return Objects.equals(getKeyId(), mk.getKeyId())
+ && Objects.equals(getProviderId(), mk.getProviderId())
+ && Objects.equals(getDefaultProviderId(), mk.getDefaultProviderId());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getKeyId(), getProviderId(), getDefaultProviderId());
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java
index 978586f2e..a99e8fb67 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyProvider.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -13,101 +13,97 @@
package com.amazonaws.encryptionsdk;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
import com.amazonaws.encryptionsdk.exception.NoSuchMasterKeyException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
/**
* Represents the logic necessary to select and construct {@link MasterKey}s for encrypting and
* decrypting messages. This is an abstract class.
*
- * @param
- * the type of {@link MasterKey} returned by this provider
+ * @param the type of {@link MasterKey} returned by this provider
*/
public abstract class MasterKeyProvider> {
- /**
- * ProviderId used by this instance when no other is specified.
- */
- public abstract String getDefaultProviderId();
+ /** ProviderId used by this instance when no other is specified. */
+ public abstract String getDefaultProviderId();
- /**
- * Returns true if this MasterKeyProvider can provide keys from the specified @{code provider}.
- *
- * @param provider
- * @return
- */
- public boolean canProvide(final String provider) {
- return getDefaultProviderId().equals(provider);
- }
+ /**
+ * Returns true if this MasterKeyProvider can provide keys from the specified @{code provider}.
+ *
+ * @param provider
+ * @return
+ */
+ public boolean canProvide(final String provider) {
+ return getDefaultProviderId().equals(provider);
+ }
- /**
- * Equivalent to calling {@link #getMasterKey(String, String)} using
- * {@link #getDefaultProviderId()} as the provider.
- */
- public K getMasterKey(final String keyId) throws UnsupportedProviderException, NoSuchMasterKeyException {
- return getMasterKey(getDefaultProviderId(), keyId);
- }
+ /**
+ * Equivalent to calling {@link #getMasterKey(String, String)} using {@link
+ * #getDefaultProviderId()} as the provider.
+ */
+ public K getMasterKey(final String keyId)
+ throws UnsupportedProviderException, NoSuchMasterKeyException {
+ return getMasterKey(getDefaultProviderId(), keyId);
+ }
- /**
- * Returns the specified {@link MasterKey} if possible.
- *
- * @param provider
- * @param keyId
- * @return
- * @throws UnsupportedProviderException
- * if this object cannot return {@link MasterKey}s associated with the given
- * provider
- * @throws NoSuchMasterKeyException
- * if this object cannot find (and thus construct) the {@link MasterKey} associated
- * with {@code keyId}
- */
- public abstract K getMasterKey(String provider, String keyId) throws UnsupportedProviderException,
- NoSuchMasterKeyException;
+ /**
+ * Returns the specified {@link MasterKey} if possible.
+ *
+ * @param provider
+ * @param keyId
+ * @return
+ * @throws UnsupportedProviderException if this object cannot return {@link MasterKey}s associated
+ * with the given provider
+ * @throws NoSuchMasterKeyException if this object cannot find (and thus construct) the {@link
+ * MasterKey} associated with {@code keyId}
+ */
+ public abstract K getMasterKey(String provider, String keyId)
+ throws UnsupportedProviderException, NoSuchMasterKeyException;
- /**
- * Returns all {@link MasterKey}s which should be used to protect the plaintext described by
- * {@code request}.
- */
- public abstract List getMasterKeysForEncryption(MasterKeyRequest request);
+ /**
+ * Returns all {@link MasterKey}s which should be used to protect the plaintext described by
+ * {@code request}.
+ */
+ public abstract List getMasterKeysForEncryption(MasterKeyRequest request);
- /**
- * Iterates through {@code encryptedDataKeys} and returns the first one which can be
- * successfully decrypted.
- *
- * @return a DataKey if one can be decrypted, otherwise returns {@code null}
- * @throws UnsupportedProviderException
- * if the {@code encryptedDataKey} is associated with an unsupported provider
- * @throws CannotUnwrapDataKeyException
- * if the {@code encryptedDataKey} cannot be decrypted
- */
- public abstract DataKey decryptDataKey(CryptoAlgorithm algorithm,
- Collection extends EncryptedDataKey> encryptedDataKeys, Map encryptionContext)
- throws UnsupportedProviderException, AwsCryptoException;
+ /**
+ * Iterates through {@code encryptedDataKeys} and returns the first one which can be successfully
+ * decrypted.
+ *
+ * @return a DataKey if one can be decrypted, otherwise returns {@code null}
+ * @throws UnsupportedProviderException if the {@code encryptedDataKey} is associated with an
+ * unsupported provider
+ * @throws CannotUnwrapDataKeyException if the {@code encryptedDataKey} cannot be decrypted
+ */
+ public abstract DataKey decryptDataKey(
+ CryptoAlgorithm algorithm,
+ Collection extends EncryptedDataKey> encryptedDataKeys,
+ Map encryptionContext)
+ throws UnsupportedProviderException, AwsCryptoException;
- protected AwsCryptoException buildCannotDecryptDksException() {
- return buildCannotDecryptDksException(Collections. emptyList());
- }
+ protected AwsCryptoException buildCannotDecryptDksException() {
+ return buildCannotDecryptDksException(Collections.emptyList());
+ }
- protected AwsCryptoException buildCannotDecryptDksException(Throwable t) {
- return buildCannotDecryptDksException(Collections.singletonList(t));
- }
+ protected AwsCryptoException buildCannotDecryptDksException(Throwable t) {
+ return buildCannotDecryptDksException(Collections.singletonList(t));
+ }
- protected AwsCryptoException buildCannotDecryptDksException(List extends Throwable> t) {
- if (t == null || t.isEmpty()) {
- return new CannotUnwrapDataKeyException("Unable to decrypt any data keys");
- } else {
- final CannotUnwrapDataKeyException ex = new CannotUnwrapDataKeyException("Unable to decrypt any data keys",
- t.get(0));
- for (final Throwable e : t) {
- ex.addSuppressed(e);
- }
- return ex;
- }
+ protected AwsCryptoException buildCannotDecryptDksException(List extends Throwable> t) {
+ if (t == null || t.isEmpty()) {
+ return new CannotUnwrapDataKeyException("Unable to decrypt any data keys");
+ } else {
+ final CannotUnwrapDataKeyException ex =
+ new CannotUnwrapDataKeyException("Unable to decrypt any data keys", t.get(0));
+ for (final Throwable e : t) {
+ ex.addSuppressed(e);
+ }
+ return ex;
}
+ }
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java
index 650fd2ee4..1b6350c3c 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/MasterKeyRequest.java
@@ -1,11 +1,11 @@
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
- *
+ *
* http://aws.amazon.com/apache2.0
- *
+ *
* or in the "license" file accompanying this file. This file 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.
@@ -13,120 +13,120 @@
package com.amazonaws.encryptionsdk;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
/**
* Contains information which {@link MasterKeyProvider}s can use to select which {@link MasterKey}s
* to use to protect a given plaintext. This class is immutable.
*/
public final class MasterKeyRequest {
- private final Map encryptionContext_;
- private final boolean isStreaming_;
- private final byte[] plaintext_;
- private final long size_;
-
- private MasterKeyRequest(final Map encryptionContext, final boolean isStreaming,
- final byte[] plaintext, final long size) {
- encryptionContext_ = encryptionContext;
- isStreaming_ = isStreaming;
- plaintext_ = plaintext;
- size_ = size;
- }
+ private final Map encryptionContext_;
+ private final boolean isStreaming_;
+ private final byte[] plaintext_;
+ private final long size_;
+
+ private MasterKeyRequest(
+ final Map encryptionContext,
+ final boolean isStreaming,
+ final byte[] plaintext,
+ final long size) {
+ encryptionContext_ = encryptionContext;
+ isStreaming_ = isStreaming;
+ plaintext_ = plaintext;
+ size_ = size;
+ }
+
+ public Map