diff --git a/pom.xml b/pom.xml index 2a233a92..240ded06 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,6 @@ ddej-build-tools common sdk1 - sdk2 examples diff --git a/sdk2/pom.xml b/sdk2/pom.xml deleted file mode 100644 index 3f70223b..00000000 --- a/sdk2/pom.xml +++ /dev/null @@ -1,330 +0,0 @@ - - - 4.0.0 - - software.amazon.cryptools - dynamodbencryptionclient-sdk2 - jar - 0.1.0-SNAPSHOT - aws-dynamodb-encryption-java :: SDK2 - AWS DynamoDB Encryption Client for AWS Java SDK v2 - https://github.com/aws/aws-dynamodb-encryption-java - - - https://github.com/aws/aws-dynamodb-encryption-java.git - scm:git:git@github.com:aws/aws-dynamodb-encryption-java.git - scm:git:git@github.com:aws/aws-dynamodb-encryption-java.git - - - - - - software.amazon.awssdk - bom - 2.5.47 - pom - import - - - - - - - software.amazon.awssdk - dynamodb - - - software.amazon.awssdk - kms - - - - software.amazon.cryptools - dynamodbencryptionclient-common - 0.1.0-SNAPSHOT - - - - org.mockito - mockito-junit-jupiter - 2.27.0 - test - - - - org.testng - testng - 6.10 - test - - - - org.quicktheories - quicktheories - 0.25 - test - - - - org.hamcrest - hamcrest-all - 1.3 - test - - - - org.bouncycastle - bcprov-ext-jdk15on - 1.60 - test - - - - com.amazonaws - DynamoDBLocal - 1.10.5.1 - test - - - - com.almworks.sqlite4java - sqlite4java - ${sqlite4java.version} - test - - - - com.almworks.sqlite4java - libsqlite4java-osx - ${sqlite4java.version} - dylib - test - - - - com.almworks.sqlite4java - sqlite4java-win32-x64 - ${sqlite4java.version} - dll - test - - - - com.almworks.sqlite4java - libsqlite4java-linux-amd64 - so - ${sqlite4java.version} - test - - - - - - Apache License, Version 2.0 - https://aws.amazon.com/apache2.0 - repo - - - - - UTF-8 - 1.0.392 - 8.29 - 3.1.0 - 0.1.0 - 0.8.3 - 3.0.1 - 3.0.0 - 3.0.0-M3 - 3.0.0-M3 - 3.1.1 - - - - - amazonwebservices - Amazon Web Services - https://aws.amazon.com - - developer - - - - - - - - dynamodb-local - DynamoDB Local Release Repository - https://s3-us-west-2.amazonaws.com/dynamodb-local/release - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.0 - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.0.1 - - *.internal:*.transform - 128m - 1024m - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.1.1 - - - copy - test-compile - - copy-dependencies - - - test - so,dll,dylib - ${project.build.directory}/test-lib - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - false - - **/Test*.java - **/*Test.java - **/*TestCase.java - **/*Tests.java - **/*TestCases.java - - - - sqlite4java.library.path - ${project.build.directory}/test-lib - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${maven-checkstyle-plugin.version} - - - com.puppycrawl.tools - checkstyle - ${checkstyle.version} - - - software.amazon.cryptools - ddej-build-tools - ${ddej-build-tools.version} - - - - - checkstyle - validate - - check - - - - - software/amazon/cryptools/ddej-build-tools/checkstyle/checkstyle.xml - software/amazon/cryptools/ddej-build-tools/checkstyle/checkstyle-suppressions.xml - true - true - true - true - - - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - false - - - sqlite4java.library.path - ${project.build.directory}/test-lib - - - - **/*ITCase.java - - - - - - integration-test - verify - - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco-maven-plugin.version} - - - - prepare-agent - - - - report - test - - report - - - - - - - - - - - org.apache.maven.plugins - maven-jxr-plugin - ${maven-jxr-plugin.version} - - - - - diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/BasicDynamoDbEncryptionConfiguration.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/BasicDynamoDbEncryptionConfiguration.java deleted file mode 100644 index 103b9c7a..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/BasicDynamoDbEncryptionConfiguration.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; - -public class BasicDynamoDbEncryptionConfiguration implements DynamoDbEncryptionConfiguration { - private final EncryptionAction defaultEncryptionAction; - private final Map encryptionActionOverrides; - private final EncryptionContext encryptionContext; - - private BasicDynamoDbEncryptionConfiguration(Builder builder) { - this.defaultEncryptionAction = builder.defaultEncryptionAction; - this.encryptionActionOverrides = Collections.unmodifiableMap(builder.encryptionActionOverrides); - this.encryptionContext = builder.encryptionContext; - } - - @Override - public EncryptionAction getDefaultEncryptionAction() { - return this.defaultEncryptionAction; - } - - @Override - public Map getEncryptionActionOverrides() { - return this.encryptionActionOverrides; - } - - @Override - public EncryptionContext getEncryptionContext() { - return this.encryptionContext; - } - - /** - * Builder for an immutable implementation of {@link DynamoDbEncryptionConfiguration}. - */ - public static class Builder { - private EncryptionAction defaultEncryptionAction; - private Map encryptionActionOverrides = new HashMap<>(); - private EncryptionContext encryptionContext; - - /** - * Set the default {@link EncryptionAction} that should be applied to any attribute that is found in the - * record and does not have a specific override associated with it. - * @param defaultEncryptionAction The default encryption action that should be applied to attributes. - * @return a mutated instance of this builder. - */ - public Builder defaultEncryptionAction(EncryptionAction defaultEncryptionAction) { - this.defaultEncryptionAction = defaultEncryptionAction; - return this; - } - - /** - * Add a map of encryption action overrides for specific attributes. Will be merged into any existing overrides - * the builder already has and will overwrite existing values with the same key. - * @param encryptionActionOverrides A map of encryption action overrides. - * @return a mutated instance of this builder. - */ - public Builder addEncryptionActionOverrides(Map encryptionActionOverrides) { - this.encryptionActionOverrides.putAll(encryptionActionOverrides); - return this; - } - - /** - * Add a single encryption action override for a specific attribute. Will be merged into any existing overrides - * ths builder already has and will overwrite existing values with the same key. - * @param attributeKey The name of the attribute. - * @param encryptionAction The encryption action to apply to that attribute. - * @return a mutated instance of this builder. - */ - public Builder addEncryptionActionOverride(String attributeKey, EncryptionAction encryptionAction) { - this.encryptionActionOverrides.put(attributeKey, encryptionAction); - return this; - } - - /** - * Sets the encryption context to be used by the encryption client when encrypting or decrypting records. At - * a minimum the following fields should be set on the context: tableName, hashKeyName, rangeKeyName. - * @param encryptionContext An {@link EncryptionContext} object to associate with this configuration. - * @return a mutated instance of this builder. - */ - public Builder encryptionContext(EncryptionContext encryptionContext) { - this.encryptionContext = encryptionContext; - return this; - } - - /** - * Construct an immutable {@link DynamoDbEncryptionConfiguration} from the information provided to the builder. - * @return an initialized {@link BasicDynamoDbEncryptionConfiguration} object. - */ - public BasicDynamoDbEncryptionConfiguration build() { - return new BasicDynamoDbEncryptionConfiguration(this); - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/DynamoDbEncryptionClient.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/DynamoDbEncryptionClient.java deleted file mode 100644 index 9825d18f..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/DynamoDbEncryptionClient.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2; - -import java.util.Map; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.DynamoDbEncryptor; - -/** - * General interface for a class that is capable of encrypting and decrypting DynamoDB records as well as signing and - * verifying signatures. - */ -public interface DynamoDbEncryptionClient { - /** - * Encrypt and sign a record. - * @param itemAttributes The map of AttributeValues that make up the record. - * @param configuration A {@link DynamoDbEncryptionConfiguration} object that configures the behavior and scope - * of encryption and signing on the record. - * @return A map of AttributeValues that has been encrypted and signed as directed. - */ - Map encryptRecord(Map itemAttributes, - DynamoDbEncryptionConfiguration configuration); - - /** - * Decrypt and verify signature on a record. - * @param itemAttributes The map of AttributeValues that make up the encrypted/signed record. - * @param configuration A {@link DynamoDbEncryptionConfiguration} object that configures the behavior and scope - * of decryption and signature verification on the record. - * @return A map of AttributeValues that have been decrypted and verified as directed. - */ - Map decryptRecord(Map itemAttributes, - DynamoDbEncryptionConfiguration configuration); - - /** - * Convenience method to return a builder for the default approved implementation of this interface, a - * {@link DynamoDbEncryptor}. - * @return A builder object for the default implementation of this interface. - */ - static DynamoDbEncryptor.Builder builder() { - return DynamoDbEncryptor.builder(); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/DynamoDbEncryptionConfiguration.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/DynamoDbEncryptionConfiguration.java deleted file mode 100644 index 3c2ca113..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/DynamoDbEncryptionConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2; - -import java.util.Map; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; - -/** - * An interface to an object that supplies configuration and context to the {@link DynamoDbEncryptionClient}. - */ -public interface DynamoDbEncryptionConfiguration { - /** - * Get the default {@link EncryptionAction} that should be applied to any attribute that is found in the record and - * does not have a specific override associated with it. - * @return The default {@link EncryptionAction}. - */ - EncryptionAction getDefaultEncryptionAction(); - - /** - * Gets a map of specific attribute {@link EncryptionAction} overrides. - * @return A map of {@link EncryptionAction} overrides, keyed by attribute name. - */ - Map getEncryptionActionOverrides(); - - /** - * Returns an {@link EncryptionContext} to be used by the encryption client. Has information about the table - * name, the names of the primary indices etc. - * @return An {@link EncryptionContext} object. - */ - EncryptionContext getEncryptionContext(); - - /** - * Default builder for an immutable implementation of {@link DynamoDbEncryptionConfiguration}. - * @return A newly initialized {@link BasicDynamoDbEncryptionConfiguration.Builder}. - */ - static BasicDynamoDbEncryptionConfiguration.Builder builder() { - return new BasicDynamoDbEncryptionConfiguration.Builder(); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/EncryptionAction.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/EncryptionAction.java deleted file mode 100644 index 9ee1c798..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/EncryptionAction.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2; - -/** - * When configuring the {@link DynamoDbEncryptionClient} you may specify a default behavior for how attributes should - * be treated when encrypting and decrypting, and also you may include overrides to change the behavior for specific - * attributes. The following enumeration are the different valid behaviors for how a single attribute should be treated. - */ -public enum EncryptionAction { - /** - * DO_NOTHING : This instructs the encryption client to completely ignore the attribute. The attribute will not be - * encrypted and it will not be included in the signature calculation of the record. - */ - DO_NOTHING, - - /** - * SIGN_ONLY : This instructs the encryption client to include the attribute in the signature calculation of the - * record, but not to encrypt its value. - */ - SIGN_ONLY, - - /** - * ENCRYPT_AND_SIGN : This instructs the encryption client to include the attribute in the signature calculation of - * the record and to encrypt its value. - */ - ENCRYPT_AND_SIGN -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedKey.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedKey.java deleted file mode 100644 index 52e02f2e..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedKey.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; - -/** - * Identifies keys which should not be used directly with {@link Cipher} but - * instead contain their own cryptographic logic. This can be used to wrap more - * complex logic, HSM integration, or service-calls. - * - *

- * Most delegated keys will only support a subset of these operations. (For - * example, AES keys will generally not support {@link #sign(byte[], String)} or - * {@link #verify(byte[], byte[], String)} and HMAC keys will generally not - * support anything except sign and verify.) - * {@link UnsupportedOperationException} should be thrown in these cases. - * - * @author Greg Rubin - */ -public interface DelegatedKey extends SecretKey { - /** - * Encrypts the provided plaintext and returns a byte-array containing the ciphertext. - * - * @param plainText - * @param additionalAssociatedData - * Optional additional data which must then also be provided for successful - * decryption. Both null and arrays of length 0 are treated identically. - * Not all keys will support this parameter. - * @param algorithm - * the transformation to be used when encrypting the data - * @return ciphertext the ciphertext produced by this encryption operation - * @throws UnsupportedOperationException - * if encryption is not supported or if additionalAssociatedData is - * provided, but not supported. - */ - byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException; - - /** - * Decrypts the provided ciphertext and returns a byte-array containing the - * plaintext. - * - * @param cipherText - * @param additionalAssociatedData - * Optional additional data which was provided during encryption. - * Both null and arrays of length 0 are treated - * identically. Not all keys will support this parameter. - * @param algorithm - * the transformation to be used when decrypting the data - * @return plaintext the result of decrypting the input ciphertext - * @throws UnsupportedOperationException - * if decryption is not supported or if - * additionalAssociatedData is provided, but not - * supported. - */ - byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidAlgorithmParameterException; - - /** - * Wraps (encrypts) the provided key to make it safe for - * storage or transmission. - * - * @param key - * @param additionalAssociatedData - * Optional additional data which must then also be provided for - * successful unwrapping. Both null and arrays of - * length 0 are treated identically. Not all keys will support - * this parameter. - * @param algorithm - * the transformation to be used when wrapping the key - * @return the wrapped key - * @throws UnsupportedOperationException - * if wrapping is not supported or if - * additionalAssociatedData is provided, but not - * supported. - */ - byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) throws InvalidKeyException, - NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException; - - /** - * Unwraps (decrypts) the provided wrappedKey to recover the - * original key. - * - * @param wrappedKey - * @param additionalAssociatedData - * Optional additional data which was provided during wrapping. - * Both null and arrays of length 0 are treated - * identically. Not all keys will support this parameter. - * @param algorithm - * the transformation to be used when unwrapping the key - * @return the unwrapped key - * @throws UnsupportedOperationException - * if wrapping is not supported or if - * additionalAssociatedData is provided, but not - * supported. - */ - Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType, - byte[] additionalAssociatedData, String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException; - - /** - * Calculates and returns a signature for dataToSign. - * - * @param dataToSign - * @param algorithm - * @return the signature - * @throws UnsupportedOperationException if signing is not supported - */ - byte[] sign(byte[] dataToSign, String algorithm) throws GeneralSecurityException; - - /** - * Checks the provided signature for correctness. - * - * @param dataToSign - * @param signature - * @param algorithm - * @return true if and only if the signature matches the dataToSign. - * @throws UnsupportedOperationException if signature validation is not supported - */ - boolean verify(byte[] dataToSign, byte[] signature, String algorithm); -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbEncryptor.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbEncryptor.java deleted file mode 100644 index 775fd12d..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbEncryptor.java +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.security.GeneralSecurityException; -import java.security.PrivateKey; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.DynamoDbEncryptionClient; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.DynamoDbEncryptionConfiguration; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.EncryptionAction; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.AttributeValueMarshaller; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.ByteBufferInputStream; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -/** - * The low-level API for performing crypto operations on the record attributes. - * - * @author Greg Rubin - */ -public class DynamoDbEncryptor implements DynamoDbEncryptionClient { - private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA"; - private static final String DEFAULT_METADATA_FIELD = "*amzn-ddb-map-desc*"; - private static final String DEFAULT_SIGNATURE_FIELD = "*amzn-ddb-map-sig*"; - private static final String DEFAULT_DESCRIPTION_BASE = "amzn-ddb-map-"; // Same as the Mapper - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final String SYMMETRIC_ENCRYPTION_MODE = "/CBC/PKCS5Padding"; - private static final ConcurrentHashMap BLOCK_SIZE_CACHE = new ConcurrentHashMap<>(); - private static final Function BLOCK_SIZE_CALCULATOR = (transformation) -> { - try { - final Cipher c = Cipher.getInstance(transformation); - return c.getBlockSize(); - } catch (final GeneralSecurityException ex) { - throw new IllegalArgumentException("Algorithm does not exist", ex); - } - }; - - private static final int CURRENT_VERSION = 0; - - // Static map used to convert an EncryptionAction into a corresponding set of EncryptionFlags - private static final Map> ENCRYPTION_ACTION_TO_FLAGS_MAP; - static { - Map> encrytionActionToFlagsMap = new HashMap<>(); - encrytionActionToFlagsMap.put(EncryptionAction.DO_NOTHING, Collections.emptySet()); - encrytionActionToFlagsMap.put(EncryptionAction.SIGN_ONLY, Collections.singleton(EncryptionFlags.SIGN)); - encrytionActionToFlagsMap.put(EncryptionAction.ENCRYPT_AND_SIGN, - Collections.unmodifiableSet(new HashSet<>(Arrays.asList(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)))); - ENCRYPTION_ACTION_TO_FLAGS_MAP = Collections.unmodifiableMap(encrytionActionToFlagsMap); - } - - private String signatureFieldName = DEFAULT_SIGNATURE_FIELD; - private String materialDescriptionFieldName = DEFAULT_METADATA_FIELD; - - private EncryptionMaterialsProvider encryptionMaterialsProvider; - private final String descriptionBase; - private final String symmetricEncryptionModeHeader; - private final String signingAlgorithmHeader; - - static final String DEFAULT_SIGNING_ALGORITHM_HEADER = DEFAULT_DESCRIPTION_BASE + "signingAlg"; - - private Function encryptionContextOverrideOperator; - - protected DynamoDbEncryptor(EncryptionMaterialsProvider provider, String descriptionBase) { - this.encryptionMaterialsProvider = provider; - this.descriptionBase = descriptionBase; - symmetricEncryptionModeHeader = this.descriptionBase + "sym-mode"; - signingAlgorithmHeader = this.descriptionBase + "signingAlg"; - } - - protected DynamoDbEncryptor(EncryptionMaterialsProvider provider) { - this(provider, DEFAULT_DESCRIPTION_BASE); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private EncryptionMaterialsProvider encryptionMaterialsProvider; - - public Builder encryptionMaterialsProvider(EncryptionMaterialsProvider encryptionMaterialsProvider) { - this.encryptionMaterialsProvider = encryptionMaterialsProvider; - return this; - } - - public DynamoDbEncryptor build() { - if (encryptionMaterialsProvider == null) { - throw new IllegalArgumentException("A DynamoDbEncryptor cannot be built without an " - + "EncryptionMaterialsProvider"); - } - - return new DynamoDbEncryptor(encryptionMaterialsProvider); - } - } - - @Override - public Map encryptRecord(Map record, - DynamoDbEncryptionConfiguration configuration) { - - validateParameters(record, configuration); - return internalEncryptRecord(record, - getEncryptionFlagsFromConfiguration(record, configuration), - configuration.getEncryptionContext()); - } - - @Override - public Map decryptRecord(Map record, - DynamoDbEncryptionConfiguration configuration) { - - validateParameters(record, configuration); - return internalDecryptRecord(record, - getEncryptionFlagsFromConfiguration(record, configuration), - configuration.getEncryptionContext()); - } - - private void validateParameters(Map record, - DynamoDbEncryptionConfiguration configuration) { - if (record == null) { - throw new IllegalArgumentException("AttributeValues must not be null"); - } - if (configuration == null) { - throw new IllegalArgumentException("DynamoDbEncryptionConfiguration must not be null"); - } - if (configuration.getEncryptionContext() == null) { - throw new IllegalArgumentException("DynamoDbEncryptionConfiguration's EncryptionContext must not be null"); - } - if (configuration.getDefaultEncryptionAction() == null) { - throw new IllegalArgumentException("DynamoDbEncryptionConfiguration's DefaultEncryptionAction must not be" - + " null"); - } - } - - - private Map> getEncryptionFlagsFromConfiguration( - Map record, - DynamoDbEncryptionConfiguration configuration) { - - return record.keySet() - .stream() - // Do not let attributes created by the encryption library participate in encrypting or signing - .filter(key -> !key.equals(getMaterialDescriptionFieldName()) - && !key.equals(getSignatureFieldName())) - .collect(Collectors.toMap(Function.identity(), key -> { - EncryptionAction encryptionAction = configuration.getEncryptionActionOverrides().get(key); - - if (encryptionAction == null) { - encryptionAction = configuration.getDefaultEncryptionAction(); - } - - return getEncryptionFlagsForAction(encryptionAction); - })); - } - - private Map internalDecryptRecord( - Map itemAttributes, - Map> attributeFlags, - EncryptionContext context) { - if (attributeFlags.isEmpty()) { - return itemAttributes; - } - // Copy to avoid changing anyone elses objects - itemAttributes = new HashMap<>(itemAttributes); - - Map materialDescription = Collections.emptyMap(); - DecryptionMaterials materials; - SecretKey decryptionKey; - - DynamoDbSigner signer = DynamoDbSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng()); - - if (itemAttributes.containsKey(materialDescriptionFieldName)) { - materialDescription = unmarshallDescription(itemAttributes.get(materialDescriptionFieldName)); - } - // Copy the material description and attribute values into the context - context = context.toBuilder() - .materialDescription(materialDescription) - .attributeValues(itemAttributes) - .build(); - - Function encryptionContextOverrideOperator = getEncryptionContextOverrideOperator(); - if (encryptionContextOverrideOperator != null) { - context = encryptionContextOverrideOperator.apply(context); - } - - materials = encryptionMaterialsProvider.getDecryptionMaterials(context); - decryptionKey = materials.getDecryptionKey(); - if (materialDescription.containsKey(signingAlgorithmHeader)) { - String signingAlg = materialDescription.get(signingAlgorithmHeader); - signer = DynamoDbSigner.getInstance(signingAlg, Utils.getRng()); - } - - ByteBuffer signature; - if (!itemAttributes.containsKey(signatureFieldName) || itemAttributes.get(signatureFieldName).b() == null) { - signature = ByteBuffer.allocate(0); - } else { - signature = itemAttributes.get(signatureFieldName).b().asByteBuffer(); - } - itemAttributes.remove(signatureFieldName); - - String associatedData = "TABLE>" + context.getTableName() + " internalEncryptRecord( - Map itemAttributes, - Map> attributeFlags, - EncryptionContext context) { - if (attributeFlags.isEmpty()) { - return itemAttributes; - } - // Copy to avoid changing anyone elses objects - itemAttributes = new HashMap<>(itemAttributes); - - // Copy the attribute values into the context - context = context.toBuilder() - .attributeValues(itemAttributes) - .build(); - - Function encryptionContextOverrideOperator = - getEncryptionContextOverrideOperator(); - if (encryptionContextOverrideOperator != null) { - context = encryptionContextOverrideOperator.apply(context); - } - - EncryptionMaterials materials = encryptionMaterialsProvider.getEncryptionMaterials(context); - // We need to copy this because we modify it to record other encryption details - Map materialDescription = new HashMap<>( - materials.getMaterialDescription()); - SecretKey encryptionKey = materials.getEncryptionKey(); - - try { - actualEncryption(itemAttributes, attributeFlags, materialDescription, encryptionKey); - - // The description must be stored after encryption because its data - // is necessary for proper decryption. - final String signingAlgo = materialDescription.get(signingAlgorithmHeader); - DynamoDbSigner signer; - if (signingAlgo != null) { - signer = DynamoDbSigner.getInstance(signingAlgo, Utils.getRng()); - } else { - signer = DynamoDbSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, Utils.getRng()); - } - - if (materials.getSigningKey() instanceof PrivateKey) { - materialDescription.put(signingAlgorithmHeader, signer.getSigningAlgorithm()); - } - if (! materialDescription.isEmpty()) { - itemAttributes.put(materialDescriptionFieldName, marshallDescription(materialDescription)); - } - - String associatedData = "TABLE>" + context.getTableName() + " itemAttributes, - Map> attributeFlags, SecretKey encryptionKey, - Map materialDescription) throws GeneralSecurityException { - final String encryptionMode = encryptionKey != null ? encryptionKey.getAlgorithm() + - materialDescription.get(symmetricEncryptionModeHeader) : null; - Cipher cipher = null; - int blockSize = -1; - - for (Map.Entry entry: itemAttributes.entrySet()) { - Set flags = attributeFlags.get(entry.getKey()); - if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) { - if (!flags.contains(EncryptionFlags.SIGN)) { - throw new IllegalArgumentException("All encrypted fields must be signed. Bad field: " + entry.getKey()); - } - ByteBuffer plainText; - ByteBuffer cipherText = entry.getValue().b().asByteBuffer(); - cipherText.rewind(); - if (encryptionKey instanceof DelegatedKey) { - plainText = ByteBuffer.wrap(((DelegatedKey)encryptionKey).decrypt(toByteArray(cipherText), null, encryptionMode)); - } else { - if (cipher == null) { - blockSize = getBlockSize(encryptionMode); - cipher = Cipher.getInstance(encryptionMode); - } - byte[] iv = new byte[blockSize]; - cipherText.get(iv); - cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv), Utils.getRng()); - plainText = ByteBuffer.allocate(cipher.getOutputSize(cipherText.remaining())); - cipher.doFinal(cipherText, plainText); - plainText.rewind(); - } - entry.setValue(AttributeValueMarshaller.unmarshall(plainText)); - } - } - } - - private static int getBlockSize(final String encryptionMode) { - return BLOCK_SIZE_CACHE.computeIfAbsent(encryptionMode, BLOCK_SIZE_CALCULATOR); - } - - /** - * This method has the side effect of replacing the plaintext - * attribute-values of "itemAttributes" with ciphertext attribute-values - * (which are always in the form of ByteBuffer) as per the corresponding - * attribute flags. - */ - private void actualEncryption(Map itemAttributes, - Map> attributeFlags, - Map materialDescription, - SecretKey encryptionKey) throws GeneralSecurityException { - String encryptionMode = null; - if (encryptionKey != null) { - materialDescription.put(this.symmetricEncryptionModeHeader, - SYMMETRIC_ENCRYPTION_MODE); - encryptionMode = encryptionKey.getAlgorithm() + SYMMETRIC_ENCRYPTION_MODE; - } - Cipher cipher = null; - int blockSize = -1; - - for (Map.Entry entry: itemAttributes.entrySet()) { - Set flags = attributeFlags.get(entry.getKey()); - if (flags != null && flags.contains(EncryptionFlags.ENCRYPT)) { - if (!flags.contains(EncryptionFlags.SIGN)) { - throw new IllegalArgumentException("All encrypted fields must be signed. Bad field: " + entry.getKey()); - } - ByteBuffer plainText = AttributeValueMarshaller.marshall(entry.getValue()); - plainText.rewind(); - ByteBuffer cipherText; - if (encryptionKey instanceof DelegatedKey) { - DelegatedKey dk = (DelegatedKey) encryptionKey; - cipherText = ByteBuffer.wrap( - dk.encrypt(toByteArray(plainText), null, encryptionMode)); - } else { - if (cipher == null) { - blockSize = getBlockSize(encryptionMode); - cipher = Cipher.getInstance(encryptionMode); - } - // Encryption format: - // Note a unique iv is generated per attribute - cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, Utils.getRng()); - cipherText = ByteBuffer.allocate(blockSize + cipher.getOutputSize(plainText.remaining())); - cipherText.position(blockSize); - cipher.doFinal(plainText, cipherText); - cipherText.flip(); - final byte[] iv = cipher.getIV(); - if (iv.length != blockSize) { - throw new IllegalStateException(String.format("Generated IV length (%d) not equal to block size (%d)", - iv.length, blockSize)); - } - cipherText.put(iv); - cipherText.rewind(); - } - // Replace the plaintext attribute value with the encrypted content - entry.setValue(AttributeValue.builder().b(SdkBytes.fromByteBuffer(cipherText)).build()); - } - } - } - - /** - * Get the name of the DynamoDB field used to store the signature. - * Defaults to {@link #DEFAULT_SIGNATURE_FIELD}. - * - * @return the name of the DynamoDB field used to store the signature - */ - String getSignatureFieldName() { - return signatureFieldName; - } - - /** - * Set the name of the DynamoDB field used to store the signature. - * - * @param signatureFieldName - */ - void setSignatureFieldName(final String signatureFieldName) { - this.signatureFieldName = signatureFieldName; - } - - /** - * Get the name of the DynamoDB field used to store metadata used by the - * DynamoDBEncryptedMapper. Defaults to {@link #DEFAULT_METADATA_FIELD}. - * - * @return the name of the DynamoDB field used to store metadata used by the - * DynamoDBEncryptedMapper - */ - String getMaterialDescriptionFieldName() { - return materialDescriptionFieldName; - } - - /** - * Set the name of the DynamoDB field used to store metadata used by the - * DynamoDBEncryptedMapper - * - * @param materialDescriptionFieldName - */ - void setMaterialDescriptionFieldName(final String materialDescriptionFieldName) { - this.materialDescriptionFieldName = materialDescriptionFieldName; - } - - /** - * Marshalls the description into a ByteBuffer by outputting - * each key (modified UTF-8) followed by its value (also in modified UTF-8). - * - * @param description - * @return the description encoded as an AttributeValue with a ByteBuffer value - * @see java.io.DataOutput#writeUTF(String) - */ - private static AttributeValue marshallDescription(Map description) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(bos); - out.writeInt(CURRENT_VERSION); - for (Map.Entry entry : description.entrySet()) { - byte[] bytes = entry.getKey().getBytes(UTF8); - out.writeInt(bytes.length); - out.write(bytes); - bytes = entry.getValue().getBytes(UTF8); - out.writeInt(bytes.length); - out.write(bytes); - } - out.close(); - return AttributeValue.builder().b(SdkBytes.fromByteArray(bos.toByteArray())).build(); - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } - } - - /** - * @see #marshallDescription(Map) - */ - private static Map unmarshallDescription(AttributeValue attributeValue) { - try (DataInputStream in = new DataInputStream( - new ByteBufferInputStream(attributeValue.b().asByteBuffer())) ) { - Map result = new HashMap<>(); - int version = in.readInt(); - if (version != CURRENT_VERSION) { - throw new IllegalArgumentException("Unsupported description version"); - } - - String key, value; - int keyLength, valueLength; - try { - while(in.available() > 0) { - keyLength = in.readInt(); - byte[] bytes = new byte[keyLength]; - if (in.read(bytes) != keyLength) { - throw new IllegalArgumentException("Malformed description"); - } - key = new String(bytes, UTF8); - valueLength = in.readInt(); - bytes = new byte[valueLength]; - if (in.read(bytes) != valueLength) { - throw new IllegalArgumentException("Malformed description"); - } - value = new String(bytes, UTF8); - result.put(key, value); - } - } catch (EOFException eof) { - throw new IllegalArgumentException("Malformed description", eof); - } - return result; - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } - } - - /** - * @param encryptionContextOverrideOperator the nullable operator which will be used to override - * the EncryptionContext. - * @see EncryptionContextOperators - */ - void setEncryptionContextOverrideOperator( - Function encryptionContextOverrideOperator) { - this.encryptionContextOverrideOperator = encryptionContextOverrideOperator; - } - - /** - * @return the operator used to override the EncryptionContext - * @see #setEncryptionContextOverrideOperator(Function) - */ - private Function getEncryptionContextOverrideOperator() { - return encryptionContextOverrideOperator; - } - - private static Set getEncryptionFlagsForAction(EncryptionAction encryptionAction) { - Set encryptionFlags = ENCRYPTION_ACTION_TO_FLAGS_MAP.get(encryptionAction); - - if (encryptionFlags == null) { - throw new RuntimeException("Unrecognized EncryptionAction : " + encryptionAction.name()); - } - - return encryptionFlags; - } - - private static byte[] toByteArray(ByteBuffer buffer) { - buffer = buffer.duplicate(); - // We can only return the array directly if: - // 1. The ByteBuffer exposes an array - // 2. The ByteBuffer starts at the beginning of the array - // 3. The ByteBuffer uses the entire array - if (buffer.hasArray() && buffer.arrayOffset() == 0) { - byte[] result = buffer.array(); - if (buffer.remaining() == result.length) { - return result; - } - } - - byte[] result = new byte[buffer.remaining()]; - buffer.get(result); - return result; - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbSigner.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbSigner.java deleted file mode 100644 index e27710c3..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbSigner.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.security.GeneralSecurityException; -import java.security.Key; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.Signature; -import java.security.SignatureException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.AttributeValueMarshaller; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * @author Greg Rubin - */ -// NOTE: This class must remain thread-safe. -class DynamoDbSigner { - private static final ConcurrentHashMap cache = - new ConcurrentHashMap<>(); - - protected static final Charset UTF8 = Charset.forName("UTF-8"); - private final SecureRandom rnd; - private final SecretKey hmacComparisonKey; - private final String signingAlgorithm; - - /** - * @param signingAlgorithm - * is the algorithm used for asymmetric signing (ex: - * SHA256withRSA). This is ignored for symmetric HMACs as that - * algorithm is fully specified by the key. - */ - static DynamoDbSigner getInstance(String signingAlgorithm, SecureRandom rnd) { - DynamoDbSigner result = cache.get(signingAlgorithm); - if (result == null) { - result = new DynamoDbSigner(signingAlgorithm, rnd); - cache.putIfAbsent(signingAlgorithm, result); - } - return result; - } - - /** - * @param signingAlgorithm - * is the algorithm used for asymmetric signing (ex: - * SHA256withRSA). This is ignored for symmetric HMACs as that - * algorithm is fully specified by the key. - */ - private DynamoDbSigner(String signingAlgorithm, SecureRandom rnd) { - if (rnd == null) { - rnd = Utils.getRng(); - } - this.rnd = rnd; - this.signingAlgorithm = signingAlgorithm; - // Shorter than the output of SHA256 to avoid weak keys. - // http://cs.nyu.edu/~dodis/ps/h-of-h.pdf - // http://link.springer.com/chapter/10.1007%2F978-3-642-32009-5_21 - byte[] tmpKey = new byte[31]; - rnd.nextBytes(tmpKey); - hmacComparisonKey = new SecretKeySpec(tmpKey, "HmacSHA256"); - } - - void verifySignature(Map itemAttributes, Map> attributeFlags, - byte[] associatedData, Key verificationKey, ByteBuffer signature) throws GeneralSecurityException { - if (verificationKey instanceof DelegatedKey) { - DelegatedKey dKey = (DelegatedKey)verificationKey; - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - if (!dKey.verify(stringToSign, toByteArray(signature), dKey.getAlgorithm())) { - throw new SignatureException("Bad signature"); - } - } else if (verificationKey instanceof SecretKey) { - byte[] calculatedSig = calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey)verificationKey); - if (!safeEquals(signature, calculatedSig)) { - throw new SignatureException("Bad signature"); - } - } else if (verificationKey instanceof PublicKey) { - PublicKey integrityKey = (PublicKey)verificationKey; - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - Signature sig = Signature.getInstance(getSigningAlgorithm()); - sig.initVerify(integrityKey); - sig.update(stringToSign); - if (!sig.verify(toByteArray(signature))) { - throw new SignatureException("Bad signature"); - } - } else { - throw new IllegalArgumentException("No integrity key provided"); - } - } - - static byte[] calculateStringToSign(Map itemAttributes, - Map> attributeFlags, byte[] associatedData) - throws NoSuchAlgorithmException { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - List attrNames = new ArrayList<>(itemAttributes.keySet()); - Collections.sort(attrNames); - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - if (associatedData != null) { - out.write(sha256.digest(associatedData)); - } else { - out.write(sha256.digest()); - } - sha256.reset(); - - for (String name : attrNames) { - Set set = attributeFlags.get(name); - if(set != null && set.contains(EncryptionFlags.SIGN)) { - AttributeValue tmp = itemAttributes.get(name); - out.write(sha256.digest(name.getBytes(UTF8))); - sha256.reset(); - if (set.contains(EncryptionFlags.ENCRYPT)) { - sha256.update("ENCRYPTED".getBytes(UTF8)); - } else { - sha256.update("PLAINTEXT".getBytes(UTF8)); - } - out.write(sha256.digest()); - - sha256.reset(); - - sha256.update(AttributeValueMarshaller.marshall(tmp)); - out.write(sha256.digest()); - sha256.reset(); - } - } - return out.toByteArray(); - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } - } - - /** - * The itemAttributes have already been encrypted, if necessary, before the - * signing. - */ - byte[] calculateSignature( - Map itemAttributes, - Map> attributeFlags, - byte[] associatedData, Key key) throws GeneralSecurityException { - if (key instanceof DelegatedKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey) key); - } else if (key instanceof SecretKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (SecretKey) key); - } else if (key instanceof PrivateKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (PrivateKey) key); - } else { - throw new IllegalArgumentException("No integrity key provided"); - } - } - - byte[] calculateSignature(Map itemAttributes, - Map> attributeFlags, byte[] associatedData, - DelegatedKey key) throws GeneralSecurityException { - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - return key.sign(stringToSign, key.getAlgorithm()); - } - - byte[] calculateSignature(Map itemAttributes, - Map> attributeFlags, byte[] associatedData, - SecretKey key) throws GeneralSecurityException { - if (key instanceof DelegatedKey) { - return calculateSignature(itemAttributes, attributeFlags, associatedData, (DelegatedKey)key); - } - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - Mac hmac = Mac.getInstance(key.getAlgorithm()); - hmac.init(key); - hmac.update(stringToSign); - return hmac.doFinal(); - } - - byte[] calculateSignature(Map itemAttributes, - Map> attributeFlags, byte[] associatedData, - PrivateKey key) throws GeneralSecurityException { - byte[] stringToSign = calculateStringToSign(itemAttributes, attributeFlags, associatedData); - Signature sig = Signature.getInstance(signingAlgorithm); - sig.initSign(key, rnd); - sig.update(stringToSign); - return sig.sign(); - } - - String getSigningAlgorithm() { - return signingAlgorithm; - } - - /** - * Constant-time equality check. - */ - private boolean safeEquals(ByteBuffer signature, byte[] calculatedSig) { - try { - signature.rewind(); - Mac hmac = Mac.getInstance(hmacComparisonKey.getAlgorithm()); - hmac.init(hmacComparisonKey); - hmac.update(signature); - byte[] signatureHash = hmac.doFinal(); - - hmac.reset(); - hmac.update(calculatedSig); - byte[] calculatedHash = hmac.doFinal(); - - return MessageDigest.isEqual(signatureHash, calculatedHash); - } catch (GeneralSecurityException ex) { - // We've hardcoded these algorithms, so the error should not be possible. - throw new RuntimeException("Unexpected exception", ex); - } - } - - private static byte[] toByteArray(ByteBuffer buffer) { - if (buffer.hasArray()) { - byte[] result = buffer.array(); - buffer.rewind(); - return result; - } else { - byte[] result = new byte[buffer.remaining()]; - buffer.get(result); - buffer.rewind(); - return result; - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContext.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContext.java deleted file mode 100644 index 4651a8ea..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContext.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * This class serves to provide additional useful data to - * {@link EncryptionMaterialsProvider}s so they can more intelligently select - * the proper {@link EncryptionMaterials} or {@link DecryptionMaterials} for - * use. Any of the methods are permitted to return null. - *

- * For the simplest cases, all a developer needs to provide in the context are: - *

    - *
  • TableName
  • - *
  • HashKeyName
  • - *
  • RangeKeyName (if present)
  • - *
- * - * This class is immutable. - * - * @author Greg Rubin - */ -public final class EncryptionContext { - private final String tableName; - private final Map attributeValues; - private final Object developerContext; - private final String hashKeyName; - private final String rangeKeyName; - private final Map materialDescription; - - /** - * Return a new builder that can be used to construct an {@link EncryptionContext} - * @return A newly initialized {@link EncryptionContext.Builder}. - */ - public static Builder builder() { - return new Builder(); - } - - private EncryptionContext(Builder builder) { - tableName = builder.tableName; - attributeValues = builder.attributeValues; - developerContext = builder.developerContext; - hashKeyName = builder.hashKeyName; - rangeKeyName = builder.rangeKeyName; - materialDescription = builder.materialDescription; - } - - /** - * Returns the name of the DynamoDB Table this record is associated with. - */ - public String getTableName() { - return tableName; - } - - /** - * Returns the DynamoDB record about to be encrypted/decrypted. - */ - public Map getAttributeValues() { - return attributeValues; - } - - /** - * This object has no meaning (and will not be set or examined) by any core libraries. - * It exists to allow custom object mappers and data access layers to pass - * data to {@link EncryptionMaterialsProvider}s through the {@link DynamoDbEncryptor}. - */ - public Object getDeveloperContext() { - return developerContext; - } - - /** - * Returns the name of the HashKey attribute for the record to be encrypted/decrypted. - */ - public String getHashKeyName() { - return hashKeyName; - } - - /** - * Returns the name of the RangeKey attribute for the record to be encrypted/decrypted. - */ - public String getRangeKeyName() { - return rangeKeyName; - } - - public Map getMaterialDescription() { - return materialDescription; - } - - /** - * Converts an existing {@link EncryptionContext} into a builder that can be used to mutate and make a new version. - * @return A new {@link EncryptionContext.Builder} with all the fields filled out to match the current object. - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Builder class for {@link EncryptionContext}. - * Mutable objects (other than developerContext) will undergo - * a defensive copy prior to being stored in the builder. - * - * This class is not thread-safe. - */ - public static final class Builder { - private String tableName = null; - private Map attributeValues = null; - private Object developerContext = null; - private String hashKeyName = null; - private String rangeKeyName = null; - private Map materialDescription = null; - - private Builder() { - } - - private Builder(EncryptionContext context) { - tableName = context.getTableName(); - attributeValues = context.getAttributeValues(); - hashKeyName = context.getHashKeyName(); - rangeKeyName = context.getRangeKeyName(); - developerContext = context.getDeveloperContext(); - materialDescription = context.getMaterialDescription(); - } - - public EncryptionContext build() { - return new EncryptionContext(this); - } - - public Builder tableName(String tableName) { - this.tableName = tableName; - return this; - } - - public Builder attributeValues(Map attributeValues) { - this.attributeValues = Collections.unmodifiableMap(new HashMap<>(attributeValues)); - return this; - } - - public Builder developerContext(Object developerContext) { - this.developerContext = developerContext; - return this; - } - - public Builder hashKeyName(String hashKeyName) { - this.hashKeyName = hashKeyName; - return this; - } - - public Builder rangeKeyName(String rangeKeyName) { - this.rangeKeyName = rangeKeyName; - return this; - } - - public Builder materialDescription(Map materialDescription) { - this.materialDescription = Collections.unmodifiableMap(new HashMap<>(materialDescription)); - return this; - } - } - - @Override - public String toString() { - return "EncryptionContext [tableName=" + tableName + ", attributeValues=" + attributeValues - + ", developerContext=" + developerContext - + ", hashKeyName=" + hashKeyName + ", rangeKeyName=" + rangeKeyName - + ", materialDescription=" + materialDescription + "]"; - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContextOperators.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContextOperators.java deleted file mode 100644 index 2c880d39..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContextOperators.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import java.util.Map; -import java.util.function.UnaryOperator; - -/** - * Implementations of common operators for overriding the EncryptionContext - */ -class EncryptionContextOperators { - - // Prevent instantiation - private EncryptionContextOperators() { - } - - /** - * An operator for overriding EncryptionContext's table name for a specific DynamoDbEncryptor. If any table names or - * the encryption context itself is null, then it returns the original EncryptionContext. - * - * @param originalTableName the name of the table that should be overridden in the Encryption Context - * @param newTableName the table name that should be used in the Encryption Context - * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name - */ - static UnaryOperator overrideEncryptionContextTableName( - String originalTableName, - String newTableName) { - return encryptionContext -> { - if (encryptionContext == null - || encryptionContext.getTableName() == null - || originalTableName == null - || newTableName == null) { - return encryptionContext; - } - if (originalTableName.equals(encryptionContext.getTableName())) { - return encryptionContext.toBuilder().tableName(newTableName).build(); - } else { - return encryptionContext; - } - }; - } - - /** - * An operator for mapping multiple table names in the Encryption Context to a new table name. If the table name for - * a given EncryptionContext is missing, then it returns the original EncryptionContext. Similarly, it returns the - * original EncryptionContext if the value it is overridden to is null, or if the original table name is null. - * - * @param tableNameOverrideMap a map specifying the names of tables that should be overridden, - * and the values to which they should be overridden. If the given table name - * corresponds to null, or isn't in the map, then the table name won't be overridden. - * @return A UnaryOperator that produces a new EncryptionContext with the supplied table name - */ - static UnaryOperator overrideEncryptionContextTableNameUsingMap( - Map tableNameOverrideMap) { - return encryptionContext -> { - if (tableNameOverrideMap == null || encryptionContext == null || encryptionContext.getTableName() == null) { - return encryptionContext; - } - String newTableName = tableNameOverrideMap.get(encryptionContext.getTableName()); - if (newTableName != null) { - return encryptionContext.toBuilder().tableName(newTableName).build(); - } else { - return encryptionContext; - } - }; - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionFlags.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionFlags.java deleted file mode 100644 index ce3031da..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionFlags.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -/** - * @author Greg Rubin - */ -enum EncryptionFlags { - ENCRYPT, - SIGN -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/exceptions/DynamoDbEncryptionException.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/exceptions/DynamoDbEncryptionException.java deleted file mode 100644 index f245d66e..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/exceptions/DynamoDbEncryptionException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions; - -/** - * Generic exception thrown for any problem the DynamoDB encryption client has performing tasks - */ -public class DynamoDbEncryptionException extends RuntimeException { - private static final long serialVersionUID = - 7565904179772520868L; - - /** - * Standard constructor - * @param cause exception cause - */ - public DynamoDbEncryptionException(Throwable cause) { - super(cause); - } - - /** - * Standard constructor - * @param message exception message - */ - public DynamoDbEncryptionException(String message) { - super(message); - } - - /** - * Standard constructor - * @param message exception message - * @param cause exception cause - */ - public DynamoDbEncryptionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AbstractRawMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AbstractRawMaterials.java deleted file mode 100644 index 5dfbb197..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AbstractRawMaterials.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.security.Key; -import java.security.KeyPair; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.SecretKey; - -/** - * @author Greg Rubin - */ -public abstract class AbstractRawMaterials implements DecryptionMaterials, EncryptionMaterials { - private Map description; - private final Key signingKey; - private final Key verificationKey; - - @SuppressWarnings("unchecked") - protected AbstractRawMaterials(KeyPair signingPair) { - this(signingPair, Collections.EMPTY_MAP); - } - - protected AbstractRawMaterials(KeyPair signingPair, Map description) { - this.signingKey = signingPair.getPrivate(); - this.verificationKey = signingPair.getPublic(); - setMaterialDescription(description); - } - - @SuppressWarnings("unchecked") - protected AbstractRawMaterials(SecretKey macKey) { - this(macKey, Collections.EMPTY_MAP); - } - - protected AbstractRawMaterials(SecretKey macKey, Map description) { - this.signingKey = macKey; - this.verificationKey = macKey; - this.description = Collections.unmodifiableMap(new HashMap<>(description)); - } - - @Override - public Map getMaterialDescription() { - return new HashMap<>(description); - } - - public void setMaterialDescription(Map description) { - this.description = Collections.unmodifiableMap(new HashMap<>(description)); - } - - @Override - public Key getSigningKey() { - return signingKey; - } - - @Override - public Key getVerificationKey() { - return verificationKey; - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AsymmetricRawMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AsymmetricRawMaterials.java deleted file mode 100644 index 003d0b60..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AsymmetricRawMaterials.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.util.Collections; -import java.util.Map; - -import javax.crypto.SecretKey; - -/** - * @author Greg Rubin - */ -public class AsymmetricRawMaterials extends WrappedRawMaterials { - @SuppressWarnings("unchecked") - public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair) - throws GeneralSecurityException { - this(encryptionKey, signingPair, Collections.EMPTY_MAP); - } - - public AsymmetricRawMaterials(KeyPair encryptionKey, KeyPair signingPair, Map description) - throws GeneralSecurityException { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description); - } - - @SuppressWarnings("unchecked") - public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey) - throws GeneralSecurityException { - this(encryptionKey, macKey, Collections.EMPTY_MAP); - } - - public AsymmetricRawMaterials(KeyPair encryptionKey, SecretKey macKey, Map description) - throws GeneralSecurityException { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/CryptographicMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/CryptographicMaterials.java deleted file mode 100644 index 033d331f..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/CryptographicMaterials.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.util.Map; - -/** - * @author Greg Rubin - */ -public interface CryptographicMaterials { - Map getMaterialDescription(); -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/DecryptionMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/DecryptionMaterials.java deleted file mode 100644 index 00f8548b..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/DecryptionMaterials.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.security.Key; - -import javax.crypto.SecretKey; - -/** - * @author Greg Rubin - */ -public interface DecryptionMaterials extends CryptographicMaterials { - SecretKey getDecryptionKey(); - Key getVerificationKey(); -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/EncryptionMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/EncryptionMaterials.java deleted file mode 100644 index ecef9e9f..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/EncryptionMaterials.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.security.Key; - -import javax.crypto.SecretKey; - -/** - * @author Greg Rubin - */ -public interface EncryptionMaterials extends CryptographicMaterials { - SecretKey getEncryptionKey(); - Key getSigningKey(); -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/SymmetricRawMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/SymmetricRawMaterials.java deleted file mode 100644 index b3daab44..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/SymmetricRawMaterials.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.security.KeyPair; -import java.util.Collections; -import java.util.Map; - -import javax.crypto.SecretKey; - -/** - * @author Greg Rubin - */ -public class SymmetricRawMaterials extends AbstractRawMaterials { - private final SecretKey cryptoKey; - - @SuppressWarnings("unchecked") - public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair) { - this(encryptionKey, signingPair, Collections.EMPTY_MAP); - } - - public SymmetricRawMaterials(SecretKey encryptionKey, KeyPair signingPair, Map description) { - super(signingPair, description); - this.cryptoKey = encryptionKey; - } - - @SuppressWarnings("unchecked") - public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey) { - this(encryptionKey, macKey, Collections.EMPTY_MAP); - } - - public SymmetricRawMaterials(SecretKey encryptionKey, SecretKey macKey, Map description) { - super(macKey, description); - this.cryptoKey = encryptionKey; - } - - @Override - public SecretKey getEncryptionKey() { - return cryptoKey; - } - - @Override - public SecretKey getDecryptionKey() { - return cryptoKey; - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/WrappedRawMaterials.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/WrappedRawMaterials.java deleted file mode 100644 index 2941cf68..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/WrappedRawMaterials.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import java.security.GeneralSecurityException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.util.Collections; -import java.util.Map; - -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.DelegatedKey; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Base64; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -/** - * Represents cryptographic materials used to manage unique record-level keys. - * This class specifically implements Envelope Encryption where a unique content - * key is randomly generated each time this class is constructed which is then - * encrypted with the Wrapping Key and then persisted in the Description. If a - * wrapped key is present in the Description, then that content key is unwrapped - * and used to decrypt the actual data in the record. - * - * Other possibly implementations might use a Key-Derivation Function to derive - * a unique key per record. - * - * @author Greg Rubin - */ - -public class WrappedRawMaterials extends AbstractRawMaterials { - /** - * The key-name in the Description which contains the algorithm use to wrap - * content key. Example values are "AESWrap", or - * "RSA/ECB/OAEPWithSHA-256AndMGF1Padding". - */ - public static final String KEY_WRAPPING_ALGORITHM = "amzn-ddb-wrap-alg"; - /** - * The key-name in the Description which contains the algorithm used by the - * content key. Example values are "AES", or "Blowfish". - */ - public static final String CONTENT_KEY_ALGORITHM = "amzn-ddb-env-alg"; - /** - * The key-name in the Description which which contains the wrapped content - * key. - */ - public static final String ENVELOPE_KEY = "amzn-ddb-env-key"; - private static final String DEFAULT_ALGORITHM = "AES/256"; - - protected final Key wrappingKey; - protected final Key unwrappingKey; - private final SecretKey envelopeKey; - - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair) - throws GeneralSecurityException { - this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap()); - } - - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, KeyPair signingPair, - Map description) throws GeneralSecurityException { - super(signingPair, description); - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.envelopeKey = initEnvelopeKey(); - } - - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey) - throws GeneralSecurityException { - this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap()); - } - - public WrappedRawMaterials(Key wrappingKey, Key unwrappingKey, SecretKey macKey, - Map description) throws GeneralSecurityException { - super(macKey, description); - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.envelopeKey = initEnvelopeKey(); - } - - @Override - public SecretKey getDecryptionKey() { - return envelopeKey; - } - - @Override - public SecretKey getEncryptionKey() { - return envelopeKey; - } - - /** - * Called by the constructors. If there is already a key associated with - * this record (usually signified by a value stored in the description in - * the key {@link #ENVELOPE_KEY}) it extracts it and returns it. Otherwise - * it generates a new key, stores a wrapped version in the Description, and - * returns the key to the caller. - * - * @return the content key (which is returned by both - * {@link #getDecryptionKey()} and {@link #getEncryptionKey()}. - * @throws GeneralSecurityException if there is a problem - */ - protected SecretKey initEnvelopeKey() throws GeneralSecurityException { - Map description = getMaterialDescription(); - if (description.containsKey(ENVELOPE_KEY)) { - if (unwrappingKey == null) { - throw new IllegalStateException("No private decryption key provided."); - } - byte[] encryptedKey = Base64.decode(description.get(ENVELOPE_KEY)); - String wrappingAlgorithm = unwrappingKey.getAlgorithm(); - if (description.containsKey(KEY_WRAPPING_ALGORITHM)) { - wrappingAlgorithm = description.get(KEY_WRAPPING_ALGORITHM); - } - return unwrapKey(description, encryptedKey, wrappingAlgorithm); - } else { - SecretKey key = description.containsKey(CONTENT_KEY_ALGORITHM) ? - generateContentKey(description.get(CONTENT_KEY_ALGORITHM)) : - generateContentKey(DEFAULT_ALGORITHM); - - String wrappingAlg = description.containsKey(KEY_WRAPPING_ALGORITHM) ? - description.get(KEY_WRAPPING_ALGORITHM) : - getTransformation(wrappingKey.getAlgorithm()); - byte[] encryptedKey = wrapKey(key, wrappingAlg); - description.put(ENVELOPE_KEY, Base64.encodeToString(encryptedKey)); - description.put(CONTENT_KEY_ALGORITHM, key.getAlgorithm()); - description.put(KEY_WRAPPING_ALGORITHM, wrappingAlg); - setMaterialDescription(description); - return key; - } - } - - public byte[] wrapKey(SecretKey key, String wrappingAlg) throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException, IllegalBlockSizeException { - if (wrappingKey instanceof DelegatedKey) { - return ((DelegatedKey)wrappingKey).wrap(key, null, wrappingAlg); - } else { - Cipher cipher = Cipher.getInstance(wrappingAlg); - cipher.init(Cipher.WRAP_MODE, wrappingKey, Utils.getRng()); - return cipher.wrap(key); - } - } - - protected SecretKey unwrapKey(Map description, byte[] encryptedKey, String wrappingAlgorithm) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { - if (unwrappingKey instanceof DelegatedKey) { - return (SecretKey)((DelegatedKey)unwrappingKey).unwrap(encryptedKey, - description.get(CONTENT_KEY_ALGORITHM), Cipher.SECRET_KEY, null, wrappingAlgorithm); - } else { - Cipher cipher = Cipher.getInstance(wrappingAlgorithm); - cipher.init(Cipher.UNWRAP_MODE, unwrappingKey, Utils.getRng()); - return (SecretKey) cipher.unwrap(encryptedKey, - description.get(CONTENT_KEY_ALGORITHM), Cipher.SECRET_KEY); - } - } - - protected SecretKey generateContentKey(final String algorithm) throws NoSuchAlgorithmException { - String[] pieces = algorithm.split("/", 2); - KeyGenerator kg = KeyGenerator.getInstance(pieces[0]); - int keyLen = 0; - if (pieces.length == 2) { - try { - keyLen = Integer.parseInt(pieces[1]); - } catch (NumberFormatException ignored) { - } - } - - if (keyLen > 0) { - kg.init(keyLen, Utils.getRng()); - } else { - kg.init(Utils.getRng()); - } - return kg.generateKey(); - } - - private static String getTransformation(final String algorithm) { - if (algorithm.equalsIgnoreCase("RSA")) { - return "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; - } else if (algorithm.equalsIgnoreCase("AES")) { - return "AESWrap"; - } else { - return algorithm; - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/AsymmetricStaticProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/AsymmetricStaticProvider.java deleted file mode 100644 index b49e2b9a..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/AsymmetricStaticProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import java.security.KeyPair; -import java.util.Collections; -import java.util.Map; - -import javax.crypto.SecretKey; - -/** - * This is a thin wrapper around the {@link WrappedMaterialsProvider}, using - * the provided encryptionKey for wrapping and unwrapping the - * record key. Please see that class for detailed documentation. - * - * @author Greg Rubin - */ -public class AsymmetricStaticProvider extends WrappedMaterialsProvider { - public AsymmetricStaticProvider(KeyPair encryptionKey, KeyPair signingPair) { - this(encryptionKey, signingPair, Collections.emptyMap()); - } - - public AsymmetricStaticProvider(KeyPair encryptionKey, SecretKey macKey) { - this(encryptionKey, macKey, Collections.emptyMap()); - } - - public AsymmetricStaticProvider(KeyPair encryptionKey, KeyPair signingPair, Map description) { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), signingPair, description); - } - - public AsymmetricStaticProvider(KeyPair encryptionKey, SecretKey macKey, Map description) { - super(encryptionKey.getPublic(), encryptionKey.getPrivate(), macKey, description); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/DirectKmsMaterialsProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/DirectKmsMaterialsProvider.java deleted file mode 100644 index ab402cbb..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/DirectKmsMaterialsProvider.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials.CONTENT_KEY_ALGORITHM; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials.ENVELOPE_KEY; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials.KEY_WRAPPING_ALGORITHM; - -import java.security.NoSuchAlgorithmException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.SymmetricRawMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Base64; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Hkdf; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.kms.KmsClient; -import software.amazon.awssdk.services.kms.model.DecryptRequest; -import software.amazon.awssdk.services.kms.model.DecryptResponse; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyRequest; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyResponse; - -/** - * Generates a unique data key for each record in DynamoDB and protects that key - * using {@link KmsClient}. Currently, the HashKey, RangeKey, and TableName will be - * included in the KMS EncryptionContext for wrapping/unwrapping the key. This - * means that records cannot be copied/moved between tables without re-encryption. - * - * @see KMS Encryption Context - */ -public class DirectKmsMaterialsProvider implements EncryptionMaterialsProvider { - private static final String COVERED_ATTR_CTX_KEY = "aws-kms-ec-attr"; - private static final String SIGNING_KEY_ALGORITHM = "amzn-ddb-sig-alg"; - private static final String TABLE_NAME_EC_KEY = "*aws-kms-table*"; - - private static final String DEFAULT_ENC_ALG = "AES/256"; - private static final String DEFAULT_SIG_ALG = "HmacSHA256/256"; - private static final String KEY_COVERAGE = "*keys*"; - private static final String KDF_ALG = "HmacSHA256"; - private static final String KDF_SIG_INFO = "Signing"; - private static final String KDF_ENC_INFO = "Encryption"; - - private final KmsClient kms; - private final String encryptionKeyId; - private final Map description; - private final String dataKeyAlg; - private final int dataKeyLength; - private final String dataKeyDesc; - private final String sigKeyAlg; - private final int sigKeyLength; - private final String sigKeyDesc; - - public DirectKmsMaterialsProvider(KmsClient kms) { - this(kms, null); - } - - public DirectKmsMaterialsProvider(KmsClient kms, String encryptionKeyId, Map materialDescription) { - this.kms = kms; - this.encryptionKeyId = encryptionKeyId; - this.description = materialDescription != null ? - Collections.unmodifiableMap(new HashMap<>(materialDescription)) : - Collections.emptyMap(); - - dataKeyDesc = description.getOrDefault(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, DEFAULT_ENC_ALG); - - String[] parts = dataKeyDesc.split("/", 2); - this.dataKeyAlg = parts[0]; - this.dataKeyLength = parts.length == 2 ? Integer.parseInt(parts[1]) : 256; - - sigKeyDesc = description.getOrDefault(SIGNING_KEY_ALGORITHM, DEFAULT_SIG_ALG); - - parts = sigKeyDesc.split("/", 2); - this.sigKeyAlg = parts[0]; - this.sigKeyLength = parts.length == 2 ? Integer.parseInt(parts[1]) : 256; - } - - public DirectKmsMaterialsProvider(KmsClient kms, String encryptionKeyId) { - this(kms, encryptionKeyId, Collections.emptyMap()); - } - - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - final Map materialDescription = context.getMaterialDescription(); - - final Map ec = new HashMap<>(); - final String providedEncAlg = materialDescription.get(CONTENT_KEY_ALGORITHM); - final String providedSigAlg = materialDescription.get(SIGNING_KEY_ALGORITHM); - - ec.put("*" + CONTENT_KEY_ALGORITHM + "*", providedEncAlg); - ec.put("*" + SIGNING_KEY_ALGORITHM + "*", providedSigAlg); - - populateKmsEcFromEc(context, ec); - - DecryptRequest.Builder request = DecryptRequest.builder(); - request.ciphertextBlob(SdkBytes.fromByteArray(Base64.decode(materialDescription.get(ENVELOPE_KEY)))); - request.encryptionContext(ec); - final DecryptResponse decryptResponse = decrypt(request.build(), context); - validateEncryptionKeyId(decryptResponse.keyId(), context); - - final Hkdf kdf; - try { - kdf = Hkdf.getInstance(KDF_ALG); - } catch (NoSuchAlgorithmException e) { - throw new DynamoDbEncryptionException(e); - } - kdf.init(decryptResponse.plaintext().asByteArray()); - - final String[] encAlgParts = providedEncAlg.split("/", 2); - int encLength = encAlgParts.length == 2 ? Integer.parseInt(encAlgParts[1]) : 256; - final String[] sigAlgParts = providedSigAlg.split("/", 2); - int sigLength = sigAlgParts.length == 2 ? Integer.parseInt(sigAlgParts[1]) : 256; - - final SecretKey encryptionKey = new SecretKeySpec(kdf.deriveKey(KDF_ENC_INFO, encLength / 8), encAlgParts[0]); - final SecretKey macKey = new SecretKeySpec(kdf.deriveKey(KDF_SIG_INFO, sigLength / 8), sigAlgParts[0]); - - return new SymmetricRawMaterials(encryptionKey, macKey, materialDescription); - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - final Map ec = new HashMap<>(); - ec.put("*" + CONTENT_KEY_ALGORITHM + "*", dataKeyDesc); - ec.put("*" + SIGNING_KEY_ALGORITHM + "*", sigKeyDesc); - populateKmsEcFromEc(context, ec); - - final String keyId = selectEncryptionKeyId(context); - if (keyId == null || keyId.isEmpty()) { - throw new DynamoDbEncryptionException("Encryption key id is empty."); - } - - final GenerateDataKeyRequest.Builder req = GenerateDataKeyRequest.builder(); - req.keyId(keyId); - // NumberOfBytes parameter is used because we're not using this key as an AES-256 key, - // we're using it as an HKDF-SHA256 key. - req.numberOfBytes(256 / 8); - req.encryptionContext(ec); - - final GenerateDataKeyResponse dataKeyResult = generateDataKey(req.build(), context); - - final Map materialDescription = new HashMap<>(description); - materialDescription.put(COVERED_ATTR_CTX_KEY, KEY_COVERAGE); - materialDescription.put(KEY_WRAPPING_ALGORITHM, "kms"); - materialDescription.put(CONTENT_KEY_ALGORITHM, dataKeyDesc); - materialDescription.put(SIGNING_KEY_ALGORITHM, sigKeyDesc); - materialDescription.put(ENVELOPE_KEY, - Base64.encodeToString(dataKeyResult.ciphertextBlob().asByteArray())); - - final Hkdf kdf; - try { - kdf = Hkdf.getInstance(KDF_ALG); - } catch (NoSuchAlgorithmException e) { - throw new DynamoDbEncryptionException(e); - } - - kdf.init(dataKeyResult.plaintext().asByteArray()); - - final SecretKey encryptionKey = new SecretKeySpec(kdf.deriveKey(KDF_ENC_INFO, dataKeyLength / 8), dataKeyAlg); - final SecretKey signatureKey = new SecretKeySpec(kdf.deriveKey(KDF_SIG_INFO, sigKeyLength / 8), sigKeyAlg); - return new SymmetricRawMaterials(encryptionKey, signatureKey, materialDescription); - } - - /** - * Get encryption key id that is used to create the {@link EncryptionMaterials}. - * - * @return encryption key id. - */ - protected String getEncryptionKeyId() { - return this.encryptionKeyId; - } - - /** - * Select encryption key id to be used to generate data key. The default implementation of this method returns - * {@link DirectKmsMaterialsProvider#encryptionKeyId}. - * - * @param context encryption context. - * @return the encryptionKeyId. - * @throws DynamoDbEncryptionException when we fails to select a valid encryption key id. - */ - protected String selectEncryptionKeyId(EncryptionContext context) throws DynamoDbEncryptionException { - return getEncryptionKeyId(); - } - - /** - * Validate the encryption key id. The default implementation of this method does not validate - * encryption key id. - * - * @param encryptionKeyId encryption key id from {@link DecryptResponse}. - * @param context encryption context. - * @throws DynamoDbEncryptionException when encryptionKeyId is invalid. - */ - protected void validateEncryptionKeyId(String encryptionKeyId, EncryptionContext context) - throws DynamoDbEncryptionException { - // No action taken. - } - - /** - * Decrypts ciphertext. The default implementation calls KMS to decrypt the ciphertext using the parameters - * provided in the {@link DecryptRequest}. Subclass can override the default implementation to provide - * additional request parameters using attributes within the {@link EncryptionContext}. - * - * @param request request parameters to decrypt the given ciphertext. - * @param context additional useful data to decrypt the ciphertext. - * @return the decrypted plaintext for the given ciphertext. - */ - protected DecryptResponse decrypt(final DecryptRequest request, final EncryptionContext context) { - return kms.decrypt(request); - } - - /** - * Returns a data encryption key that you can use in your application to encrypt data locally. The default - * implementation calls KMS to generate the data key using the parameters provided in the - * {@link GenerateDataKeyRequest}. Subclass can override the default implementation to provide additional - * request parameters using attributes within the {@link EncryptionContext}. - * - * @param request request parameters to generate the data key. - * @param context additional useful data to generate the data key. - * @return the newly generated data key which includes both the plaintext and ciphertext. - */ - protected GenerateDataKeyResponse generateDataKey(final GenerateDataKeyRequest request, - final EncryptionContext context) { - return kms.generateDataKey(request); - } - - /** - * Extracts relevant information from {@code context} and uses it to populate fields in - * {@code kmsEc}. Subclass can override the default implementation to provide an alternative - * encryption context in calls to KMS. Currently, the default implementation includes these fields: - *
- *
{@code HashKeyName}
- *
{@code HashKeyValue}
- *
{@code RangeKeyName}
- *
{@code RangeKeyValue}
- *
{@link #TABLE_NAME_EC_KEY}
- *
{@code TableName}
- */ - protected void populateKmsEcFromEc(EncryptionContext context, Map kmsEc) { - final String hashKeyName = context.getHashKeyName(); - if (hashKeyName != null) { - final AttributeValue hashKey = context.getAttributeValues().get(hashKeyName); - if (hashKey.n() != null) { - kmsEc.put(hashKeyName, hashKey.n()); - } else if (hashKey.s() != null) { - kmsEc.put(hashKeyName, hashKey.s()); - } else if (hashKey.b() != null) { - kmsEc.put(hashKeyName, Base64.encodeToString(hashKey.b().asByteArray())); - } else { - throw new UnsupportedOperationException("DirectKmsMaterialsProvider only supports String, Number, and Binary HashKeys"); - } - } - final String rangeKeyName = context.getRangeKeyName(); - if (rangeKeyName != null) { - final AttributeValue rangeKey = context.getAttributeValues().get(rangeKeyName); - if (rangeKey.n() != null) { - kmsEc.put(rangeKeyName, rangeKey.n()); - } else if (rangeKey.s() != null) { - kmsEc.put(rangeKeyName, rangeKey.s()); - } else if (rangeKey.b() != null) { - kmsEc.put(rangeKeyName, Base64.encodeToString(rangeKey.b().asByteArray())); - } else { - throw new UnsupportedOperationException("DirectKmsMaterialsProvider only supports String, Number, and Binary RangeKeys"); - } - } - - final String tableName = context.getTableName(); - if (tableName != null) { - kmsEc.put(TABLE_NAME_EC_KEY, tableName); - } - } - - @Override - public void refresh() { - // No action needed - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/EncryptionMaterialsProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/EncryptionMaterialsProvider.java deleted file mode 100644 index b60fee3e..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/EncryptionMaterialsProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; - -/** - * Interface for providing encryption materials. - * Implementations are free to use any strategy for providing encryption - * materials, such as simply providing static material that doesn't change, - * or more complicated implementations, such as integrating with existing - * key management systems. - * - * @author Greg Rubin - */ -public interface EncryptionMaterialsProvider { - - /** - * Retrieves encryption materials matching the specified description from some source. - * - * @param context - * Information to assist in selecting a the proper return value. The implementation - * is free to determine the minimum necessary for successful processing. - * - * @return - * The encryption materials that match the description, or null if no matching encryption materials found. - */ - DecryptionMaterials getDecryptionMaterials(EncryptionContext context); - - /** - * Returns EncryptionMaterials which the caller can use for encryption. - * Each implementation of EncryptionMaterialsProvider can choose its own - * strategy for loading encryption material. For example, an - * implementation might load encryption material from an existing key - * management system, or load new encryption material when keys are - * rotated. - * - * @param context - * Information to assist in selecting a the proper return value. The implementation - * is free to determine the minimum necessary for successful processing. - * - * @return EncryptionMaterials which the caller can use to encrypt or - * decrypt data. - */ - EncryptionMaterials getEncryptionMaterials(EncryptionContext context); - - /** - * Forces this encryption materials provider to refresh its encryption - * material. For many implementations of encryption materials provider, - * this may simply be a no-op, such as any encryption materials provider - * implementation that vends static/non-changing encryption material. - * For other implementations that vend different encryption material - * throughout their lifetime, this method should force the encryption - * materials provider to refresh its encryption material. - */ - void refresh(); -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/KeyStoreMaterialsProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/KeyStoreMaterialsProvider.java deleted file mode 100644 index 483b81b5..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/KeyStoreMaterialsProvider.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyStore; -import java.security.KeyStore.Entry; -import java.security.KeyStore.PrivateKeyEntry; -import java.security.KeyStore.ProtectionParameter; -import java.security.KeyStore.SecretKeyEntry; -import java.security.KeyStore.TrustedCertificateEntry; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.UnrecoverableEntryException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.AsymmetricRawMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.SymmetricRawMaterials; - -/** - * @author Greg Rubin - */ -public class KeyStoreMaterialsProvider implements EncryptionMaterialsProvider { - private final Map description; - private final String encryptionAlias; - private final String signingAlias; - private final ProtectionParameter encryptionProtection; - private final ProtectionParameter signingProtection; - private final KeyStore keyStore; - private final AtomicReference currMaterials = - new AtomicReference<>(); - - public KeyStoreMaterialsProvider(KeyStore keyStore, String encryptionAlias, String signingAlias, Map description) - throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { - this(keyStore, encryptionAlias, signingAlias, null, null, description); - } - - public KeyStoreMaterialsProvider(KeyStore keyStore, String encryptionAlias, String signingAlias, - ProtectionParameter encryptionProtection, ProtectionParameter signingProtection, - Map description) - throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { - super(); - this.keyStore = keyStore; - this.encryptionAlias = encryptionAlias; - this.signingAlias = signingAlias; - this.encryptionProtection = encryptionProtection; - this.signingProtection = signingProtection; - this.description = Collections.unmodifiableMap(new HashMap<>(description)); - - validateKeys(); - loadKeys(); - } - - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - CurrentMaterials materials = currMaterials.get(); - if (context.getMaterialDescription().entrySet().containsAll(description.entrySet())) { - if (materials.encryptionEntry instanceof SecretKeyEntry) { - return materials.symRawMaterials; - } else { - try { - return makeAsymMaterials(materials, context.getMaterialDescription()); - } catch (GeneralSecurityException ex) { - throw new DynamoDbEncryptionException("Unable to decrypt envelope key", ex); - } - } - } else { - return null; - } - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - CurrentMaterials materials = currMaterials.get(); - if (materials.encryptionEntry instanceof SecretKeyEntry) { - return materials.symRawMaterials; - } else { - try { - return makeAsymMaterials(materials, description); - } catch (GeneralSecurityException ex) { - throw new DynamoDbEncryptionException("Unable to encrypt envelope key", ex); - } - } - } - - private AsymmetricRawMaterials makeAsymMaterials(CurrentMaterials materials, - Map description) throws GeneralSecurityException { - KeyPair encryptionPair = entry2Pair(materials.encryptionEntry); - if (materials.signingEntry instanceof SecretKeyEntry) { - return new AsymmetricRawMaterials(encryptionPair, - ((SecretKeyEntry) materials.signingEntry).getSecretKey(), description); - } else { - return new AsymmetricRawMaterials(encryptionPair, entry2Pair(materials.signingEntry), - description); - } - } - - private static KeyPair entry2Pair(Entry entry) { - PublicKey pub = null; - PrivateKey priv = null; - - if (entry instanceof PrivateKeyEntry) { - PrivateKeyEntry pk = (PrivateKeyEntry) entry; - if (pk.getCertificate() != null) { - pub = pk.getCertificate().getPublicKey(); - } - priv = pk.getPrivateKey(); - } else if (entry instanceof TrustedCertificateEntry) { - TrustedCertificateEntry tc = (TrustedCertificateEntry) entry; - pub = tc.getTrustedCertificate().getPublicKey(); - } else { - throw new IllegalArgumentException( - "Only entry types PrivateKeyEntry and TrustedCertificateEntry are supported."); - } - return new KeyPair(pub, priv); - } - - /** - * Reloads the keys from the underlying keystore by calling - * {@link KeyStore#getEntry(String, ProtectionParameter)} again for each of them. - */ - @Override - public void refresh() { - try { - loadKeys(); - } catch (GeneralSecurityException ex) { - throw new DynamoDbEncryptionException("Unable to load keys from keystore", ex); - } - } - - private void validateKeys() throws KeyStoreException { - if (!keyStore.containsAlias(encryptionAlias)) { - throw new IllegalArgumentException("Keystore does not contain alias: " - + encryptionAlias); - } - if (!keyStore.containsAlias(signingAlias)) { - throw new IllegalArgumentException("Keystore does not contain alias: " - + signingAlias); - } - } - - private void loadKeys() throws NoSuchAlgorithmException, UnrecoverableEntryException, - KeyStoreException { - Entry encryptionEntry = keyStore.getEntry(encryptionAlias, encryptionProtection); - Entry signingEntry = keyStore.getEntry(signingAlias, signingProtection); - CurrentMaterials newMaterials = new CurrentMaterials(encryptionEntry, signingEntry); - currMaterials.set(newMaterials); - } - - private class CurrentMaterials { - public final Entry encryptionEntry; - public final Entry signingEntry; - public final SymmetricRawMaterials symRawMaterials; - - public CurrentMaterials(Entry encryptionEntry, Entry signingEntry) { - super(); - this.encryptionEntry = encryptionEntry; - this.signingEntry = signingEntry; - - if (encryptionEntry instanceof SecretKeyEntry) { - if (signingEntry instanceof SecretKeyEntry) { - this.symRawMaterials = new SymmetricRawMaterials( - ((SecretKeyEntry) encryptionEntry).getSecretKey(), - ((SecretKeyEntry) signingEntry).getSecretKey(), - description); - } else { - this.symRawMaterials = new SymmetricRawMaterials( - ((SecretKeyEntry) encryptionEntry).getSecretKey(), - entry2Pair(signingEntry), - description); - } - } else { - this.symRawMaterials = null; - } - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/MostRecentProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/MostRecentProvider.java deleted file mode 100644 index f0edc768..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/MostRecentProvider.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2016-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.store.ProviderStore; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.LRUCache; - -/** - * This meta-Provider encrypts data with the most recent version of keying materials from a - * {@link ProviderStore} and decrypts using whichever version is appropriate. It also caches the - * results from the {@link ProviderStore} to avoid excessive load on the backing systems. The cache - * is not currently configurable. - */ -public class MostRecentProvider implements EncryptionMaterialsProvider { - private static final long MILLI_TO_NANO = 1000000L; - private static final long TTL_GRACE_IN_NANO = 500 * MILLI_TO_NANO; - private final ProviderStore keystore; - protected final String defaultMaterialName; - private final long ttlInNanos; - private final LRUCache cache; - private final LRUCache currentVersions; - - /** - * Creates a new {@link MostRecentProvider}. - * - * @param ttlInMillis - * The length of time in milliseconds to cache the most recent provider - */ - public MostRecentProvider(final ProviderStore keystore, final String materialName, final long ttlInMillis) { - this.keystore = checkNotNull(keystore, "keystore must not be null"); - this.defaultMaterialName = materialName; - this.ttlInNanos = ttlInMillis * MILLI_TO_NANO; - this.cache = new LRUCache(1000); - this.currentVersions = new LRUCache<>(1000); - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - final String materialName = getMaterialName(context); - final LockedState ls = getCurrentVersion(materialName); - - final State s = ls.getState(); - if (s.provider != null && System.nanoTime() - s.lastUpdated <= ttlInNanos) { - return s.provider.getEncryptionMaterials(context); - } - if (s.provider == null || System.nanoTime() - s.lastUpdated > ttlInNanos + TTL_GRACE_IN_NANO) { - // Either we don't have a provider at all, or we're more than 500 milliseconds past - // our update time. Either way, grab the lock and force an update. - ls.lock(); - } else if (!ls.tryLock()) { - // If we can't get the lock immediately, just use the current provider - return s.provider.getEncryptionMaterials(context); - } - - try { - final long newVersion = keystore.getMaxVersion(materialName); - final long currentVersion; - final EncryptionMaterialsProvider currentProvider; - if (newVersion < 0) { - // First version of the material, so we want to allow creation - currentVersion = 0; - currentProvider = keystore.getOrCreate(materialName, currentVersion); - cache.add(buildCacheKey(materialName, currentVersion), currentProvider); - } else if (newVersion != s.currentVersion) { - // We're retrieving an existing version, so we avoid the creation - // flow as it is slower - currentVersion = newVersion; - currentProvider = keystore.getProvider(materialName, currentVersion); - cache.add(buildCacheKey(materialName, currentVersion), currentProvider); - } else { - // Our version hasn't changed, so we'll just re-use the existing - // provider to avoid the overhead of retrieving and building a new one - currentVersion = newVersion; - currentProvider = s.provider; - // There is no need to add this to the cache as it's already there - } - - ls.update(currentProvider, currentVersion); - - return ls.getState().provider.getEncryptionMaterials(context); - } finally { - ls.unlock(); - } - } - - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - final String materialName = getMaterialName(context); - final long version = keystore.getVersionFromMaterialDescription( - context.getMaterialDescription()); - EncryptionMaterialsProvider provider = cache.get(buildCacheKey(materialName, version)); - if (provider == null) { - provider = keystore.getProvider(materialName, version); - cache.add(buildCacheKey(materialName, version), provider); - } - return provider.getDecryptionMaterials(context); - } - - /** - * Completely empties the cache of both the current and old versions. - */ - @Override - public void refresh() { - currentVersions.clear(); - cache.clear(); - } - - public String getMaterialName() { - return defaultMaterialName; - } - - public long getTtlInMills() { - return ttlInNanos / MILLI_TO_NANO; - } - - /** - * The current version of the materials being used for encryption. Returns -1 if we do not - * currently have a current version. - */ - public long getCurrentVersion() { - return getCurrentVersion(getMaterialName()).getState().currentVersion; - } - - /** - * The last time the current version was updated. Returns 0 if we do not currently have a - * current version. - */ - public long getLastUpdated() { - return getCurrentVersion(getMaterialName()).getState().lastUpdated / MILLI_TO_NANO; - } - - protected String getMaterialName(final EncryptionContext context) { - return defaultMaterialName; - } - - private LockedState getCurrentVersion(final String materialName) { - final LockedState result = currentVersions.get(materialName); - if (result == null) { - currentVersions.add(materialName, new LockedState()); - return currentVersions.get(materialName); - } else { - return result; - } - } - - private static String buildCacheKey(final String materialName, final long version) { - StringBuilder result = new StringBuilder(materialName); - result.append('#'); - result.append(version); - return result.toString(); - } - - private static V checkNotNull(final V ref, final String errMsg) { - if (ref == null) { - throw new NullPointerException(errMsg); - } else { - return ref; - } - } - - private static class LockedState { - private final ReentrantLock lock = new ReentrantLock(true); - private volatile AtomicReference state = new AtomicReference<>(new State()); - - public State getState() { - return state.get(); - } - - public void unlock() { - lock.unlock(); - } - - public boolean tryLock() { - return lock.tryLock(); - } - - public void lock() { - lock.lock(); - } - - public void update(EncryptionMaterialsProvider provider, long currentVersion) { - if (!lock.isHeldByCurrentThread()) { - throw new IllegalStateException("Lock not held by current thread"); - } - state.set(new State(provider, currentVersion)); - } - } - - private static class State { - public final EncryptionMaterialsProvider provider; - public final long currentVersion; - public final long lastUpdated; - - public State() { - this(null, -1); - } - - public State(EncryptionMaterialsProvider provider, long currentVersion) { - this.provider = provider; - this.currentVersion = currentVersion; - this.lastUpdated = currentVersion == -1 ? 0 : System.nanoTime(); - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/SymmetricStaticProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/SymmetricStaticProvider.java deleted file mode 100644 index 8a63a032..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/SymmetricStaticProvider.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import java.security.KeyPair; -import java.util.Collections; -import java.util.Map; - -import javax.crypto.SecretKey; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.CryptographicMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.SymmetricRawMaterials; - -/** - * A provider which always returns the same provided symmetric - * encryption/decryption key and the same signing/verification key(s). - * - * @author Greg Rubin - */ -public class SymmetricStaticProvider implements EncryptionMaterialsProvider { - private final SymmetricRawMaterials materials; - - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If - * only the public key is provided, then this provider may be - * used for decryption, but not encryption. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, KeyPair signingPair) { - this(encryptionKey, signingPair, Collections.emptyMap()); - } - - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If - * only the public key is provided, then this provider may be - * used for decryption, but not encryption. - * @param description - * the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for - * any {@link CryptographicMaterials} returned by this object. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, - KeyPair signingPair, Map description) { - materials = new SymmetricRawMaterials(encryptionKey, signingPair, - description); - } - - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, SecretKey macKey) { - this(encryptionKey, macKey, Collections.emptyMap()); - } - - /** - * @param encryptionKey - * the value to be returned by - * {@link #getEncryptionMaterials(EncryptionContext)} and - * {@link #getDecryptionMaterials(EncryptionContext)} - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - * @param description - * the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for - * any {@link CryptographicMaterials} returned by this object. - */ - public SymmetricStaticProvider(SecretKey encryptionKey, SecretKey macKey, Map description) { - materials = new SymmetricRawMaterials(encryptionKey, macKey, description); - } - - /** - * Returns the encryptionKey provided to the constructor if and only if - * materialDescription is a super-set (may be equal) to the - * description provided to the constructor. - */ - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - if (context.getMaterialDescription().entrySet().containsAll(materials.getMaterialDescription().entrySet())) { - return materials; - } - else { - return null; - } - } - - /** - * Returns the encryptionKey provided to the constructor. - */ - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - return materials; - } - - /** - * Does nothing. - */ - @Override - public void refresh() { - // Do Nothing - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/WrappedMaterialsProvider.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/WrappedMaterialsProvider.java deleted file mode 100644 index 1c92fb3f..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/WrappedMaterialsProvider.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import java.security.GeneralSecurityException; -import java.security.Key; -import java.security.KeyPair; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.SecretKey; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.CryptographicMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials; - -/** - * This provider will use create a unique (random) symmetric key upon each call to - * {@link #getEncryptionMaterials(EncryptionContext)}. Practically, this means each record in DynamoDB will be - * encrypted under a unique record key. A wrapped/encrypted copy of this record key is stored in the - * MaterialsDescription field of that record and is unwrapped/decrypted upon reading that record. - * - * This is generally a more secure way of encrypting data than with the - * {@link SymmetricStaticProvider}. - * - * @see WrappedRawMaterials - * - * @author Greg Rubin - */ -public class WrappedMaterialsProvider implements EncryptionMaterialsProvider { - private final Key wrappingKey; - private final Key unwrappingKey; - private final KeyPair sigPair; - private final SecretKey macKey; - private final Map description; - - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If only the public key - * is provided, then this provider may only be used for decryption, but not - * encryption. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, KeyPair signingPair) { - this(wrappingKey, unwrappingKey, signingPair, Collections.emptyMap()); - } - - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param signingPair - * the keypair used to sign/verify the data stored in Dynamo. If only the public key - * is provided, then this provider may only be used for decryption, but not - * encryption. - * @param description - * description the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for any - * {@link CryptographicMaterials} returned by this object. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, KeyPair signingPair, Map description) { - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.sigPair = signingPair; - this.macKey = null; - this.description = Collections.unmodifiableMap(new HashMap<>(description)); - } - - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, SecretKey macKey) { - this(wrappingKey, unwrappingKey, macKey, Collections.emptyMap()); - } - - /** - * @param wrappingKey - * The key used to wrap/encrypt the symmetric record key. (May be the same as the - * unwrappingKey.) - * @param unwrappingKey - * The key used to unwrap/decrypt the symmetric record key. (May be the same as the - * wrappingKey.) If null, then this provider may only be used for - * decryption, but not encryption. - * @param macKey - * the key used to sign/verify the data stored in Dynamo. - * @param description - * description the value to be returned by - * {@link CryptographicMaterials#getMaterialDescription()} for any - * {@link CryptographicMaterials} returned by this object. - */ - public WrappedMaterialsProvider(Key wrappingKey, Key unwrappingKey, SecretKey macKey, Map description) { - this.wrappingKey = wrappingKey; - this.unwrappingKey = unwrappingKey; - this.sigPair = null; - this.macKey = macKey; - this.description = Collections.unmodifiableMap(new HashMap<>(description)); - } - - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - try { - if (macKey != null) { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, macKey, context.getMaterialDescription()); - } else { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, sigPair, context.getMaterialDescription()); - } - } catch (GeneralSecurityException ex) { - throw new DynamoDbEncryptionException("Unable to decrypt envelope key", ex); - } - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - try { - if (macKey != null) { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, macKey, description); - } else { - return new WrappedRawMaterials(wrappingKey, unwrappingKey, sigPair, description); - } - } catch (GeneralSecurityException ex) { - throw new DynamoDbEncryptionException("Unable to encrypt envelope key", ex); - } - } - - @Override - public void refresh() { - // Do nothing - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/MetaStore.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/MetaStore.java deleted file mode 100644 index 9bc50036..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/MetaStore.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.store; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.DynamoDbEncryptionConfiguration; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.EncryptionAction; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.DynamoDbEncryptor; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.WrappedMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - - -/** - * Provides a simple collection of EncryptionMaterialProviders backed by an encrypted DynamoDB - * table. This can be used to build key hierarchies or meta providers. - * - * Currently, this only supports AES-256 in AESWrap mode and HmacSHA256 for the providers persisted - * in the table. - * - * @author rubin - */ -public class MetaStore extends ProviderStore { - private static final String INTEGRITY_ALGORITHM_FIELD = "intAlg"; - private static final String INTEGRITY_KEY_FIELD = "int"; - private static final String ENCRYPTION_ALGORITHM_FIELD = "encAlg"; - private static final String ENCRYPTION_KEY_FIELD = "enc"; - private static final Pattern COMBINED_PATTERN = Pattern.compile("([^#]+)#(\\d*)"); - private static final String DEFAULT_INTEGRITY = "HmacSHA256"; - private static final String DEFAULT_ENCRYPTION = "AES"; - private static final String MATERIAL_TYPE_VERSION = "t"; - private static final String META_ID = "amzn-ddb-meta-id"; - - private static final String DEFAULT_HASH_KEY = "N"; - private static final String DEFAULT_RANGE_KEY = "V"; - - /** Default no-op implementation of {@link ExtraDataSupplier}. */ - private static final EmptyExtraDataSupplier EMPTY_EXTRA_DATA_SUPPLIER - = new EmptyExtraDataSupplier(); - - /** DDB fields that must be encrypted. */ - private static final Set ENCRYPTED_FIELDS; - static { - final Set tempEncryptedFields = new HashSet<>(); - tempEncryptedFields.add(MATERIAL_TYPE_VERSION); - tempEncryptedFields.add(ENCRYPTION_KEY_FIELD); - tempEncryptedFields.add(ENCRYPTION_ALGORITHM_FIELD); - tempEncryptedFields.add(INTEGRITY_KEY_FIELD); - tempEncryptedFields.add(INTEGRITY_ALGORITHM_FIELD); - ENCRYPTED_FIELDS = tempEncryptedFields; - } - - private final Map doesNotExist; - private final DynamoDbEncryptionConfiguration encryptionConfiguration; - private final String tableName; - private final DynamoDbClient ddb; - private final DynamoDbEncryptor encryptor; - private final ExtraDataSupplier extraDataSupplier; - - /** - * Provides extra data that should be persisted along with the standard material data. - */ - public interface ExtraDataSupplier { - - /** - * Gets the extra data attributes for the specified material name. - * - * @param materialName material name. - * @param version version number. - * @return plain text of the extra data. - */ - Map getAttributes(final String materialName, final long version); - - /** - * Gets the extra data field names that should be signed only but not encrypted. - * - * @return signed only fields. - */ - Set getSignedOnlyFieldNames(); - } - - /** - * Create a new MetaStore with specified table name. - * - * @param ddb Interface for accessing DynamoDB. - * @param tableName DynamoDB table name for this {@link MetaStore}. - * @param encryptor used to perform crypto operations on the record attributes. - */ - public MetaStore(final DynamoDbClient ddb, final String tableName, - final DynamoDbEncryptor encryptor) { - this(ddb, tableName, encryptor, EMPTY_EXTRA_DATA_SUPPLIER); - } - - /** - * Create a new MetaStore with specified table name and extra data supplier. - * - * @param ddb Interface for accessing DynamoDB. - * @param tableName DynamoDB table name for this {@link MetaStore}. - * @param encryptor used to perform crypto operations on the record attributes - * @param extraDataSupplier provides extra data that should be stored along with the material. - */ - public MetaStore(final DynamoDbClient ddb, final String tableName, - final DynamoDbEncryptor encryptor, final ExtraDataSupplier extraDataSupplier) { - this.ddb = checkNotNull(ddb, "ddb must not be null"); - this.tableName = checkNotNull(tableName, "tableName must not be null"); - this.encryptor = checkNotNull(encryptor, "encryptor must not be null"); - this.extraDataSupplier = checkNotNull(extraDataSupplier, "extraDataSupplier must not be null"); - - final Map tmpExpected = new HashMap<>(); - tmpExpected.put(DEFAULT_HASH_KEY, ExpectedAttributeValue.builder().exists(false).build()); - tmpExpected.put(DEFAULT_RANGE_KEY, ExpectedAttributeValue.builder().exists(false).build()); - doesNotExist = Collections.unmodifiableMap(tmpExpected); - - this.encryptionConfiguration = DynamoDbEncryptionConfiguration.builder() - .encryptionContext(EncryptionContext.builder() - .tableName(this.tableName) - .hashKeyName(DEFAULT_HASH_KEY) - .rangeKeyName(DEFAULT_RANGE_KEY) - .build()) - // All fields default to ENCRYPT_AND_SIGN with 'sign only' fields being explicitly overridden - .defaultEncryptionAction(EncryptionAction.ENCRYPT_AND_SIGN) - .addEncryptionActionOverrides(getSignedOnlyFields(extraDataSupplier).stream() - .collect(Collectors.toMap(Function.identity(), - ignored -> EncryptionAction.SIGN_ONLY))) - .build(); - ; - } - - @Override - public EncryptionMaterialsProvider getProvider(final String materialName, final long version) { - final Map item = getMaterialItem(materialName, version); - return decryptProvider(item); - } - - @Override - public EncryptionMaterialsProvider getOrCreate(final String materialName, final long nextId) { - final Map plaintext = createMaterialItem(materialName, nextId); - final Map ciphertext = conditionalPut(getEncryptedText(plaintext)); - return decryptProvider(ciphertext); - } - - @Override - public long getMaxVersion(final String materialName) { - - final List> items = - ddb.query( - QueryRequest.builder() - .tableName(tableName) - .consistentRead(Boolean.TRUE) - .keyConditions( - Collections.singletonMap( - DEFAULT_HASH_KEY, - Condition.builder() - .comparisonOperator(ComparisonOperator.EQ) - .attributeValueList(AttributeValue.builder().s(materialName).build()) - .build())) - .limit(1) - .scanIndexForward(false) - .attributesToGet(DEFAULT_RANGE_KEY) - .build()) - .items(); - - if (items.isEmpty()) { - return -1L; - } else { - return Long.parseLong(items.get(0).get(DEFAULT_RANGE_KEY).n()); - } - } - - @Override - public long getVersionFromMaterialDescription(final Map description) { - final Matcher m = COMBINED_PATTERN.matcher(description.get(META_ID)); - if (m.matches()) { - return Long.parseLong(m.group(2)); - } else { - throw new IllegalArgumentException("No meta id found"); - } - } - - /** - * This API retrieves the intermediate keys from the source region and replicates it in the target region. - * - * @param materialName material name of the encryption material. - * @param version version of the encryption material. - * @param targetMetaStore target MetaStore where the encryption material to be stored. - */ - public void replicate(final String materialName, final long version, final MetaStore targetMetaStore) { - try { - final Map item = getMaterialItem(materialName, version); - - final Map plainText = getPlainText(item); - final Map encryptedText = targetMetaStore.getEncryptedText(plainText); - final PutItemRequest put = PutItemRequest.builder() - .tableName(targetMetaStore.tableName) - .item(encryptedText) - .expected(doesNotExist) - .build(); - targetMetaStore.ddb.putItem(put); - } catch (ConditionalCheckFailedException e) { - //Item already present. - } - } - - /** - * Creates a DynamoDB Table with the correct properties to be used with a ProviderStore. - * - * @param ddb interface for accessing DynamoDB - * @param tableName name of table that stores the meta data of the material. - * @param provisionedThroughput required provisioned throughput of the this table. - * @return result of create table request. - */ - public static CreateTableResponse createTable(final DynamoDbClient ddb, final String tableName, - final ProvisionedThroughput provisionedThroughput) { - return ddb.createTable( - CreateTableRequest.builder() - .tableName(tableName) - .attributeDefinitions(Arrays.asList( - AttributeDefinition.builder() - .attributeName(DEFAULT_HASH_KEY) - .attributeType(ScalarAttributeType.S) - .build(), - AttributeDefinition.builder() - .attributeName(DEFAULT_RANGE_KEY) - .attributeType(ScalarAttributeType.N).build())) - .keySchema(Arrays.asList( - KeySchemaElement.builder() - .attributeName(DEFAULT_HASH_KEY) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(DEFAULT_RANGE_KEY) - .keyType(KeyType.RANGE) - .build())) - .provisionedThroughput(provisionedThroughput).build()); - } - - private Map getMaterialItem(final String materialName, final long version) { - final Map ddbKey = new HashMap<>(); - ddbKey.put(DEFAULT_HASH_KEY, AttributeValue.builder().s(materialName).build()); - ddbKey.put(DEFAULT_RANGE_KEY, AttributeValue.builder().n(Long.toString(version)).build()); - final Map item = ddbGet(ddbKey); - if (item == null || item.isEmpty()) { - throw new IndexOutOfBoundsException("No material found: " + materialName + "#" + version); - } - return item; - } - - - /** - * Empty extra data supplier. This default class is intended to simplify the default - * implementation of {@link MetaStore}. - */ - private static class EmptyExtraDataSupplier implements ExtraDataSupplier { - @Override - public Map getAttributes(String materialName, long version) { - return Collections.emptyMap(); - } - - @Override - public Set getSignedOnlyFieldNames() { - return Collections.emptySet(); - } - } - - /** - * Get a set of fields that must be signed but not encrypted. - * - * @param extraDataSupplier extra data supplier that is used to return sign only field names. - * @return fields that must be signed. - */ - private static Set getSignedOnlyFields(final ExtraDataSupplier extraDataSupplier) { - final Set signedOnlyFields = extraDataSupplier.getSignedOnlyFieldNames(); - for (final String signedOnlyField : signedOnlyFields) { - if (ENCRYPTED_FIELDS.contains(signedOnlyField)) { - throw new IllegalArgumentException(signedOnlyField + " must be encrypted"); - } - } - - // fields that should not be encrypted - final Set doNotEncryptFields = new HashSet<>(); - doNotEncryptFields.add(DEFAULT_HASH_KEY); - doNotEncryptFields.add(DEFAULT_RANGE_KEY); - doNotEncryptFields.addAll(signedOnlyFields); - return Collections.unmodifiableSet(doNotEncryptFields); - } - - private Map conditionalPut(final Map item) { - try { - final PutItemRequest put = PutItemRequest.builder().tableName(tableName).item(item) - .expected(doesNotExist).build(); - ddb.putItem(put); - return item; - } catch (final ConditionalCheckFailedException ex) { - final Map ddbKey = new HashMap<>(); - ddbKey.put(DEFAULT_HASH_KEY, item.get(DEFAULT_HASH_KEY)); - ddbKey.put(DEFAULT_RANGE_KEY, item.get(DEFAULT_RANGE_KEY)); - return ddbGet(ddbKey); - } - } - - private Map ddbGet(final Map ddbKey) { - return ddb.getItem( - GetItemRequest.builder().tableName(tableName).consistentRead(true) - .key(ddbKey).build()).item(); - } - - /** - * Build an material item for a given material name and version with newly generated - * encryption and integrity keys. - * - * @param materialName material name. - * @param version version of the material. - * @return newly generated plaintext material item. - */ - private Map createMaterialItem(final String materialName, final long version) { - final SecretKeySpec encryptionKey = new SecretKeySpec(Utils.getRandom(32), DEFAULT_ENCRYPTION); - final SecretKeySpec integrityKey = new SecretKeySpec(Utils.getRandom(32), DEFAULT_INTEGRITY); - - final Map plaintext = new HashMap<>(); - plaintext.put(DEFAULT_HASH_KEY, AttributeValue.builder().s(materialName).build()); - plaintext.put(DEFAULT_RANGE_KEY, AttributeValue.builder().n(Long.toString(version)).build()); - plaintext.put(MATERIAL_TYPE_VERSION, AttributeValue.builder().s("0").build()); - plaintext.put(ENCRYPTION_KEY_FIELD, - AttributeValue.builder().b(SdkBytes.fromByteArray(encryptionKey.getEncoded())).build()); - plaintext.put(ENCRYPTION_ALGORITHM_FIELD, AttributeValue.builder().s(encryptionKey.getAlgorithm()).build()); - plaintext.put(INTEGRITY_KEY_FIELD, - AttributeValue.builder().b(SdkBytes.fromByteArray(integrityKey.getEncoded())).build()); - plaintext.put(INTEGRITY_ALGORITHM_FIELD, AttributeValue.builder().s(integrityKey.getAlgorithm()).build()); - plaintext.putAll(extraDataSupplier.getAttributes(materialName, version)); - - return plaintext; - } - - private EncryptionMaterialsProvider decryptProvider(final Map item) { - final Map plaintext = getPlainText(item); - - final String type = plaintext.get(MATERIAL_TYPE_VERSION).s(); - final SecretKey encryptionKey; - final SecretKey integrityKey; - // This switch statement is to make future extensibility easier and more obvious - switch (type) { - case "0": // Only currently supported type - encryptionKey = new SecretKeySpec(plaintext.get(ENCRYPTION_KEY_FIELD).b().asByteArray(), - plaintext.get(ENCRYPTION_ALGORITHM_FIELD).s()); - integrityKey = new SecretKeySpec(plaintext.get(INTEGRITY_KEY_FIELD).b().asByteArray(), plaintext - .get(INTEGRITY_ALGORITHM_FIELD).s()); - break; - default: - throw new IllegalStateException("Unsupported material type: " + type); - } - return new WrappedMaterialsProvider(encryptionKey, encryptionKey, integrityKey, - buildDescription(plaintext)); - } - - /** - * Decrypts attributes in the ciphertext item using {@link DynamoDbEncryptor}. - * except the attribute names specified in doNotEncrypt. - * @param ciphertext the ciphertext to be decrypted. - * @throws SdkClientException when failed to decrypt material item. - * @return decrypted item. - */ - private Map getPlainText(final Map ciphertext) { - return encryptor.decryptRecord(ciphertext, this.encryptionConfiguration); - } - - /** - * Encrypts attributes in the plaintext item using {@link DynamoDbEncryptor}. - * except the attribute names specified in doNotEncrypt. - * - * @throws SdkClientException when failed to encrypt material item. - * @param plaintext plaintext to be encrypted. - */ - private Map getEncryptedText(Map plaintext) { - return encryptor.encryptRecord(plaintext, this.encryptionConfiguration); - } - - private Map buildDescription(final Map plaintext) { - return Collections.singletonMap(META_ID, plaintext.get(DEFAULT_HASH_KEY).s() + "#" - + plaintext.get(DEFAULT_RANGE_KEY).n()); - } - - private static V checkNotNull(final V ref, final String errMsg) { - if (ref == null) { - throw new NullPointerException(errMsg); - } else { - return ref; - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/ProviderStore.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/ProviderStore.java deleted file mode 100644 index a29fe9b3..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/ProviderStore.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.store; - -import java.util.Map; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; - -/** - * Provides a standard way to retrieve and optionally create {@link EncryptionMaterialsProvider}s - * backed by some form of persistent storage. - * - * @author rubin - * - */ -public abstract class ProviderStore { - - /** - * Returns the most recent provider with the specified name. If there are no providers with this - * name, it will create one with version 0. - */ - public EncryptionMaterialsProvider getProvider(final String materialName) { - final long currVersion = getMaxVersion(materialName); - if (currVersion >= 0) { - return getProvider(materialName, currVersion); - } else { - return getOrCreate(materialName, 0); - } - } - - /** - * Returns the provider with the specified name and version. - * - * @throws IndexOutOfBoundsException - * if {@code version} is not a valid version - */ - public abstract EncryptionMaterialsProvider getProvider(final String materialName, final long version); - - /** - * Creates a new provider with a version one greater than the current max version. If multiple - * clients attempt to create a provider with this same version simultaneously, they will - * properly coordinate and the result will be that a single provider is created and that all - * ProviderStores return the same one. - */ - public EncryptionMaterialsProvider newProvider(final String materialName) { - final long nextId = getMaxVersion(materialName) + 1; - return getOrCreate(materialName, nextId); - } - - /** - * Returns the provider with the specified name and version and creates it if it doesn't exist. - * - * @throws UnsupportedOperationException - * if a new provider cannot be created - */ - public EncryptionMaterialsProvider getOrCreate(final String materialName, final long nextId) { - try { - return getProvider(materialName, nextId); - } catch (final IndexOutOfBoundsException ex) { - throw new UnsupportedOperationException("This ProviderStore does not support creation.", ex); - } - } - - /** - * Returns the maximum version number associated with {@code materialName}. If there are no - * versions, returns -1. - */ - public abstract long getMaxVersion(final String materialName); - - /** - * Extracts the material version from {@code description}. - */ - public abstract long getVersionFromMaterialDescription(final Map description); -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/AttributeValueMarshaller.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/AttributeValueMarshaller.java deleted file mode 100644 index e9348af0..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/AttributeValueMarshaller.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import software.amazon.awssdk.core.BytesWrapper; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.core.util.DefaultSdkAutoConstructList; -import software.amazon.awssdk.core.util.DefaultSdkAutoConstructMap; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - - -/** - * @author Greg Rubin - */ -public class AttributeValueMarshaller { - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final int TRUE_FLAG = 1; - private static final int FALSE_FLAG = 0; - - private AttributeValueMarshaller() { - // Prevent instantiation - } - - /** - * Marshalls the data using a TLV (Tag-Length-Value) encoding. The tag may be 'b', 'n', 's', - * '?', '\0' to represent a ByteBuffer, Number, String, Boolean, or Null respectively. The tag - * may also be capitalized (for 'b', 'n', and 's',) to represent an array of that type. If an - * array is stored, then a four-byte big-endian integer is written representing the number of - * array elements. If a ByteBuffer is stored, the length of the buffer is stored as a four-byte - * big-endian integer and the buffer then copied directly. Both Numbers and Strings are treated - * identically and are stored as UTF8 encoded Unicode, proceeded by the length of the encoded - * string (in bytes) as a four-byte big-endian integer. Boolean is encoded as a single byte, 0 - * for false and 1 for true (and so has no Length parameter). The - * Null tag ('\0') takes neither a Length nor a Value parameter. - * - * The tags 'L' and 'M' are for the document types List and Map respectively. These are encoded - * recursively with the Length being the size of the collection. In the case of List, the value - * is a Length number of marshalled AttributeValues. If the case of Map, the value is a Length - * number of AttributeValue Pairs where the first must always have a String value. - * - * This implementation does not recognize loops. If an AttributeValue contains itself - * (even indirectly) this code will recurse infinitely. - * - * @param attributeValue an AttributeValue instance - * @return the serialized AttributeValue - * @see java.io.DataInput - */ - public static ByteBuffer marshall(final AttributeValue attributeValue) { - try (ByteArrayOutputStream resultBytes = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(resultBytes);) { - marshall(attributeValue, out); - out.close(); - resultBytes.close(); - return ByteBuffer.wrap(resultBytes.toByteArray()); - } catch (final IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } - } - - private static void marshall(final AttributeValue attributeValue, final DataOutputStream out) - throws IOException { - - if (attributeValue.b() != null) { - out.writeChar('b'); - writeBytes(attributeValue.b().asByteBuffer(), out); - } else if (hasAttributeValueSet(attributeValue.bs())) { - out.writeChar('B'); - writeBytesList(attributeValue.bs().stream() - .map(BytesWrapper::asByteBuffer).collect(Collectors.toList()), out); - } else if (attributeValue.n() != null) { - out.writeChar('n'); - writeString(trimZeros(attributeValue.n()), out); - } else if (hasAttributeValueSet(attributeValue.ns())) { - out.writeChar('N'); - - final List ns = new ArrayList<>(attributeValue.ns().size()); - for (final String n : attributeValue.ns()) { - ns.add(trimZeros(n)); - } - writeStringList(ns, out); - } else if (attributeValue.s() != null) { - out.writeChar('s'); - writeString(attributeValue.s(), out); - } else if (hasAttributeValueSet(attributeValue.ss())) { - out.writeChar('S'); - writeStringList(attributeValue.ss(), out); - } else if (attributeValue.bool() != null) { - out.writeChar('?'); - out.writeByte((attributeValue.bool() ? TRUE_FLAG : FALSE_FLAG)); - } else if (Boolean.TRUE.equals(attributeValue.nul())) { - out.writeChar('\0'); - } else if (hasAttributeValueSet(attributeValue.l())) { - final List l = attributeValue.l(); - out.writeChar('L'); - out.writeInt(l.size()); - for (final AttributeValue attr : l) { - if (attr == null) { - throw new NullPointerException( - "Encountered null list entry value while marshalling attribute value " - + attributeValue); - } - marshall(attr, out); - } - } else if (hasAttributeValueMap(attributeValue.m())) { - final Map m = attributeValue.m(); - final List mKeys = new ArrayList<>(m.keySet()); - Collections.sort(mKeys); - out.writeChar('M'); - out.writeInt(m.size()); - for (final String mKey : mKeys) { - marshall(AttributeValue.builder().s(mKey).build(), out); - - final AttributeValue mValue = m.get(mKey); - - if (mValue == null) { - throw new NullPointerException( - "Encountered null map value for key " - + mKey - + " while marshalling attribute value " - + attributeValue); - } - marshall(mValue, out); - } - } else { - throw new IllegalArgumentException("A seemingly empty AttributeValue is indicative of invalid input or potential errors"); - } - } - - /** - * @see #marshall(AttributeValue) - */ - public static AttributeValue unmarshall(final ByteBuffer plainText) { - try (final DataInputStream in = new DataInputStream( - new ByteBufferInputStream(plainText.asReadOnlyBuffer()))) { - return unmarshall(in); - } catch (IOException ex) { - // Due to the objects in use, an IOException is not possible. - throw new RuntimeException("Unexpected exception", ex); - } - } - - private static AttributeValue unmarshall(final DataInputStream in) throws IOException { - char type = in.readChar(); - AttributeValue.Builder result = AttributeValue.builder(); - switch (type) { - case '\0': - result.nul(Boolean.TRUE); - break; - case 'b': - result.b(SdkBytes.fromByteBuffer(readBytes(in))); - break; - case 'B': - result.bs(readBytesList(in).stream().map(SdkBytes::fromByteBuffer).collect(Collectors.toList())); - break; - case 'n': - result.n(readString(in)); - break; - case 'N': - result.ns(readStringList(in)); - break; - case 's': - result.s(readString(in)); - break; - case 'S': - result.ss(readStringList(in)); - break; - case '?': - final byte boolValue = in.readByte(); - - if (boolValue == TRUE_FLAG) { - result.bool(Boolean.TRUE); - } else if (boolValue == FALSE_FLAG) { - result.bool(Boolean.FALSE); - } else { - throw new IllegalArgumentException("Improperly formatted data"); - } - break; - case 'L': - final int lCount = in.readInt(); - final List l = new ArrayList<>(lCount); - for (int lIdx = 0; lIdx < lCount; lIdx++) { - l.add(unmarshall(in)); - } - result.l(l); - break; - case 'M': - final int mCount = in.readInt(); - final Map m = new HashMap<>(); - for (int mIdx = 0; mIdx < mCount; mIdx++) { - final AttributeValue key = unmarshall(in); - if (key.s() == null) { - throw new IllegalArgumentException("Improperly formatted data"); - } - AttributeValue value = unmarshall(in); - m.put(key.s(), value); - } - result.m(m); - break; - default: - throw new IllegalArgumentException("Unsupported data encoding"); - } - - return result.build(); - } - - private static String trimZeros(final String n) { - BigDecimal number = new BigDecimal(n); - if (number.compareTo(BigDecimal.ZERO) == 0) { - return "0"; - } - return number.stripTrailingZeros().toPlainString(); - } - - private static void writeStringList(List values, final DataOutputStream out) throws IOException { - final List sorted = new ArrayList<>(values); - Collections.sort(sorted); - out.writeInt(sorted.size()); - for (final String v : sorted) { - writeString(v, out); - } - } - - private static List readStringList(final DataInputStream in) throws IOException, - IllegalArgumentException { - final int nCount = in.readInt(); - List ns = new ArrayList<>(nCount); - for (int nIdx = 0; nIdx < nCount; nIdx++) { - ns.add(readString(in)); - } - return ns; - } - - private static void writeString(String value, final DataOutputStream out) throws IOException { - final byte[] bytes = value.getBytes(UTF8); - out.writeInt(bytes.length); - out.write(bytes); - } - - private static String readString(final DataInputStream in) throws IOException, - IllegalArgumentException { - byte[] bytes; - int length; - length = in.readInt(); - bytes = new byte[length]; - if(in.read(bytes) != length) { - throw new IllegalArgumentException("Improperly formatted data"); - } - return new String(bytes, UTF8); - } - - private static void writeBytesList(List values, final DataOutputStream out) throws IOException { - final List sorted = new ArrayList<>(values); - Collections.sort(sorted); - out.writeInt(sorted.size()); - for (final ByteBuffer v : sorted) { - writeBytes(v, out); - } - } - - private static List readBytesList(final DataInputStream in) throws IOException { - final int bCount = in.readInt(); - List bs = new ArrayList<>(bCount); - for (int bIdx = 0; bIdx < bCount; bIdx++) { - bs.add(readBytes(in)); - } - return bs; - } - - private static void writeBytes(ByteBuffer value, final DataOutputStream out) throws IOException { - value = value.asReadOnlyBuffer(); - value.rewind(); - out.writeInt(value.remaining()); - while (value.hasRemaining()) { - out.writeByte(value.get()); - } - } - - private static ByteBuffer readBytes(final DataInputStream in) throws IOException { - final int length = in.readInt(); - final byte[] buf = new byte[length]; - in.readFully(buf); - return ByteBuffer.wrap(buf); - } - - /** - * Determines if the value of a 'set' type AttributeValue (various S types) has been explicitly set or not. - * @param value the actual value portion of an AttributeValue of the appropriate type - * @return true if the value of this type field has been explicitly set, false if it has not - */ - private static boolean hasAttributeValueSet(Collection value) { - return value != null && value != DefaultSdkAutoConstructList.getInstance(); - } - - /** - * Determines if the value of a 'map' type AttributeValue (M type) has been explicitly set or not. - * @param value the actual value portion of a AttributeValue of the appropriate type - * @return true if the value of this type field has been explicitly set, false if it has not - */ - private static boolean hasAttributeValueMap(Map value) { - return value != null && value != DefaultSdkAutoConstructMap.getInstance(); - } - -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Base64.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Base64.java deleted file mode 100644 index ee94a86a..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Base64.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import static java.util.Base64.*; - -/** - * A class for decoding Base64 strings and encoding bytes as Base64 strings. - */ -public class Base64 { - private static final Decoder DECODER = getMimeDecoder(); - private static final Encoder ENCODER = getEncoder(); - - private Base64() { } - - /** - * Encode the bytes as a Base64 string. - *

- * See the Basic encoder in {@link java.util.Base64} - */ - public static String encodeToString(byte[] bytes) { - return ENCODER.encodeToString(bytes); - } - - /** - * Decode the Base64 string as bytes, ignoring illegal characters. - *

- * See the Mime Decoder in {@link java.util.Base64} - */ - public static byte[] decode(String str) { - if(str == null) { - return null; - } - return DECODER.decode(str); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/ByteBufferInputStream.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/ByteBufferInputStream.java deleted file mode 100644 index ff703068..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/ByteBufferInputStream.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import java.io.InputStream; -import java.nio.ByteBuffer; - -/** - * @author Greg Rubin - */ -public class ByteBufferInputStream extends InputStream { - private final ByteBuffer buffer; - - public ByteBufferInputStream(ByteBuffer buffer) { - this.buffer = buffer; - } - - @Override - public int read() { - if (buffer.hasRemaining()) { - int tmp = buffer.get(); - if (tmp < 0) { - tmp += 256; - } - return tmp; - } else { - return -1; - } - } - - @Override - public int read(byte[] b, int off, int len) { - if (available() < len) { - len = available(); - } - buffer.get(b, off, len); - return len; - } - - @Override - public int available() { - return buffer.remaining(); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Hkdf.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Hkdf.java deleted file mode 100644 index 15422aaa..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Hkdf.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Provider; -import java.util.Arrays; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.SecretKeySpec; - -/** - * HMAC-based Key Derivation Function. - * - * @see RFC 5869 - */ -public final class Hkdf { - private static final byte[] EMPTY_ARRAY = new byte[0]; - private final String algorithm; - private final Provider provider; - - private SecretKey prk = null; - - /** - * Returns an Hkdf object using the specified algorithm. - * - * @param algorithm - * the standard name of the requested MAC algorithm. See the Mac - * section in the Java Cryptography Architecture Standard Algorithm Name - * Documentation for information about standard algorithm - * names. - * @return the new Hkdf object - * @throws NoSuchAlgorithmException - * if no Provider supports a MacSpi implementation for the - * specified algorithm. - */ - public static Hkdf getInstance(final String algorithm) - throws NoSuchAlgorithmException { - // Constructed specifically to sanity-test arguments. - Mac mac = Mac.getInstance(algorithm); - return new Hkdf(algorithm, mac.getProvider()); - } - - /** - * Returns an Hkdf object using the specified algorithm. - * - * @param algorithm - * the standard name of the requested MAC algorithm. See the Mac - * section in the Java Cryptography Architecture Standard Algorithm Name - * Documentation for information about standard algorithm - * names. - * @param provider - * the name of the provider - * @return the new Hkdf object - * @throws NoSuchAlgorithmException - * if a MacSpi implementation for the specified algorithm is not - * available from the specified provider. - * @throws NoSuchProviderException - * if the specified provider is not registered in the security - * provider list. - */ - public static Hkdf getInstance(final String algorithm, final String provider) - throws NoSuchAlgorithmException, NoSuchProviderException { - // Constructed specifically to sanity-test arguments. - Mac mac = Mac.getInstance(algorithm, provider); - return new Hkdf(algorithm, mac.getProvider()); - } - - /** - * Returns an Hkdf object using the specified algorithm. - * - * @param algorithm - * the standard name of the requested MAC algorithm. See the Mac - * section in the Java Cryptography Architecture Standard Algorithm Name - * Documentation for information about standard algorithm - * names. - * @param provider - * the provider - * @return the new Hkdf object - * @throws NoSuchAlgorithmException - * if a MacSpi implementation for the specified algorithm is not - * available from the specified provider. - */ - public static Hkdf getInstance(final String algorithm, - final Provider provider) throws NoSuchAlgorithmException { - // Constructed specifically to sanity-test arguments. - Mac mac = Mac.getInstance(algorithm, provider); - return new Hkdf(algorithm, mac.getProvider()); - } - - /** - * Initializes this Hkdf with input keying material. A default salt of - * HashLen zeros will be used (where HashLen is the length of the return - * value of the supplied algorithm). - * - * @param ikm - * the Input Keying Material - */ - public void init(final byte[] ikm) { - init(ikm, null); - } - - /** - * Initializes this Hkdf with input keying material and a salt. If - * salt is null or of length 0, then a default salt of - * HashLen zeros will be used (where HashLen is the length of the return - * value of the supplied algorithm). - * - * @param salt - * the salt used for key extraction (optional) - * @param ikm - * the Input Keying Material - */ - public void init(final byte[] ikm, final byte[] salt) { - byte[] realSalt = (salt == null) ? EMPTY_ARRAY : salt.clone(); - byte[] rawKeyMaterial = EMPTY_ARRAY; - try { - Mac extractionMac = Mac.getInstance(algorithm, provider); - if (realSalt.length == 0) { - realSalt = new byte[extractionMac.getMacLength()]; - Arrays.fill(realSalt, (byte) 0); - } - extractionMac.init(new SecretKeySpec(realSalt, algorithm)); - rawKeyMaterial = extractionMac.doFinal(ikm); - SecretKeySpec key = new SecretKeySpec(rawKeyMaterial, algorithm); - Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array - unsafeInitWithoutKeyExtraction(key); - } catch (GeneralSecurityException e) { - // We've already checked all of the parameters so no exceptions - // should be possible here. - throw new RuntimeException("Unexpected exception", e); - } finally { - Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array - } - } - - /** - * Initializes this Hkdf to use the provided key directly for creation of - * new keys. If rawKey is not securely generated and uniformly - * distributed over the total key-space, then this will result in an - * insecure key derivation function (KDF). DO NOT USE THIS UNLESS YOU - * ARE ABSOLUTELY POSITIVE THIS IS THE CORRECT THING TO DO. - * - * @param rawKey - * the pseudorandom key directly used to derive keys - * @throws InvalidKeyException - * if the algorithm for rawKey does not match the - * algorithm this Hkdf was created with - */ - public void unsafeInitWithoutKeyExtraction(final SecretKey rawKey) - throws InvalidKeyException { - if (!rawKey.getAlgorithm().equals(algorithm)) { - throw new InvalidKeyException( - "Algorithm for the provided key must match the algorithm for this Hkdf. Expected " + - algorithm + " but found " + rawKey.getAlgorithm()); - } - - this.prk = rawKey; - } - - private Hkdf(final String algorithm, final Provider provider) { - if (!algorithm.startsWith("Hmac")) { - throw new IllegalArgumentException("Invalid algorithm " + algorithm - + ". Hkdf may only be used with Hmac algorithms."); - } - this.algorithm = algorithm; - this.provider = provider; - } - - /** - * Returns a pseudorandom key of length bytes. - * - * @param info - * optional context and application specific information (can be - * a zero-length string). This will be treated as UTF-8. - * @param length - * the length of the output key in bytes - * @return a pseudorandom key of length bytes. - * @throws IllegalStateException - * if this object has not been initialized - */ - public byte[] deriveKey(final String info, final int length) throws IllegalStateException { - return deriveKey((info != null ? info.getBytes(StandardCharsets.UTF_8) : null), length); - } - - /** - * Returns a pseudorandom key of length bytes. - * - * @param info - * optional context and application specific information (can be - * a zero-length array). - * @param length - * the length of the output key in bytes - * @return a pseudorandom key of length bytes. - * @throws IllegalStateException - * if this object has not been initialized - */ - public byte[] deriveKey(final byte[] info, final int length) throws IllegalStateException { - byte[] result = new byte[length]; - try { - deriveKey(info, length, result, 0); - } catch (ShortBufferException ex) { - // This exception is impossible as we ensure the buffer is long - // enough - throw new RuntimeException(ex); - } - return result; - } - - /** - * Derives a pseudorandom key of length bytes and stores the - * result in output. - * - * @param info - * optional context and application specific information (can be - * a zero-length array). - * @param length - * the length of the output key in bytes - * @param output - * the buffer where the pseudorandom key will be stored - * @param offset - * the offset in output where the key will be stored - * @throws ShortBufferException - * if the given output buffer is too small to hold the result - * @throws IllegalStateException - * if this object has not been initialized - */ - public void deriveKey(final byte[] info, final int length, - final byte[] output, final int offset) throws ShortBufferException, - IllegalStateException { - assertInitialized(); - if (length < 0) { - throw new IllegalArgumentException("Length must be a non-negative value."); - } - if (output.length < offset + length) { - throw new ShortBufferException(); - } - Mac mac = createMac(); - - if (length > 255 * mac.getMacLength()) { - throw new IllegalArgumentException( - "Requested keys may not be longer than 255 times the underlying HMAC length."); - } - - byte[] t = EMPTY_ARRAY; - try { - int loc = 0; - byte i = 1; - while (loc < length) { - mac.update(t); - mac.update(info); - mac.update(i); - t = mac.doFinal(); - - for (int x = 0; x < t.length && loc < length; x++, loc++) { - output[loc] = t[x]; - } - - i++; - } - } finally { - Arrays.fill(t, (byte) 0); // Zeroize temporary array - } - } - - private Mac createMac() { - try { - Mac mac = Mac.getInstance(algorithm, provider); - mac.init(prk); - return mac; - } catch (NoSuchAlgorithmException ex) { - // We've already validated that this algorithm is correct. - throw new RuntimeException(ex); - } catch (InvalidKeyException ex) { - // We've already validated that this key is correct. - throw new RuntimeException(ex); - } - } - - /** - * Throws an IllegalStateException if this object has not been - * initialized. - * - * @throws IllegalStateException - * if this object has not been initialized - */ - private void assertInitialized() throws IllegalStateException { - if (prk == null) { - throw new IllegalStateException("Hkdf has not been initialized"); - } - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/LRUCache.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/LRUCache.java deleted file mode 100644 index 22a4d35e..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/LRUCache.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import software.amazon.awssdk.annotations.ThreadSafe; - -/** - * A bounded cache that has a LRU eviction policy when the cache is full. - * - * @param - * value type - */ -@ThreadSafe -public final class LRUCache { - /** - * Used for the internal cache. - */ - private final Map map; - /** - * Listener for cache entry eviction. - */ - private final RemovalListener listener; - /** - * Maximum size of the cache. - */ - private final int maxSize; - - /** - * @param maxSize - * the maximum number of entries of the cache - * @param listener - * object which is notified immediately prior to the removal of - * any objects from the cache - */ - public LRUCache(final int maxSize, final RemovalListener listener) { - if (maxSize < 1) { - throw new IllegalArgumentException("maxSize " + maxSize + " must be at least 1"); - } - this.maxSize = maxSize; - this.listener = listener; - map = Collections.synchronizedMap(new LRUHashMap(maxSize, listener)); - } - - /** - * @param maxSize - * the maximum number of entries of the cache - */ - public LRUCache(final int maxSize) { - this(maxSize, null); - } - - /** - * Adds an entry to the cache, evicting the earliest entry if necessary. - */ - public T add(final String key, final T value) { - return map.put(key, value); - } - - /** Returns the value of the given key; or null of no such entry exists. */ - public T get(final String key) { - return map.get(key); - } - - /** - * Returns the current size of the cache. - */ - public int size() { - return map.size(); - } - - /** - * Returns the maximum size of the cache. - */ - public int getMaxSize() { - return maxSize; - } - - public void clear() { - // The more complicated logic is to ensure that the listener is - // actually called for all entries. - if (listener != null) { - List> removedEntries = new ArrayList>(); - synchronized (map) { - Iterator> it = map.entrySet().iterator(); - while(it.hasNext()) { - removedEntries.add(it.next()); - it.remove(); - } - } - for (Entry entry : removedEntries) { - listener.onRemoval(entry); - } - } else { - map.clear(); - } - } - - @Override - public String toString() { - return map.toString(); - } - - @SuppressWarnings("serial") - private static class LRUHashMap extends LinkedHashMap { - private final int maxSize; - private final RemovalListener listener; - - private LRUHashMap(final int maxSize, final RemovalListener listener) { - super(10, 0.75F, true); - this.maxSize = maxSize; - this.listener = listener; - } - - @Override - protected boolean removeEldestEntry(final Entry eldest) { - if (size() > maxSize) { - if (listener != null) { - listener.onRemoval(eldest); - } - return true; - } - return false; - } - } - - public interface RemovalListener { - void onRemoval(Entry entry); - } -} diff --git a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Utils.java b/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Utils.java deleted file mode 100644 index 6d092cc0..00000000 --- a/sdk2/src/main/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/Utils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import java.security.SecureRandom; - -public class Utils { - private static final ThreadLocal RND = ThreadLocal.withInitial(() -> { - final SecureRandom result = new SecureRandom(); - result.nextBoolean(); // Force seeding - return result; - }); - - private Utils() { - // Prevent instantiation - } - - public static SecureRandom getRng() { - return RND.get(); - } - - public static byte[] getRandom(int len) { - final byte[] result = new byte[len]; - getRng().nextBytes(result); - return result; - } -} diff --git a/sdk2/src/test/java/log4j.properties b/sdk2/src/test/java/log4j.properties deleted file mode 100644 index ad0e4b0a..00000000 --- a/sdk2/src/test/java/log4j.properties +++ /dev/null @@ -1,13 +0,0 @@ -log4j.rootLogger=INFO, A1 -log4j.appender.A1=org.apache.log4j.ConsoleAppender -log4j.appender.A1.layout=org.apache.log4j.PatternLayout - -# Print the date in ISO 8601 format -log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n - -# Adjust to see more / less logging -#log4j.logger.httpclient.wire=TRACE -log4j.logger.com.amazonaws=DEBUG - -# HttpClient 4 Wire Logging -# log4j.logger.org.apache.http.wire=DEBUG \ No newline at end of file diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedEncryptionTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedEncryptionTest.java deleted file mode 100644 index f11de383..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedEncryptionTest.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; - -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.crypto.spec.SecretKeySpec; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.SymmetricStaticProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.AttrMatcher; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.EncryptionTestHelper; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.TestDelegatedKey; - -public class DelegatedEncryptionTest { - private static SecretKeySpec rawEncryptionKey; - private static SecretKeySpec rawMacKey; - private static DelegatedKey encryptionKey; - private static DelegatedKey macKey; - - private EncryptionMaterialsProvider prov; - private DynamoDbEncryptor encryptor; - private Map attribs; - private EncryptionContext context; - - @BeforeClass - public static void setupClass() { - rawEncryptionKey = new SecretKeySpec(Utils.getRandom(32), "AES"); - encryptionKey = new TestDelegatedKey(rawEncryptionKey); - - rawMacKey = new SecretKeySpec(Utils.getRandom(32), "HmacSHA256"); - macKey = new TestDelegatedKey(rawMacKey); - } - - @BeforeMethod - public void setUp() { - prov = new SymmetricStaticProvider(encryptionKey, macKey, - Collections.emptyMap()); - encryptor = new DynamoDbEncryptor(prov, "encryptor-"); - - attribs = new HashMap<>(); - attribs.put("intValue", AttributeValue.builder().n("123").build()); - attribs.put("stringValue", AttributeValue.builder().s("Hello world!").build()); - attribs.put("byteArrayValue", - AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] {0, 1, 2, 3, 4, 5})).build()); - attribs.put("stringSet", AttributeValue.builder().ss("Goodbye", "Cruel", "World", "?").build()); - attribs.put("intSet", AttributeValue.builder().ns("1", "200", "10", "15", "0").build()); - attribs.put("hashKey", AttributeValue.builder().n("5").build()); - attribs.put("rangeKey", AttributeValue.builder().n("7").build()); - attribs.put("version", AttributeValue.builder().n("0").build()); - - context = EncryptionContext.builder() - .tableName("TableName") - .hashKeyName("hashKey") - .rangeKeyName("rangeKey") - .build(); - } - - @Test - public void testSetSignatureFieldName() { - assertNotNull(encryptor.getSignatureFieldName()); - encryptor.setSignatureFieldName("A different value"); - assertEquals("A different value", encryptor.getSignatureFieldName()); - } - - @Test - public void testSetMaterialDescriptionFieldName() { - assertNotNull(encryptor.getMaterialDescriptionFieldName()); - encryptor.setMaterialDescriptionFieldName("A different value"); - assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); - } - - @Test - public void fullEncryption() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").s()); - assertNotNull(encryptedAttributes.get("stringValue").b()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void fullEncryptionBadSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(modifiedEncryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void badVersionNumber() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - byte[] rawArray = encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).b().asByteArray(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getMaterialDescriptionFieldName(), - AttributeValue.builder().b(SdkBytes.fromByteArray(rawArray)).build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - } - - @Test - public void signedOnly() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() { - prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); - encryptor = new DynamoDbEncryptor(prov, "encryptor-"); - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void signedOnlyBadSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void signedOnlyNoSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - } - - @Test - public void RsaSignedOnly() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new DynamoDbEncryptor( - new SymmetricStaticProvider(encryptionKey, sigPair, Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new DynamoDbEncryptor( - new SymmetricStaticProvider(encryptionKey, sigPair, Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - assertEquals(o1.b(), o2.b()); - assertSetsEqual(o1.bs(), o2.bs()); - assertEquals(o1.n(), o2.n()); - assertSetsEqual(o1.ns(), o2.ns()); - assertEquals(o1.s(), o2.s()); - assertSetsEqual(o1.ss(), o2.ss()); - } - - private void assertSetsEqual(Collection c1, Collection c2) { - assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet<>(c1); - Set s2 = new HashSet<>(c2); - assertEquals(s1, s2); - } - } - -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedEnvelopeEncryptionTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedEnvelopeEncryptionTest.java deleted file mode 100644 index a403db97..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DelegatedEnvelopeEncryptionTest.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; - -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.crypto.spec.SecretKeySpec; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.SymmetricStaticProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.WrappedMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.AttrMatcher; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.EncryptionTestHelper; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.TestDelegatedKey; - -public class DelegatedEnvelopeEncryptionTest { - private static SecretKeySpec rawEncryptionKey; - private static SecretKeySpec rawMacKey; - private static DelegatedKey encryptionKey; - private static DelegatedKey macKey; - - private EncryptionMaterialsProvider prov; - private DynamoDbEncryptor encryptor; - private Map attribs; - private EncryptionContext context; - - @BeforeClass - public static void setupClass() { - rawEncryptionKey = new SecretKeySpec(Utils.getRandom(32), "AES"); - encryptionKey = new TestDelegatedKey(rawEncryptionKey); - - rawMacKey = new SecretKeySpec(Utils.getRandom(32), "HmacSHA256"); - macKey = new TestDelegatedKey(rawMacKey); - } - - @BeforeMethod - public void setUp() { - prov = new WrappedMaterialsProvider(encryptionKey, encryptionKey, macKey, Collections.emptyMap()); - encryptor = new DynamoDbEncryptor(prov, "encryptor-"); - - attribs = new HashMap<>(); - attribs.put("intValue", AttributeValue.builder().n("123").build()); - attribs.put("stringValue", AttributeValue.builder().s("Hello world!").build()); - attribs.put("byteArrayValue", - AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[]{0, 1, 2, 3, 4, 5})).build()); - attribs.put("stringSet",AttributeValue.builder().ss("Goodbye", "Cruel", "World", "?").build()); - attribs.put("intSet", AttributeValue.builder().ns("1", "200", "10", "15", "0").build()); - attribs.put("hashKey", AttributeValue.builder().n("5").build()); - attribs.put("rangeKey", AttributeValue.builder().n("7").build()); - attribs.put("version", AttributeValue.builder().n("0").build()); - - context = EncryptionContext.builder() - .tableName("TableName") - .hashKeyName("hashKey") - .rangeKeyName("rangeKey") - .build(); - } - - @Test - public void testSetSignatureFieldName() { - assertNotNull(encryptor.getSignatureFieldName()); - encryptor.setSignatureFieldName("A different value"); - assertEquals("A different value", encryptor.getSignatureFieldName()); - } - - @Test - public void testSetMaterialDescriptionFieldName() { - assertNotNull(encryptor.getMaterialDescriptionFieldName()); - encryptor.setMaterialDescriptionFieldName("A different value"); - assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); - } - - @Test - public void fullEncryption() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").s()); - assertNotNull(encryptedAttributes.get("stringValue").b()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void fullEncryptionBadSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(modifiedEncryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void badVersionNumber() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - byte[] rawArray = encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).b().asByteArray(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getMaterialDescriptionFieldName(), - AttributeValue.builder().b(SdkBytes.fromByteArray(rawArray)).build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - } - - @Test - public void signedOnly() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() { - prov = new SymmetricStaticProvider(null, macKey, Collections.emptyMap()); - encryptor = new DynamoDbEncryptor(prov, "encryptor-"); - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void signedOnlyBadSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void signedOnlyNoSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - } - - @Test - public void RsaSignedOnly() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new DynamoDbEncryptor( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new DynamoDbEncryptor( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - assertEquals(o1.b(), o2.b()); - assertSetsEqual(o1.bs(), o2.bs()); - assertEquals(o1.n(), o2.n()); - assertSetsEqual(o1.ns(), o2.ns()); - assertEquals(o1.s(), o2.s()); - assertSetsEqual(o1.ss(), o2.ss()); - } - - private void assertSetsEqual(Collection c1, Collection c2) { - assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet<>(c1); - Set s2 = new HashSet<>(c2); - assertEquals(s1, s2); - } - } - -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbEncryptorTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbEncryptorTest.java deleted file mode 100644 index 8acbcabc..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbEncryptorTest.java +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContextOperators.overrideEncryptionContextTableName; - -import java.lang.reflect.Method; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Security; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.bouncycastle.jce.ECNamedCurveTable; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.DynamoDbEncryptionConfiguration; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.EncryptionAction; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.SymmetricStaticProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.AttrMatcher; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.EncryptionTestHelper; - -public class DynamoDbEncryptorTest { - private static SecretKey encryptionKey; - private static SecretKey macKey; - - private InstrumentedEncryptionMaterialsProvider prov; - private DynamoDbEncryptor encryptor; - private Map attribs; - private EncryptionContext context; - private static final String OVERRIDDEN_TABLE_NAME = "TheBestTableName"; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - prov = new InstrumentedEncryptionMaterialsProvider( - new SymmetricStaticProvider(encryptionKey, macKey, - Collections.emptyMap())); - encryptor = new DynamoDbEncryptor(prov, "encryptor-"); - - attribs = new HashMap<>(); - attribs.put("intValue", AttributeValue.builder().n("123").build()); - attribs.put("stringValue", AttributeValue.builder().s("Hello world!").build()); - attribs.put("byteArrayValue", - AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] {0, 1, 2, 3, 4, 5})).build()); - attribs.put("stringSet", AttributeValue.builder().ss("Goodbye", "Cruel", "World", "?").build()); - attribs.put("intSet", AttributeValue.builder().ns("1", "200", "10", "15", "0").build()); - attribs.put("hashKey", AttributeValue.builder().n("5").build()); - attribs.put("rangeKey", AttributeValue.builder().n("7").build()); - attribs.put("version", AttributeValue.builder().n("0").build()); - - // New(er) data types - attribs.put("booleanTrue", AttributeValue.builder().bool(true).build()); - attribs.put("booleanFalse", AttributeValue.builder().bool(false).build()); - attribs.put("nullValue", AttributeValue.builder().nul(true).build()); - Map tmpMap = new HashMap<>(attribs); - attribs.put("listValue", AttributeValue.builder().l( - AttributeValue.builder().s("I'm a string").build(), - AttributeValue.builder().n("42").build(), - AttributeValue.builder().s("Another string").build(), - AttributeValue.builder().ns("1", "4", "7").build(), - AttributeValue.builder().m(tmpMap).build(), - AttributeValue.builder().l( - AttributeValue.builder().n("123").build(), - AttributeValue.builder().ns("1", "200", "10", "15", "0").build(), - AttributeValue.builder().ss("Goodbye", "Cruel", "World", "!").build() - ).build()).build()); - tmpMap = new HashMap<>(); - tmpMap.put("another string", AttributeValue.builder().s("All around the cobbler's bench").build()); - tmpMap.put("next line", AttributeValue.builder().ss("the monkey", "chased", "the weasel").build()); - tmpMap.put("more lyrics", AttributeValue.builder().l( - AttributeValue.builder().s("the monkey").build(), - AttributeValue.builder().s("thought twas").build(), - AttributeValue.builder().s("all in fun").build() - ).build()); - tmpMap.put("weasel", AttributeValue.builder().m(Collections.singletonMap("pop", AttributeValue.builder().bool(true).build())).build()); - attribs.put("song", AttributeValue.builder().m(tmpMap).build()); - - - context = EncryptionContext.builder() - .tableName("TableName") - .hashKeyName("hashKey") - .rangeKeyName("rangeKey") - .build(); - } - - @Test - public void testSetSignatureFieldName() { - assertNotNull(encryptor.getSignatureFieldName()); - encryptor.setSignatureFieldName("A different value"); - assertEquals("A different value", encryptor.getSignatureFieldName()); - } - - @Test - public void testSetMaterialDescriptionFieldName() { - assertNotNull(encryptor.getMaterialDescriptionFieldName()); - encryptor.setMaterialDescriptionFieldName("A different value"); - assertEquals("A different value", encryptor.getMaterialDescriptionFieldName()); - } - - @Test - public void fullEncryption() { - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, - Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - - Map decryptedAttributes = EncryptionTestHelper.decryptAllFieldsExcept(encryptor, - Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").s()); - assertNotNull(encryptedAttributes.get("stringValue").b()); - - // Make sure we're calling the proper getEncryptionMaterials method - assertEquals("Wrong getEncryptionMaterials() called", - 1, prov.getCallCount("getEncryptionMaterials(EncryptionContext context)")); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void encryptWithDefaultEncryptionActionOfNullWithKeyOverridesThrowsIllegalArgumentException() { - DynamoDbEncryptionConfiguration configuration = DynamoDbEncryptionConfiguration.builder() - .addEncryptionActionOverride("hashKey", EncryptionAction.SIGN_ONLY) - .addEncryptionActionOverride("rangeKey", EncryptionAction.SIGN_ONLY) - .addEncryptionActionOverride("version", EncryptionAction.SIGN_ONLY) - .encryptionContext(context) - .build(); - - encryptor.encryptRecord(Collections.unmodifiableMap(attribs), configuration); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void decryptWithDefaultEncryptionActionOfNullWithKeyOverridesThrowsIllegalArgumentException() { - DynamoDbEncryptionConfiguration configuration = DynamoDbEncryptionConfiguration.builder() - .addEncryptionActionOverride("hashKey", EncryptionAction.SIGN_ONLY) - .addEncryptionActionOverride("rangeKey", EncryptionAction.SIGN_ONLY) - .addEncryptionActionOverride("version", EncryptionAction.SIGN_ONLY) - .encryptionContext(context) - .build(); - - encryptor.decryptRecord(Collections.unmodifiableMap(attribs), configuration); - } - - @Test - public void defaultEncryptionActionOfSignAndEncryptWithKeyOverrides() { - DynamoDbEncryptionConfiguration configuration = DynamoDbEncryptionConfiguration.builder() - .defaultEncryptionAction(EncryptionAction.ENCRYPT_AND_SIGN) - .addEncryptionActionOverride("hashKey", EncryptionAction.SIGN_ONLY) - .addEncryptionActionOverride("rangeKey", EncryptionAction.SIGN_ONLY) - .addEncryptionActionOverride("version", EncryptionAction.SIGN_ONLY) - .encryptionContext(context) - .build(); - - Map encryptedAttributes = - encryptor.encryptRecord(Collections.unmodifiableMap(attribs), configuration); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptRecord(Collections.unmodifiableMap(encryptedAttributes), configuration); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has been encrypted (we'll assume the others are correct as well) - assertTrue(encryptedAttributes.containsKey("stringValue")); - assertNull(encryptedAttributes.get("stringValue").s()); - assertNotNull(encryptedAttributes.get("stringValue").b()); - - // Make sure we're calling the proper getEncryptionMaterials method - assertEquals("Wrong getEncryptionMaterials() called", - 1, prov.getCallCount("getEncryptionMaterials(EncryptionContext context)")); - } - - @Test - public void defaultEncryptionActionOfSignOnlyWithNoOverrides() { - DynamoDbEncryptionConfiguration configuration = DynamoDbEncryptionConfiguration.builder() - .defaultEncryptionAction(EncryptionAction.SIGN_ONLY) - .encryptionContext(context) - .build(); - - Map encryptedAttributes = - encryptor.encryptRecord(Collections.unmodifiableMap(attribs), configuration); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - encryptor.decryptRecord(Collections.unmodifiableMap(encryptedAttributes), configuration); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Verify that nothing actually got encrypted - Map copyOfEncryptedAttributes = new HashMap<>(encryptedAttributes); - copyOfEncryptedAttributes.remove(encryptor.getMaterialDescriptionFieldName()); - copyOfEncryptedAttributes.remove(encryptor.getSignatureFieldName()); - assertThat(copyOfEncryptedAttributes, AttrMatcher.match(attribs)); - } - - @Test - public void defaultEncryptionActionOfIgnoreWithNoOverrides() { - DynamoDbEncryptionConfiguration configuration = DynamoDbEncryptionConfiguration.builder() - .defaultEncryptionAction(EncryptionAction.DO_NOTHING) - .encryptionContext(context) - .build(); - - Map encryptedAttributes = - new HashMap<>(encryptor.encryptRecord(Collections.unmodifiableMap(attribs), configuration)); - - // Verify that nothing actually got encrypted - Map copyOfEncryptedAttributes = new HashMap<>(encryptedAttributes); - copyOfEncryptedAttributes.remove(encryptor.getMaterialDescriptionFieldName()); - copyOfEncryptedAttributes.remove(encryptor.getSignatureFieldName()); - assertThat(copyOfEncryptedAttributes, AttrMatcher.match(attribs)); - - // Now modify one of the attributes and decrypt to prove that it was not signed - Map copyOfAttributes = new HashMap<>(attribs); - encryptedAttributes.put("stringValue", AttributeValue.builder().s("Goodbye world!").build()); - copyOfAttributes.put("stringValue", AttributeValue.builder().s("Goodbye world!").build()); - Map decryptedAttributes = encryptor.decryptRecord(encryptedAttributes, configuration); - assertThat(decryptedAttributes, AttrMatcher.match(copyOfAttributes)); - } - - @Test - public void ensureEncryptedAttributesUnmodified() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - String encryptedString = encryptedAttributes.toString(); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - - assertEquals(encryptedString, encryptedAttributes.toString()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void fullEncryptionBadSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(modifiedEncryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - } - - @Test(expectedExceptions =IllegalArgumentException.class) - public void badVersionNumber() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(attribs), context, asList("hashKey", "rangeKey", "version")); - byte[] rawArray = encryptedAttributes.get(encryptor.getMaterialDescriptionFieldName()).b().asByteArray(); - assertEquals(0, rawArray[0]); // This will need to be kept in sync with the current version. - rawArray[0] = 100; - encryptedAttributes.put(encryptor.getMaterialDescriptionFieldName(), - AttributeValue.builder().b(SdkBytes.fromByteArray(rawArray)).build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, Collections.unmodifiableMap(encryptedAttributes), context, asList("hashKey", "rangeKey", "version")); - } - - @Test - public void signedOnly() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test - public void signedOnlyNullCryptoKey() { - prov = new InstrumentedEncryptionMaterialsProvider( - new SymmetricStaticProvider(null, macKey, Collections.emptyMap())); - encryptor = new DynamoDbEncryptor(prov, "encryptor-"); - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void signedOnlyBadSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void signedOnlyNoSignature() { - Map encryptedAttributes = - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - } - - @Test - public void RsaSignedOnly() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new DynamoDbEncryptor( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void RsaSignedOnlyBadSignature() throws GeneralSecurityException { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - encryptor = new DynamoDbEncryptor( - new SymmetricStaticProvider(encryptionKey, sigPair, - Collections.emptyMap()), "encryptor-"); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - /** - * Tests that no exception is thrown when the encryption context override operator is null - */ - @Test - public void testNullEncryptionContextOperator() { - DynamoDbEncryptor encryptor = new DynamoDbEncryptor(prov); - encryptor.setEncryptionContextOverrideOperator(null); - EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, Collections.emptyList()); - } - - /** - * Tests decrypt and encrypt with an encryption context override operator - */ - @Test - public void testTableNameOverriddenEncryptionContextOperator() { - // Ensure that the table name is different from what we override the table to. - assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); - DynamoDbEncryptor encryptor = new DynamoDbEncryptor(prov); - encryptor.setEncryptionContextOverrideOperator(overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); - Map encryptedItems = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, Collections.emptyList()); - Map decryptedItems = EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedItems, context, Collections.emptyList()); - assertThat(decryptedItems, AttrMatcher.match(attribs)); - } - - - /** - * Tests encrypt with an encryption context override operator, and a second encryptor without an override - */ - @Test - public void testTableNameOverriddenEncryptionContextOperatorWithSecondEncryptor() { - // Ensure that the table name is different from what we override the table to. - assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); - DynamoDbEncryptor encryptor = new DynamoDbEncryptor(prov); - DynamoDbEncryptor encryptorWithoutOverride = new DynamoDbEncryptor(prov); - encryptor.setEncryptionContextOverrideOperator(overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); - Map encryptedItems = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, Collections.emptyList()); - - EncryptionContext expectedOverriddenContext = context.toBuilder().tableName("TheBestTableName").build(); - Map decryptedItems = - EncryptionTestHelper.decryptAllFieldsExcept(encryptorWithoutOverride, encryptedItems, expectedOverriddenContext, Collections.emptyList()); - assertThat(decryptedItems, AttrMatcher.match(attribs)); - } - - /** - * Tests encrypt with an encryption context override operator, and a second encryptor without an override - */ - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void testTableNameOverriddenEncryptionContextOperatorWithSecondEncryptorButTheOriginalEncryptionContext() { - // Ensure that the table name is different from what we override the table to. - assertThat(context.getTableName(), not(equalTo(OVERRIDDEN_TABLE_NAME))); - DynamoDbEncryptor encryptor = new DynamoDbEncryptor(prov); - DynamoDbEncryptor encryptorWithoutOverride = new DynamoDbEncryptor(prov); - encryptor.setEncryptionContextOverrideOperator(overrideEncryptionContextTableName(context.getTableName(), OVERRIDDEN_TABLE_NAME)); - Map encryptedItems = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, Collections.emptyList()); - - // Use the original encryption context, and expect a signature failure - EncryptionTestHelper.decryptAllFieldsExcept(encryptorWithoutOverride, encryptedItems, context, Collections.emptyList()); - } - - @Test - public void EcdsaSignedOnly() throws GeneralSecurityException { - encryptor = new DynamoDbEncryptor(getMaterialProviderwithECDSA()); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map decryptedAttributes = - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, encryptedAttributes, context, attribs.keySet()); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - - // Make sure keys and version are not encrypted - assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey")); - assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey")); - assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version")); - - // Make sure String has not been encrypted (we'll assume the others are correct as well) - assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue")); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void EcdsaSignedOnlyBadSignature() throws GeneralSecurityException { - - encryptor = new DynamoDbEncryptor(getMaterialProviderwithECDSA()); - - Map encryptedAttributes = EncryptionTestHelper.encryptAllFieldsExcept(encryptor, attribs, context, attribs.keySet()); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - Map modifiedEncryptedAttributes = new HashMap<>(encryptedAttributes); - modifiedEncryptedAttributes.put("hashKey", AttributeValue.builder().n("666").build()); - EncryptionTestHelper.decryptAllFieldsExcept(encryptor, modifiedEncryptedAttributes, context, attribs.keySet()); - } - - @Test - public void toByteArray() throws ReflectiveOperationException { - final byte[] expected = new byte[] {0, 1, 2, 3, 4, 5}; - assertToByteArray("Wrap", expected, ByteBuffer.wrap(expected)); - assertToByteArray("Wrap-RO", expected, ByteBuffer.wrap(expected).asReadOnlyBuffer()); - - assertToByteArray("Wrap-Truncated-Sliced", expected, ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 6).slice()); - assertToByteArray("Wrap-Offset-Sliced", expected, ByteBuffer.wrap(new byte[] {6, 0, 1, 2, 3, 4, 5, 6}, 1, 6).slice()); - assertToByteArray("Wrap-Truncated", expected, ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 6)); - assertToByteArray("Wrap-Offset", expected, ByteBuffer.wrap(new byte[] {6, 0, 1, 2, 3, 4, 5, 6}, 1, 6)); - - ByteBuffer buff = ByteBuffer.allocate(expected.length + 10); - buff.put(expected); - buff.flip(); - assertToByteArray("Normal", expected, buff); - - buff = ByteBuffer.allocateDirect(expected.length + 10); - buff.put(expected); - buff.flip(); - assertToByteArray("Direct", expected, buff); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void encryptWithNullAttributeValuesThrowsIllegalArgumentException() { - encryptor.encryptRecord(null, DynamoDbEncryptionConfiguration.builder().encryptionContext(context).build()); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void encryptWithNullEncryptionContextThrowsIllegalArgumentException() { - encryptor.encryptRecord(attribs, DynamoDbEncryptionConfiguration.builder().build()); - } - - private void assertToByteArray(final String msg, final byte[] expected, final ByteBuffer testValue) throws ReflectiveOperationException { - Method m = DynamoDbEncryptor.class.getDeclaredMethod("toByteArray", ByteBuffer.class); - m.setAccessible(true); - - int oldPosition = testValue.position(); - int oldLimit = testValue.limit(); - - assertThat(m.invoke(null, testValue), is(expected)); - assertEquals(msg + ":Position", oldPosition, testValue.position()); - assertEquals(msg + ":Limit", oldLimit, testValue.limit()); - } - - private void assertAttrEquals(AttributeValue o1, AttributeValue o2) { - assertEquals(o1.b(), o2.b()); - assertSetsEqual(o1.bs(), o2.bs()); - assertEquals(o1.n(), o2.n()); - assertSetsEqual(o1.ns(), o2.ns()); - assertEquals(o1.s(), o2.s()); - assertSetsEqual(o1.ss(), o2.ss()); - } - - private void assertSetsEqual(Collection c1, Collection c2) { - assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet<>(c1); - Set s2 = new HashSet<>(c2); - assertEquals(s1, s2); - } - } - - private EncryptionMaterialsProvider getMaterialProviderwithECDSA() - throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException { - Security.addProvider(new BouncyCastleProvider()); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1"); - KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); - g.initialize(ecSpec, Utils.getRng()); - KeyPair keypair = g.generateKeyPair(); - Map description = new HashMap<>(); - description.put(DynamoDbEncryptor.DEFAULT_SIGNING_ALGORITHM_HEADER, "SHA384withECDSA"); - return new SymmetricStaticProvider(null, keypair, description); - } - - private static final class InstrumentedEncryptionMaterialsProvider implements EncryptionMaterialsProvider { - private final EncryptionMaterialsProvider delegate; - private final ConcurrentHashMap calls = new ConcurrentHashMap<>(); - - InstrumentedEncryptionMaterialsProvider(EncryptionMaterialsProvider delegate) { - this.delegate = delegate; - } - - @Override - public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { - incrementMethodCount("getDecryptionMaterials()"); - return delegate.getDecryptionMaterials(context); - } - - @Override - public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { - incrementMethodCount("getEncryptionMaterials(EncryptionContext context)"); - return delegate.getEncryptionMaterials(context); - } - - @Override - public void refresh() { - incrementMethodCount("refresh()"); - delegate.refresh(); - } - - int getCallCount(String method) { - AtomicInteger count = calls.get(method); - if (count != null) { - return count.intValue(); - } else { - return 0; - } - } - - @SuppressWarnings("unused") - public void resetCallCounts() { - calls.clear(); - } - - private void incrementMethodCount(String method) { - AtomicInteger oldValue = calls.putIfAbsent(method, new AtomicInteger(1)); - if (oldValue != null) { - oldValue.incrementAndGet(); - } - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbSignerTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbSignerTest.java deleted file mode 100644 index b51193f2..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/DynamoDbSignerTest.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.Security; -import java.security.SignatureException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import javax.crypto.KeyGenerator; - -import org.bouncycastle.jce.ECNamedCurveTable; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class DynamoDbSignerTest { - // These use the Key type (rather than PublicKey, PrivateKey, and SecretKey) - // to test the routing logic within the signer. - private static Key pubKeyRsa; - private static Key privKeyRsa; - private static Key macKey; - private DynamoDbSigner signerRsa; - private DynamoDbSigner signerEcdsa; - private static Key pubKeyEcdsa; - private static Key privKeyEcdsa; - - @BeforeClass - public static void setUpClass() throws Exception { - - //RSA key generation - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - KeyPair sigPair = rsaGen.generateKeyPair(); - pubKeyRsa = sigPair.getPublic(); - privKeyRsa = sigPair.getPrivate(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - - Security.addProvider(new BouncyCastleProvider()); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1"); - KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); - g.initialize(ecSpec, Utils.getRng()); - KeyPair keypair = g.generateKeyPair(); - pubKeyEcdsa = keypair.getPublic(); - privKeyEcdsa = keypair.getPrivate(); - - } - - @BeforeMethod - public void setUp() { - signerRsa = DynamoDbSigner.getInstance("SHA256withRSA", Utils.getRng()); - signerEcdsa = DynamoDbSigner.getInstance("SHA384withECDSA", Utils.getRng()); - } - - @Test - public void mac() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] {0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macLists() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().ss("Value1", "Value2", "Value3").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().ns("100", "200", "300").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().bs(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3}), - SdkBytes.fromByteArray(new byte[] { 3, 2, 1})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macListsUnsorted() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().ss("Value3", "Value1", "Value2").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().ns("100", "300", "200").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().bs(SdkBytes.fromByteArray(new byte[] { 3, 2, 1}), - SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - Map scrambledAttributes = new HashMap<>(); - scrambledAttributes.put("Key1", AttributeValue.builder().ss("Value1", "Value2", "Value3").build()); - scrambledAttributes.put("Key2", AttributeValue.builder().ns("100", "200", "300").build()); - scrambledAttributes.put("Key3", AttributeValue.builder().bs(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3}), - SdkBytes.fromByteArray(new byte[] { 3, 2, 1})).build()); - - signerRsa.verifySignature(scrambledAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macNoAdMatchesEmptyAd() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, null, macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void macWithIgnoredChange() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - itemAttributes.put("Key4", AttributeValue.builder().s("Ignored Value").build()); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - - itemAttributes.put("Key4", AttributeValue.builder().s("New Ignored Value").build()); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void macChangedValue() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - itemAttributes.put("Key2", AttributeValue.builder().n("99").build()); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void macChangedFlag() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], macKey); - - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], macKey, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void macChangedAssociatedData() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[] {3, 2, 1}, macKey); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[] {1, 2, 3}, macKey, ByteBuffer.wrap(signature)); - } - - @Test - public void sig() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigWithReadOnlySignature() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature).asReadOnlyBuffer()); - } - - @Test - public void sigNoAdMatchesEmptyAd() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, null, privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigWithIgnoredChange() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - itemAttributes.put("Key4", AttributeValue.builder().s("Ignored Value").build()); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - itemAttributes.put("Key4", AttributeValue.builder().s("New Ignored Value").build()); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigChangedValue() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - itemAttributes.put("Key2", AttributeValue.builder().n("99").build()); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigChangedFlag() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigChangedAssociatedData() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN, EncryptionFlags.ENCRYPT)); - byte[] signature = signerRsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyRsa); - - signerRsa.verifySignature(itemAttributes, attributeFlags, new byte[] {1, 2, 3}, pubKeyRsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigEcdsa() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigEcdsaWithReadOnlySignature() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature).asReadOnlyBuffer()); - } - - @Test - public void sigEcdsaNoAdMatchesEmptyAd() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, null, privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test - public void sigEcdsaWithIgnoredChange() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key4", AttributeValue.builder().s("Ignored Value").build()); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - itemAttributes.put("Key4", AttributeValue.builder().s("New Ignored Value").build()); - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigEcdsaChangedValue() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - itemAttributes.put("Key2", AttributeValue.builder().n("99").build()); - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[0], pubKeyEcdsa, ByteBuffer.wrap(signature)); - } - - @Test(expectedExceptions = SignatureException.class) - public void sigEcdsaChangedAssociatedData() throws GeneralSecurityException { - Map itemAttributes = new HashMap<>(); - Map> attributeFlags = new HashMap<>(); - - itemAttributes.put("Key1", AttributeValue.builder().s("Value1").build()); - attributeFlags.put("Key1", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key2", AttributeValue.builder().n("100").build()); - attributeFlags.put("Key2", EnumSet.of(EncryptionFlags.SIGN)); - itemAttributes.put("Key3", AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] { 0, 1, 2, 3})).build()); - attributeFlags.put("Key3", EnumSet.of(EncryptionFlags.SIGN)); - byte[] signature = signerEcdsa.calculateSignature(itemAttributes, attributeFlags, new byte[0], privKeyEcdsa); - - signerEcdsa.verifySignature(itemAttributes, attributeFlags, new byte[] {1, 2, 3}, pubKeyEcdsa, ByteBuffer.wrap(signature)); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContextOperatorsTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContextOperatorsTest.java deleted file mode 100644 index 9a386c8d..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/EncryptionContextOperatorsTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption; - -import static org.testng.AssertJUnit.assertEquals; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContextOperators.overrideEncryptionContextTableName; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContextOperators.overrideEncryptionContextTableNameUsingMap; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import org.testng.annotations.Test; - -public class EncryptionContextOperatorsTest { - - @Test - public void testCreateEncryptionContextTableNameOverride_expectedOverride() { - Function myNewTableName = overrideEncryptionContextTableName("OriginalTableName", "MyNewTableName"); - - EncryptionContext context = EncryptionContext.builder().tableName("OriginalTableName").build(); - - EncryptionContext newContext = myNewTableName.apply(context); - - assertEquals("OriginalTableName", context.getTableName()); - assertEquals("MyNewTableName", newContext.getTableName()); - } - - /** - * Some pretty clear repetition in null cases. May make sense to replace with data providers or parameterized - * classes for null cases - */ - @Test - public void testNullCasesCreateEncryptionContextTableNameOverride_nullOriginalTableName() { - assertEncryptionContextUnchanged(EncryptionContext.builder().tableName("example").build(), - null, - "MyNewTableName"); - } - - @Test - public void testCreateEncryptionContextTableNameOverride_differentOriginalTableName() { - assertEncryptionContextUnchanged(EncryptionContext.builder().tableName("example").build(), - "DifferentTableName", - "MyNewTableName"); - } - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverride_nullEncryptionContext() { - assertEncryptionContextUnchanged(null, - "DifferentTableName", - "MyNewTableName"); - } - - @Test - public void testCreateEncryptionContextTableNameOverrideMap_expectedOverride() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("OriginalTableName", "MyNewTableName"); - - - Function nameOverrideMap = - overrideEncryptionContextTableNameUsingMap(tableNameOverrides); - - EncryptionContext context = EncryptionContext.builder().tableName("OriginalTableName").build(); - - EncryptionContext newContext = nameOverrideMap.apply(context); - - assertEquals("OriginalTableName", context.getTableName()); - assertEquals("MyNewTableName", newContext.getTableName()); - } - - @Test - public void testCreateEncryptionContextTableNameOverrideMap_multipleOverrides() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("OriginalTableName1", "MyNewTableName1"); - tableNameOverrides.put("OriginalTableName2", "MyNewTableName2"); - - - Function overrideOperator = - overrideEncryptionContextTableNameUsingMap(tableNameOverrides); - - EncryptionContext context = EncryptionContext.builder().tableName("OriginalTableName1").build(); - - EncryptionContext newContext = overrideOperator.apply(context); - - assertEquals("OriginalTableName1", context.getTableName()); - assertEquals("MyNewTableName1", newContext.getTableName()); - - EncryptionContext context2 = EncryptionContext.builder().tableName("OriginalTableName2").build(); - - EncryptionContext newContext2 = overrideOperator.apply(context2); - - assertEquals("OriginalTableName2", context2.getTableName()); - assertEquals("MyNewTableName2", newContext2.getTableName()); - - } - - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullEncryptionContextTableName() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("DifferentTableName", "MyNewTableName"); - assertEncryptionContextUnchangedFromMap(EncryptionContext.builder().build(), - tableNameOverrides); - } - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullEncryptionContext() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("DifferentTableName", "MyNewTableName"); - assertEncryptionContextUnchangedFromMap(null, - tableNameOverrides); - } - - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullOriginalTableName() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put(null, "MyNewTableName"); - assertEncryptionContextUnchangedFromMap(EncryptionContext.builder().tableName("example").build(), - tableNameOverrides); - } - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullNewTableName() { - Map tableNameOverrides = new HashMap<>(); - tableNameOverrides.put("MyOriginalTableName", null); - assertEncryptionContextUnchangedFromMap(EncryptionContext.builder().tableName("MyOriginalTableName").build(), - tableNameOverrides); - } - - - @Test - public void testNullCasesCreateEncryptionContextTableNameOverrideFromMap_nullMap() { - assertEncryptionContextUnchangedFromMap(EncryptionContext.builder().tableName("MyOriginalTableName").build(), - null); - } - - - private void assertEncryptionContextUnchanged(EncryptionContext encryptionContext, String originalTableName, String newTableName) { - Function encryptionContextTableNameOverride = overrideEncryptionContextTableName(originalTableName, newTableName); - EncryptionContext newEncryptionContext = encryptionContextTableNameOverride.apply(encryptionContext); - assertEquals(encryptionContext, newEncryptionContext); - } - - - private void assertEncryptionContextUnchangedFromMap(EncryptionContext encryptionContext, Map overrideMap) { - Function encryptionContextTableNameOverrideFromMap = overrideEncryptionContextTableNameUsingMap(overrideMap); - EncryptionContext newEncryptionContext = encryptionContextTableNameOverrideFromMap.apply(encryptionContext); - assertEquals(encryptionContext, newEncryptionContext); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AsymmetricRawMaterialsTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AsymmetricRawMaterialsTest.java deleted file mode 100644 index a6f9b1f4..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/AsymmetricRawMaterialsTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; - -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class AsymmetricRawMaterialsTest { - private static SecureRandom rnd; - private static KeyPair encryptionPair; - private static SecretKey macKey; - private static KeyPair sigPair; - private Map description; - - @BeforeClass - public static void setUpClass() throws NoSuchAlgorithmException { - rnd = new SecureRandom(); - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, rnd); - encryptionPair = rsaGen.generateKeyPair(); - sigPair = rsaGen.generateKeyPair(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, rnd); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - } - - @Test - public void macNoDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, macKey); - assertEquals(macKey, matEncryption.getSigningKey()); - assertEquals(macKey, matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, macKey, matEncryption.getMaterialDescription()); - assertEquals(macKey, matDecryption.getSigningKey()); - assertEquals(macKey, matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - } - - @Test - public void macWithDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, macKey, description); - assertEquals(macKey, matEncryption.getSigningKey()); - assertEquals(macKey, matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - assertEquals("test value", matEncryption.getMaterialDescription().get("TestKey")); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, macKey, matEncryption.getMaterialDescription()); - assertEquals(macKey, matDecryption.getSigningKey()); - assertEquals(macKey, matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - assertEquals("test value", matDecryption.getMaterialDescription().get("TestKey")); - } - - @Test - public void sigNoDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, sigPair); - assertEquals(sigPair.getPrivate(), matEncryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, sigPair, matEncryption.getMaterialDescription()); - assertEquals(sigPair.getPrivate(), matDecryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - } - - @Test - public void sigWithDescription() throws GeneralSecurityException { - AsymmetricRawMaterials matEncryption = new AsymmetricRawMaterials(encryptionPair, sigPair, description); - assertEquals(sigPair.getPrivate(), matEncryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matEncryption.getVerificationKey()); - assertFalse(matEncryption.getMaterialDescription().isEmpty()); - assertEquals("test value", matEncryption.getMaterialDescription().get("TestKey")); - - SecretKey envelopeKey = matEncryption.getEncryptionKey(); - assertEquals(envelopeKey, matEncryption.getDecryptionKey()); - - AsymmetricRawMaterials matDecryption = new AsymmetricRawMaterials(encryptionPair, sigPair, matEncryption.getMaterialDescription()); - assertEquals(sigPair.getPrivate(), matDecryption.getSigningKey()); - assertEquals(sigPair.getPublic(), matDecryption.getVerificationKey()); - assertEquals(envelopeKey, matDecryption.getEncryptionKey()); - assertEquals(envelopeKey, matDecryption.getDecryptionKey()); - assertEquals("test value", matDecryption.getMaterialDescription().get("TestKey")); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/SymmetricRawMaterialsTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/SymmetricRawMaterialsTest.java deleted file mode 100644 index f4d3568f..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/materials/SymmetricRawMaterialsTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class SymmetricRawMaterialsTest { - private static SecretKey encryptionKey; - private static SecretKey macKey; - private static KeyPair sigPair; - private static SecureRandom rnd; - private Map description; - - @BeforeClass - public static void setUpClass() throws NoSuchAlgorithmException { - rnd = new SecureRandom(); - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, rnd); - sigPair = rsaGen.generateKeyPair(); - - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, rnd); - encryptionKey = aesGen.generateKey(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, rnd); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - } - - @Test - public void macNoDescription() { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, macKey); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(macKey, mat.getSigningKey()); - assertEquals(macKey, mat.getVerificationKey()); - assertTrue(mat.getMaterialDescription().isEmpty()); - } - - @Test - public void macWithDescription() { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, macKey, description); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(macKey, mat.getSigningKey()); - assertEquals(macKey, mat.getVerificationKey()); - assertEquals(description, mat.getMaterialDescription()); - assertEquals("test value", mat.getMaterialDescription().get("TestKey")); - } - - @Test - public void sigNoDescription() { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, sigPair); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(sigPair.getPrivate(), mat.getSigningKey()); - assertEquals(sigPair.getPublic(), mat.getVerificationKey()); - assertTrue(mat.getMaterialDescription().isEmpty()); - } - - @Test - public void sigWithDescription() { - SymmetricRawMaterials mat = new SymmetricRawMaterials(encryptionKey, sigPair, description); - assertEquals(encryptionKey, mat.getEncryptionKey()); - assertEquals(encryptionKey, mat.getDecryptionKey()); - assertEquals(sigPair.getPrivate(), mat.getSigningKey()); - assertEquals(sigPair.getPublic(), mat.getVerificationKey()); - assertEquals(description, mat.getMaterialDescription()); - assertEquals("test value", mat.getMaterialDescription().get("TestKey")); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/AsymmetricStaticProviderTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/AsymmetricStaticProviderTest.java deleted file mode 100644 index c42460dd..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/AsymmetricStaticProviderTest.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; - -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -public class AsymmetricStaticProviderTest { - private static KeyPair encryptionPair; - private static SecretKey macKey; - private static KeyPair sigPair; - private Map description; - private EncryptionContext ctx; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - sigPair = rsaGen.generateKeyPair(); - encryptionPair = rsaGen.generateKeyPair(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = EncryptionContext.builder().build(); - } - - @Test - public void simpleMac() { - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, macKey, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void simpleSig() { - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void randomEnvelopeKeys() { - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, macKey, Collections.emptyMap()); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(macKey, eMat.getSigningKey()); - - EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey2 = eMat2.getEncryptionKey(); - assertEquals(macKey, eMat.getSigningKey()); - - assertFalse("Envelope keys must be different", encryptionKey.equals(encryptionKey2)); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw and exception. - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, macKey, description); - prov.refresh(); - } - - // Following tests should be moved the WrappedRawMaterialsTests when that is created - @Test - public void explicitWrappingAlgorithmPkcs1() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM, "RSA/ECB/PKCS1Padding"); - - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("RSA/ECB/PKCS1Padding", eMat.getMaterialDescription().get(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void explicitWrappingAlgorithmPkcs2() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM, "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); - - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", eMat.getMaterialDescription().get(WrappedRawMaterials.KEY_WRAPPING_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void explicitContentKeyAlgorithm() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); - - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void explicitContentKeyLength128() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(16, encryptionKey.getEncoded().length); // 128 Bits - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - @Test - public void explicitContentKeyLength256() throws GeneralSecurityException { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - AsymmetricStaticProvider prov = new AsymmetricStaticProvider(encryptionPair, sigPair, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertThat(encryptionKey, is(not(nullValue()))); - assertEquals(32, encryptionKey.getEncoded().length); // 256 Bits - assertEquals(sigPair.getPrivate(), eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(sigPair.getPublic(), dMat.getVerificationKey()); - } - - private static EncryptionContext ctx(EncryptionMaterials mat) { - return EncryptionContext.builder() - .materialDescription(mat.getMaterialDescription()).build(); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/DirectKmsMaterialsProviderTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/DirectKmsMaterialsProviderTest.java deleted file mode 100644 index 29aa48f5..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/DirectKmsMaterialsProviderTest.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; - -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.Key; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.crypto.SecretKey; - -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.kms.KmsClient; -import software.amazon.awssdk.services.kms.model.DecryptRequest; -import software.amazon.awssdk.services.kms.model.DecryptResponse; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyRequest; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyResponse; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.WrappedRawMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.FakeKMS; - -public class DirectKmsMaterialsProviderTest { - private FakeKMS kms; - private String keyId; - private Map description; - private EncryptionContext ctx; - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = EncryptionContext.builder().build(); - kms = new FakeKMS(); - keyId = kms.createKey().keyMetadata().keyId(); - } - - @Test - public void simple() { - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - - String expectedEncAlg = encryptionKey.getAlgorithm() + "/" - + (encryptionKey.getEncoded().length * 8); - String expectedSigAlg = signingKey.getAlgorithm() + "/" - + (signingKey.getEncoded().length * 8); - - Map kmsCtx = kms.getSingleEc(); - assertEquals(expectedEncAlg, - kmsCtx.get("*" + WrappedRawMaterials.CONTENT_KEY_ALGORITHM + "*")); - assertEquals(expectedSigAlg, kmsCtx.get("*amzn-ddb-sig-alg*")); - } - - @Test - public void simpleWithKmsEc() { - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", AttributeValue.builder().s("HashKeyValue").build()); - attrVals.put("rk", AttributeValue.builder().s("RangeKeyValue").build()); - - ctx = EncryptionContext.builder().hashKeyName("hk").rangeKeyName("rk") - .tableName("KmsTableName").attributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals("HashKeyValue", kmsCtx.get("hk")); - assertEquals("RangeKeyValue", kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = ctx(eMat).toBuilder() - .hashKeyName("hk") - .rangeKeyName("rk") - .tableName("KmsTableName") - .attributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test - public void simpleWithKmsEc2() throws GeneralSecurityException { - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId); - - Map attrVals = new HashMap(); - attrVals.put("hk", AttributeValue.builder().n("10").build()); - attrVals.put("rk", AttributeValue.builder().n("20").build()); - - ctx = EncryptionContext.builder().hashKeyName("hk").rangeKeyName("rk") - .tableName("KmsTableName").attributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals("10", kmsCtx.get("hk")); - assertEquals("20", kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = ctx(eMat).toBuilder() - .hashKeyName("hk") - .rangeKeyName("rk") - .tableName("KmsTableName") - .attributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test - public void simpleWithKmsEc3() { - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", - AttributeValue.builder().b(SdkBytes.fromByteArray("Foo".getBytes(StandardCharsets.UTF_8))).build()); - attrVals.put("rk", - AttributeValue.builder().b(SdkBytes.fromByteArray("Bar".getBytes(StandardCharsets.UTF_8))).build()); - - ctx = EncryptionContext.builder().hashKeyName("hk").rangeKeyName("rk") - .tableName("KmsTableName").attributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals(Base64.getEncoder().encodeToString("Foo".getBytes(StandardCharsets.UTF_8)), - kmsCtx.get("hk")); - assertEquals(Base64.getEncoder().encodeToString("Bar".getBytes(StandardCharsets.UTF_8)), - kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = ctx(eMat).toBuilder() - .hashKeyName("hk") - .rangeKeyName("rk") - .tableName("KmsTableName") - .attributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test - public void randomEnvelopeKeys() { - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey2 = eMat2.getEncryptionKey(); - - assertFalse("Envelope keys must be different", encryptionKey.equals(encryptionKey2)); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw and exception. - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId); - prov.refresh(); - } - - @Test - public void explicitContentKeyAlgorithm() { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES"); - - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES", - eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - } - - @Test - public void explicitContentKeyLength128() { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/128"); - - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(16, encryptionKey.getEncoded().length); // 128 Bits - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES/128", - eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", eMat.getEncryptionKey().getAlgorithm()); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - } - - @Test - public void explicitContentKeyLength256() { - Map desc = new HashMap<>(); - desc.put(WrappedRawMaterials.CONTENT_KEY_ALGORITHM, "AES/256"); - - DirectKmsMaterialsProvider prov = new DirectKmsMaterialsProvider(kms, keyId, desc); - - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(32, encryptionKey.getEncoded().length); // 256 Bits - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals("AES/256", - eMat.getMaterialDescription().get(WrappedRawMaterials.CONTENT_KEY_ALGORITHM)); - assertEquals("AES", eMat.getEncryptionKey().getAlgorithm()); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - } - - @Test - public void extendedWithDerivedEncryptionKeyId() { - ExtendedKmsMaterialProvider prov = new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); - String customKeyId = kms.createKey().keyMetadata().keyId(); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", AttributeValue.builder().n("10").build()); - attrVals.put("rk", AttributeValue.builder().n("20").build()); - attrVals.put("encryptionKeyId", AttributeValue.builder().s(customKeyId).build()); - - ctx = EncryptionContext.builder().hashKeyName("hk").rangeKeyName("rk") - .tableName("KmsTableName").attributeValues(attrVals).build(); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - Key signingKey = eMat.getSigningKey(); - assertNotNull(signingKey); - Map kmsCtx = kms.getSingleEc(); - assertEquals("10", kmsCtx.get("hk")); - assertEquals("20", kmsCtx.get("rk")); - assertEquals("KmsTableName", kmsCtx.get("*aws-kms-table*")); - - EncryptionContext dCtx = ctx(eMat).toBuilder() - .hashKeyName("hk") - .rangeKeyName("rk") - .tableName("KmsTableName") - .attributeValues(attrVals) - .build(); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(dCtx); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(signingKey, dMat.getVerificationKey()); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void encryptionKeyIdMismatch() { - DirectKmsMaterialsProvider directProvider = new DirectKmsMaterialsProvider(kms, keyId); - String customKeyId = kms.createKey().keyMetadata().keyId(); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", AttributeValue.builder().n("10").build()); - attrVals.put("rk", AttributeValue.builder().n("20").build()); - attrVals.put("encryptionKeyId", AttributeValue.builder().s(customKeyId).build()); - - ctx = EncryptionContext.builder().hashKeyName("hk").rangeKeyName("rk") - .tableName("KmsTableName").attributeValues(attrVals).build(); - EncryptionMaterials eMat = directProvider.getEncryptionMaterials(ctx); - - EncryptionContext dCtx = ctx(eMat).toBuilder() - .hashKeyName("hk") - .rangeKeyName("rk") - .tableName("KmsTableName") - .attributeValues(attrVals) - .build(); - - ExtendedKmsMaterialProvider extendedProvider = new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); - - extendedProvider.getDecryptionMaterials(dCtx); - } - - @Test(expectedExceptions = DynamoDbEncryptionException.class) - public void missingEncryptionKeyId() { - ExtendedKmsMaterialProvider prov = new ExtendedKmsMaterialProvider(kms, keyId, "encryptionKeyId"); - - Map attrVals = new HashMap<>(); - attrVals.put("hk", AttributeValue.builder().n("10").build()); - attrVals.put("rk", AttributeValue.builder().n("20").build()); - - ctx = EncryptionContext.builder().hashKeyName("hk").rangeKeyName("rk") - .tableName("KmsTableName").attributeValues(attrVals).build(); - prov.getEncryptionMaterials(ctx); - } - - @Test - public void generateDataKeyIsCalledWith256NumberOfBits() { - final AtomicBoolean gdkCalled = new AtomicBoolean(false); - KmsClient kmsSpy = new FakeKMS() { - @Override public GenerateDataKeyResponse generateDataKey(GenerateDataKeyRequest r) { - gdkCalled.set(true); - assertEquals((Integer) 32, r.numberOfBytes()); - assertNull(r.keySpec()); - return super.generateDataKey(r); - } - }; - assertFalse(gdkCalled.get()); - new DirectKmsMaterialsProvider(kmsSpy, keyId).getEncryptionMaterials(ctx); - assertTrue(gdkCalled.get()); - } - - private static class ExtendedKmsMaterialProvider extends DirectKmsMaterialsProvider { - private final String encryptionKeyIdAttributeName; - - public ExtendedKmsMaterialProvider(KmsClient kms, String encryptionKeyId, String encryptionKeyIdAttributeName) { - super(kms, encryptionKeyId); - - this.encryptionKeyIdAttributeName = encryptionKeyIdAttributeName; - } - - @Override - protected String selectEncryptionKeyId(EncryptionContext context) throws DynamoDbEncryptionException { - if (!context.getAttributeValues().containsKey(encryptionKeyIdAttributeName)) { - throw new DynamoDbEncryptionException("encryption key attribute is not provided"); - } - - return context.getAttributeValues().get(encryptionKeyIdAttributeName).s(); - } - - @Override - protected void validateEncryptionKeyId(String encryptionKeyId, EncryptionContext context) - throws DynamoDbEncryptionException { - if (!context.getAttributeValues().containsKey(encryptionKeyIdAttributeName)) { - throw new DynamoDbEncryptionException("encryption key attribute is not provided"); - } - - String customEncryptionKeyId = context.getAttributeValues().get(encryptionKeyIdAttributeName).s(); - if (!customEncryptionKeyId.equals(encryptionKeyId)) { - throw new DynamoDbEncryptionException("encryption key ids do not match."); - } - } - - @Override - protected DecryptResponse decrypt(DecryptRequest request, EncryptionContext context) { - return super.decrypt(request, context); - } - - @Override - protected GenerateDataKeyResponse generateDataKey(GenerateDataKeyRequest request, EncryptionContext context) { - return super.generateDataKey(request, context); - } - } - - private static EncryptionContext ctx(EncryptionMaterials mat) { - return EncryptionContext.builder() - .materialDescription(mat.getMaterialDescription()).build(); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/KeyStoreMaterialsProviderTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/KeyStoreMaterialsProviderTest.java deleted file mode 100644 index 3bc77615..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/KeyStoreMaterialsProviderTest.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.fail; - -import java.io.ByteArrayInputStream; -import java.security.KeyFactory; -import java.security.KeyStore; -import java.security.KeyStore.PasswordProtection; -import java.security.KeyStore.PrivateKeyEntry; -import java.security.KeyStore.SecretKeyEntry; -import java.security.PrivateKey; -import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -public class KeyStoreMaterialsProviderTest { - private static final String certPem = - "MIIDbTCCAlWgAwIBAgIJANdRvzVsW1CIMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV" + - "BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYDVQQKDANBV1MxGzAZBgNV" + - "BAMMEktleVN0b3JlIFRlc3QgQ2VydDAeFw0xMzA1MDgyMzMyMjBaFw0xMzA2MDcy" + - "MzMyMjBaME0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYD" + - "VQQKDANBV1MxGzAZBgNVBAMMEktleVN0b3JlIFRlc3QgQ2VydDCCASIwDQYJKoZI" + - "hvcNAQEBBQADggEPADCCAQoCggEBAJ8+umOX8x/Ma4OZishtYpcA676bwK5KScf3" + - "w+YGM37L12KTdnOyieiGtRW8p0fS0YvnhmVTvaky09I33bH+qy9gliuNL2QkyMxp" + - "uu1IwkTKKuB67CaKT6osYJLFxV/OwHcaZnTszzDgbAVg/Z+8IZxhPgxMzMa+7nDn" + - "hEm9Jd+EONq3PnRagnFeLNbMIePprdJzXHyNNiZKRRGQ/Mo9rr7mqMLSKnFNsmzB" + - "OIfeZM8nXeg+cvlmtXl72obwnGGw2ksJfaxTPm4eEhzRoAgkbjPPLHbwiJlc+GwF" + - "i8kh0Y3vQTj/gOFE4nzipkm7ux1lsGHNRVpVDWpjNd8Fl9JFELkCAwEAAaNQME4w" + - "HQYDVR0OBBYEFM0oGUuFAWlLXZaMXoJgGZxWqfOxMB8GA1UdIwQYMBaAFM0oGUuF" + - "AWlLXZaMXoJgGZxWqfOxMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB" + - "AAXCsXeC8ZRxovP0Wc6C5qv3d7dtgJJVzHwoIRt2YR3yScBa1XI40GKT80jP3MYH" + - "8xMu3mBQtcYrgRKZBy4GpHAyxoFTnPcuzq5Fg7dw7fx4E4OKIbWOahdxwtbVxQfZ" + - "UHnGG88Z0bq2twj7dALGyJhUDdiccckJGmJPOFMzjqsvoAu0n/p7eS6y5WZ5ewqw" + - "p7VwYOP3N9wVV7Podmkh1os+eCcp9GoFf0MHBMFXi2Ps2azKx8wHRIA5D1MZv/Va" + - "4L4/oTBKCjORpFlP7EhMksHBYnjqXLDP6awPMAgQNYB5J9zX6GfJsAgly3t4Rjr5" + - "cLuNYBmRuByFGo+SOdrj6D8="; - private static final String keyPem = - "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfPrpjl/MfzGuD" + - "mYrIbWKXAOu+m8CuSknH98PmBjN+y9dik3ZzsonohrUVvKdH0tGL54ZlU72pMtPS" + - "N92x/qsvYJYrjS9kJMjMabrtSMJEyirgeuwmik+qLGCSxcVfzsB3GmZ07M8w4GwF" + - "YP2fvCGcYT4MTMzGvu5w54RJvSXfhDjatz50WoJxXizWzCHj6a3Sc1x8jTYmSkUR" + - "kPzKPa6+5qjC0ipxTbJswTiH3mTPJ13oPnL5ZrV5e9qG8JxhsNpLCX2sUz5uHhIc" + - "0aAIJG4zzyx28IiZXPhsBYvJIdGN70E4/4DhROJ84qZJu7sdZbBhzUVaVQ1qYzXf" + - "BZfSRRC5AgMBAAECggEBAJMwx9eGe5LIwBfDtCPN93LbxwtHq7FtuQS8XrYexTpN" + - "76eN5c7LF+11lauh1HzuwAEw32iJHqVl9aQ5PxFm85O3ExbuSP+ngHJwx/bLacVr" + - "mHYlKGH3Net1WU5Qvz7vO7bbEBjDSj9DMJVIMSWUHv0MZO25jw2lLX/ufrgpvPf7" + - "KXSgXg/8uV7PbnTbBDNlg02u8eOc+IbH4O8XDKAhD+YQ8AE3pxtopJbb912U/cJs" + - "Y0hQ01zbkWYH7wL9BeQmR7+TEjjtr/IInNjnXmaOmSX867/rTSTuozaVrl1Ce7r8" + - "EmUDg9ZLZeKfoNYovMy08wnxWVX2J+WnNDjNiSOm+IECgYEA0v3jtGrOnKbd0d9E" + - "dbyIuhjgnwp+UsgALIiBeJYjhFS9NcWgs+02q/0ztqOK7g088KBBQOmiA+frLIVb" + - "uNCt/3jF6kJvHYkHMZ0eBEstxjVSM2UcxzJ6ceHZ68pmrru74382TewVosxccNy0" + - "glsUWNN0t5KQDcetaycRYg50MmcCgYEAwTb8klpNyQE8AWxVQlbOIEV24iarXxex" + - "7HynIg9lSeTzquZOXjp0m5omQ04psil2gZ08xjiudG+Dm7QKgYQcxQYUtZPQe15K" + - "m+2hQM0jA7tRfM1NAZHoTmUlYhzRNX6GWAqQXOgjOqBocT4ySBXRaSQq9zuZu36s" + - "fI17knap798CgYArDa2yOf0xEAfBdJqmn7MSrlLfgSenwrHuZGhu78wNi7EUUOBq" + - "9qOqUr+DrDmEO+VMgJbwJPxvaZqeehPuUX6/26gfFjFQSI7UO+hNHf4YLPc6D47g" + - "wtcjd9+c8q8jRqGfWWz+V4dOsf7G9PJMi0NKoNN3RgvpE+66J72vUZ26TwKBgEUq" + - "DdfGA7pEetp3kT2iHT9oHlpuRUJRFRv2s015/WQqVR+EOeF5Q2zADZpiTIK+XPGg" + - "+7Rpbem4UYBXPruGM1ZECv3E4AiJhGO0+Nhdln8reswWIc7CEEqf4nXwouNnW2gA" + - "wBTB9Hp0GW8QOKedR80/aTH/X9TCT7R2YRnY6JQ5AoGBAKjgPySgrNDhlJkW7jXR" + - "WiGpjGSAFPT9NMTvEHDo7oLTQ8AcYzcGQ7ISMRdVXR6GJOlFVsH4NLwuHGtcMTPK" + - "zoHbPHJyOn1SgC5tARD/1vm5CsG2hATRpWRQCTJFg5VRJ4R7Pz+HuxY4SoABcPQd" + - "K+MP8GlGqTldC6NaB1s7KuAX"; - - private static SecretKey encryptionKey; - private static SecretKey macKey; - private static KeyStore keyStore; - private static final String password = "Password"; - private static final PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray()); - - private Map description; - private EncryptionContext ctx; - private static PrivateKey privateKey; - private static Certificate certificate; - - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - - keyStore = KeyStore.getInstance("jceks"); - keyStore.load(null, password.toCharArray()); - - KeyFactory kf = KeyFactory.getInstance("RSA"); - PKCS8EncodedKeySpec rsaSpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(keyPem)); - privateKey = kf.generatePrivate(rsaSpec); - CertificateFactory cf = CertificateFactory.getInstance("X509"); - certificate = cf.generateCertificate(new ByteArrayInputStream(Base64.getDecoder().decode(certPem))); - - - keyStore.setEntry("enc", new SecretKeyEntry(encryptionKey), passwordProtection); - keyStore.setEntry("sig", new SecretKeyEntry(macKey), passwordProtection); - keyStore.setEntry("enc-a", new PrivateKeyEntry(privateKey, new Certificate[] {certificate}), passwordProtection); - keyStore.setEntry("sig-a", new PrivateKeyEntry(privateKey, new Certificate[] {certificate}), passwordProtection); - keyStore.setCertificateEntry("trustedCert", certificate); - } - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = EncryptionContext.builder().build(); - } - - @Test - @SuppressWarnings("unchecked") - public void simpleSymMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, Collections.EMPTY_MAP); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); - } - - @Test - @SuppressWarnings("unchecked") - public void simpleSymSig() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig-a", passwordProtection, passwordProtection, Collections.EMPTY_MAP); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(privateKey, encryptionMaterials.getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); - assertEquals(certificate.getPublicKey(), prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); - } - - @Test - public void equalSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); - } - - @Test - public void superSetSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - Map tmpDesc = new HashMap<>(encryptionMaterials.getMaterialDescription()); - tmpDesc.put("randomValue", "random"); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getVerificationKey()); - } - - @Test - @SuppressWarnings("unchecked") - public void subSetSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - - assertNull(prov.getDecryptionMaterials(ctx(Collections.EMPTY_MAP))); - } - - - @Test - public void noMatchSymDescMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); - assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); - assertEquals(macKey, encryptionMaterials.getSigningKey()); - Map tmpDesc = new HashMap<>(); - tmpDesc.put("randomValue", "random"); - - assertNull(prov.getDecryptionMaterials(ctx(tmpDesc))); - } - - @Test - public void testRefresh() throws Exception { - // Mostly make sure we don't throw an exception - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); - prov.refresh(); - } - - @Test - public void asymSimpleMac() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "sig", passwordProtection, passwordProtection, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(macKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(macKey, dMat.getVerificationKey()); - } - - @Test - public void asymSimpleSig() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "sig-a", passwordProtection, passwordProtection, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(privateKey, eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); - } - - @Test - public void asymSigVerifyOnly() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "trustedCert", passwordProtection, null, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertNull(eMat.getSigningKey()); - - DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); - } - - @Test - public void asymSigEncryptOnly() throws Exception { - KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "trustedCert", "sig-a", null, passwordProtection, description); - EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - assertEquals(privateKey, eMat.getSigningKey()); - - try { - prov.getDecryptionMaterials(ctx(eMat)); - fail("Expected exception"); - } catch (IllegalStateException ex) { - assertEquals("No private decryption key provided.", ex.getMessage()); - } - } - - private static EncryptionContext ctx(EncryptionMaterials mat) { - return ctx(mat.getMaterialDescription()); - } - - private static EncryptionContext ctx(Map desc) { - return EncryptionContext.builder() - .materialDescription(desc).build(); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/MostRecentProviderTests.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/MostRecentProviderTests.java deleted file mode 100644 index 58236929..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/MostRecentProviderTests.java +++ /dev/null @@ -1,545 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; - -import java.util.Collections; -import java.util.Map; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import org.mockito.Mockito; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.DynamoDbEncryptor; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.store.MetaStore; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.store.ProviderStore; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.AttributeValueBuilder; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.LocalDynamoDb; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; - -public class MostRecentProviderTests { - private static final String TABLE_NAME = "keystoreTable"; - private static final String MATERIAL_NAME = "material"; - private static final String MATERIAL_PARAM = "materialName"; - private static final SecretKey AES_KEY = new SecretKeySpec(new byte[] { 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, "AES"); - private static final SecretKey HMAC_KEY = new SecretKeySpec(new byte[] { 0, - 1, 2, 3, 4, 5, 6, 7 }, "HmacSHA256"); - private static final EncryptionMaterialsProvider BASE_PROVIDER = new SymmetricStaticProvider(AES_KEY, HMAC_KEY); - private static final DynamoDbEncryptor ENCRYPTOR = - DynamoDbEncryptor.builder().encryptionMaterialsProvider(BASE_PROVIDER).build(); - - private LocalDynamoDb localDynamoDb = new LocalDynamoDb(); - private DynamoDbClient client; - private ProviderStore store; - private EncryptionContext ctx; - - @BeforeMethod - public void setup() { - localDynamoDb.start(); - client = Mockito.spy(localDynamoDb.createLimitedWrappedClient()); - MetaStore.createTable(client, TABLE_NAME, ProvisionedThroughput.builder() - .writeCapacityUnits(1L) - .readCapacityUnits(1L) - .build()); - store = new MetaStore(client, TABLE_NAME, ENCRYPTOR); - ctx = EncryptionContext.builder().build(); - reset(client); - } - - @AfterMethod - public void stopLocalDynamoDb() { - localDynamoDb.stop(); - } - - @Test - public void constructor() { - final MostRecentProvider prov = new MostRecentProvider(store, MATERIAL_NAME, 100); - assertEquals(MATERIAL_NAME, prov.getMaterialName()); - assertEquals(100, prov.getTtlInMills()); - assertEquals(-1, prov.getCurrentVersion()); - assertEquals(0, prov.getLastUpdated()); - } - - @Test - public void singleVersion() throws InterruptedException { - final MostRecentProvider prov = new MostRecentProvider(store, MATERIAL_NAME, 500); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - // Let the TTL be exceeded - Thread.sleep(500); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - verify(client).query(any(QueryRequest.class)); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertEquals(eMat1.getSigningKey(), eMat3.getSigningKey()); - // Check algorithms. Right now we only support AES and HmacSHA256 - assertEquals("AES", eMat1.getEncryptionKey().getAlgorithm()); - assertEquals("HmacSHA256", eMat1.getSigningKey().getAlgorithm()); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new MostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - reset(client); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - verifyNoMoreInteractions(client); - } - - @Test - public void singleVersionWithRefresh() throws InterruptedException { - final MostRecentProvider prov = new MostRecentProvider(store, MATERIAL_NAME, 500); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - prov.refresh(); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - verify(client).query(any(QueryRequest.class)); // To find current version - verify(client).getItem(any(GetItemRequest.class)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - prov.refresh(); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertEquals(eMat1.getSigningKey(), eMat3.getSigningKey()); - - // Ensure that after cache refresh we only get one more hit as opposed to multiple - prov.getEncryptionMaterials(ctx); - Thread.sleep(700); - // Force refresh - prov.getEncryptionMaterials(ctx); - reset(client); - // Check to ensure no more hits - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - assertEquals(eMat1.getSigningKey(), prov.getEncryptionMaterials(ctx).getSigningKey()); - verifyNoMoreInteractions(client); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new MostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - reset(client); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - verifyNoMoreInteractions(client); - } - - - @Test - public void twoVersions() throws InterruptedException { - final MostRecentProvider prov = new MostRecentProvider(store, MATERIAL_NAME, 500); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Create the new material - store.newProvider(MATERIAL_NAME); - reset(client); - - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - verifyNoMoreInteractions(client); - // Let the TTL be exceeded - Thread.sleep(500); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - - verify(client).query(any(QueryRequest.class)); // To find current version - verify(client).getItem(any(GetItemRequest.class)); // To retrieve current version - verify(client, never()).putItem(any(PutItemRequest.class)); // No attempt to create a new item - assertEquals(1, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertFalse(eMat1.getSigningKey().equals(eMat3.getSigningKey())); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new MostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - reset(client); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - // Get item will be hit once for the one old key - verify(client).getItem(any(GetItemRequest.class)); - verifyNoMoreInteractions(client); - } - - @Test - public void twoVersionsWithRefresh() throws InterruptedException { - final MostRecentProvider prov = new MostRecentProvider(store, MATERIAL_NAME, 100); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1 = prov.getEncryptionMaterials(ctx); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Create the new material - store.newProvider(MATERIAL_NAME); - reset(client); - // Ensure the cache is working - final EncryptionMaterials eMat2 = prov.getEncryptionMaterials(ctx); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - prov.refresh(); - final EncryptionMaterials eMat3 = prov.getEncryptionMaterials(ctx); - verify(client).query(any(QueryRequest.class)); // To find current version - verify(client).getItem(any(GetItemRequest.class)); - assertEquals(1, store.getVersionFromMaterialDescription(eMat3.getMaterialDescription())); - - assertEquals(eMat1.getSigningKey(), eMat2.getSigningKey()); - assertFalse(eMat1.getSigningKey().equals(eMat3.getSigningKey())); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new MostRecentProvider(store, MATERIAL_NAME, 500); - final DecryptionMaterials dMat1 = prov2.getDecryptionMaterials(ctx(eMat1)); - reset(client); - assertEquals(eMat1.getEncryptionKey(), dMat1.getDecryptionKey()); - assertEquals(eMat1.getSigningKey(), dMat1.getVerificationKey()); - final DecryptionMaterials dMat2 = prov2.getDecryptionMaterials(ctx(eMat2)); - assertEquals(eMat2.getEncryptionKey(), dMat2.getDecryptionKey()); - assertEquals(eMat2.getSigningKey(), dMat2.getVerificationKey()); - final DecryptionMaterials dMat3 = prov2.getDecryptionMaterials(ctx(eMat3)); - assertEquals(eMat3.getEncryptionKey(), dMat3.getDecryptionKey()); - assertEquals(eMat3.getSigningKey(), dMat3.getVerificationKey()); - // Get item will be hit once for the one old key - verify(client).getItem(any(GetItemRequest.class)); - verifyNoMoreInteractions(client); - } - - @Test - public void singleVersionTwoMaterials() throws InterruptedException { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, AttributeValueBuilder.ofS("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, AttributeValueBuilder.ofS("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final MostRecentProvider prov = new ExtendedProvider(store, 500); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Ensure the two materials are, in fact, different - assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); - - // Ensure the cache is working - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); - - // Let the TTL be exceeded - Thread.sleep(500); - final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); - verify(client).query(any(QueryRequest.class)); // To find current version - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); - reset(client); - final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); - verify(client).query(any(QueryRequest.class)); // To find current version - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); - - assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), eMat3_1.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat3_2.getSigningKey()); - // Check algorithms. Right now we only support AES and HmacSHA256 - assertEquals("AES", eMat1_1.getEncryptionKey().getAlgorithm()); - assertEquals("AES", eMat1_2.getEncryptionKey().getAlgorithm()); - assertEquals("HmacSHA256", eMat1_1.getSigningKey().getAlgorithm()); - assertEquals("HmacSHA256", eMat1_2.getSigningKey().getAlgorithm()); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new ExtendedProvider(store, 500); - final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); - final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); - reset(client); - assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); - assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); - assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); - assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); - final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); - final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); - assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); - assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); - assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); - assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); - final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); - final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); - assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); - assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); - assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); - assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); - verifyNoMoreInteractions(client); - } - - @Test - public void singleVersionWithTwoMaterialsWithRefresh() throws InterruptedException { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, AttributeValueBuilder.ofS("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, AttributeValueBuilder.ofS("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final MostRecentProvider prov = new ExtendedProvider(store, 500); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Ensure the two materials are, in fact, different - assertFalse(eMat1_1.getSigningKey().equals(eMat1_2.getSigningKey())); - - // Ensure the cache is working - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); - - prov.refresh(); - final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); - verify(client).query(any(QueryRequest.class)); // To find current version - verify(client).getItem(any(GetItemRequest.class)); - final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); - verify(client, times(2)).query(any(QueryRequest.class)); // To find current version - verify(client, times(2)).getItem(any(GetItemRequest.class)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); - prov.refresh(); - - assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), eMat3_1.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), eMat3_2.getSigningKey()); - - // Ensure that after cache refresh we only get one more hit as opposed to multiple - prov.getEncryptionMaterials(ctx1); - prov.getEncryptionMaterials(ctx2); - Thread.sleep(700); - // Force refresh - prov.getEncryptionMaterials(ctx1); - prov.getEncryptionMaterials(ctx2); - reset(client); - // Check to ensure no more hits - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - assertEquals(eMat1_1.getSigningKey(), prov.getEncryptionMaterials(ctx1).getSigningKey()); - - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - assertEquals(eMat1_2.getSigningKey(), prov.getEncryptionMaterials(ctx2).getSigningKey()); - verifyNoMoreInteractions(client); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new ExtendedProvider(store, 500); - final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); - final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); - reset(client); - assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); - assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); - assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); - assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); - final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); - final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); - assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); - assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); - assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); - assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); - final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); - final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); - assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); - assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); - assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); - assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); - verifyNoMoreInteractions(client); - } - - @Test - public void twoVersionsWithTwoMaterialsWithRefresh() { - final Map attr1 = Collections.singletonMap(MATERIAL_PARAM, AttributeValueBuilder.ofS("material1")); - final EncryptionContext ctx1 = ctx(attr1); - final Map attr2 = Collections.singletonMap(MATERIAL_PARAM, AttributeValueBuilder.ofS("material2")); - final EncryptionContext ctx2 = ctx(attr2); - - final MostRecentProvider prov = new ExtendedProvider(store, 500); - verify(client, never()).putItem(any(PutItemRequest.class)); - final EncryptionMaterials eMat1_1 = prov.getEncryptionMaterials(ctx1); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - final EncryptionMaterials eMat1_2 = prov.getEncryptionMaterials(ctx2); - // It's a new provider, so we see a single putItem - verify(client).putItem(any(PutItemRequest.class)); - reset(client); - // Create the new material - store.newProvider("material1"); - store.newProvider("material2"); - reset(client); - // Ensure the cache is working - final EncryptionMaterials eMat2_1 = prov.getEncryptionMaterials(ctx1); - final EncryptionMaterials eMat2_2 = prov.getEncryptionMaterials(ctx2); - verifyNoMoreInteractions(client); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_1.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat1_2.getMaterialDescription())); - assertEquals(0, store.getVersionFromMaterialDescription(eMat2_2.getMaterialDescription())); - prov.refresh(); - final EncryptionMaterials eMat3_1 = prov.getEncryptionMaterials(ctx1); - final EncryptionMaterials eMat3_2 = prov.getEncryptionMaterials(ctx2); - verify(client, times(2)).query(any(QueryRequest.class)); // To find current version - verify(client, times(2)).getItem(any(GetItemRequest.class)); - assertEquals(1, store.getVersionFromMaterialDescription(eMat3_1.getMaterialDescription())); - assertEquals(1, store.getVersionFromMaterialDescription(eMat3_2.getMaterialDescription())); - - assertEquals(eMat1_1.getSigningKey(), eMat2_1.getSigningKey()); - assertFalse(eMat1_1.getSigningKey().equals(eMat3_1.getSigningKey())); - assertEquals(eMat1_2.getSigningKey(), eMat2_2.getSigningKey()); - assertFalse(eMat1_2.getSigningKey().equals(eMat3_2.getSigningKey())); - - // Ensure we can decrypt all of them without hitting ddb more than the minimum - final MostRecentProvider prov2 = new ExtendedProvider(store, 500); - final DecryptionMaterials dMat1_1 = prov2.getDecryptionMaterials(ctx(eMat1_1, attr1)); - final DecryptionMaterials dMat1_2 = prov2.getDecryptionMaterials(ctx(eMat1_2, attr2)); - reset(client); - assertEquals(eMat1_1.getEncryptionKey(), dMat1_1.getDecryptionKey()); - assertEquals(eMat1_2.getEncryptionKey(), dMat1_2.getDecryptionKey()); - assertEquals(eMat1_1.getSigningKey(), dMat1_1.getVerificationKey()); - assertEquals(eMat1_2.getSigningKey(), dMat1_2.getVerificationKey()); - final DecryptionMaterials dMat2_1 = prov2.getDecryptionMaterials(ctx(eMat2_1, attr1)); - final DecryptionMaterials dMat2_2 = prov2.getDecryptionMaterials(ctx(eMat2_2, attr2)); - assertEquals(eMat2_1.getEncryptionKey(), dMat2_1.getDecryptionKey()); - assertEquals(eMat2_2.getEncryptionKey(), dMat2_2.getDecryptionKey()); - assertEquals(eMat2_1.getSigningKey(), dMat2_1.getVerificationKey()); - assertEquals(eMat2_2.getSigningKey(), dMat2_2.getVerificationKey()); - final DecryptionMaterials dMat3_1 = prov2.getDecryptionMaterials(ctx(eMat3_1, attr1)); - final DecryptionMaterials dMat3_2 = prov2.getDecryptionMaterials(ctx(eMat3_2, attr2)); - assertEquals(eMat3_1.getEncryptionKey(), dMat3_1.getDecryptionKey()); - assertEquals(eMat3_2.getEncryptionKey(), dMat3_2.getDecryptionKey()); - assertEquals(eMat3_1.getSigningKey(), dMat3_1.getVerificationKey()); - assertEquals(eMat3_2.getSigningKey(), dMat3_2.getVerificationKey()); - // Get item will be hit once for the one old key - verify(client, times(2)).getItem(any(GetItemRequest.class)); - verifyNoMoreInteractions(client); - } - - private static EncryptionContext ctx(final Map attr) { - return EncryptionContext.builder() - .attributeValues(attr).build(); - } - - private static EncryptionContext ctx(final EncryptionMaterials mat, Map attr) { - return EncryptionContext.builder() - .attributeValues(attr) - .materialDescription(mat.getMaterialDescription()).build(); - } - - private static EncryptionContext ctx(final EncryptionMaterials mat) { - return EncryptionContext.builder() - .materialDescription(mat.getMaterialDescription()).build(); - } - - private static class ExtendedProvider extends MostRecentProvider { - public ExtendedProvider(ProviderStore keystore, long ttlInMillis) { - super(keystore, null, ttlInMillis); - } - - @Override - public long getCurrentVersion() { - throw new UnsupportedOperationException(); - } - - @Override - protected String getMaterialName(final EncryptionContext context) { - return context.getAttributeValues().get(MATERIAL_PARAM).s(); - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/SymmetricStaticProviderTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/SymmetricStaticProviderTest.java deleted file mode 100644 index 807804bd..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/SymmetricStaticProviderTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.Utils; - -public class SymmetricStaticProviderTest { - private static SecretKey encryptionKey; - private static SecretKey macKey; - private static KeyPair sigPair; - private Map description; - private EncryptionContext ctx; - - @BeforeClass - public static void setUpClass() throws Exception { - KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); - rsaGen.initialize(2048, Utils.getRng()); - sigPair = rsaGen.generateKeyPair(); - - KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); - macGen.init(256, Utils.getRng()); - macKey = macGen.generateKey(); - - KeyGenerator aesGen = KeyGenerator.getInstance("AES"); - aesGen.init(128, Utils.getRng()); - encryptionKey = aesGen.generateKey(); - } - - @BeforeMethod - public void setUp() { - description = new HashMap<>(); - description.put("TestKey", "test value"); - description = Collections.unmodifiableMap(description); - ctx = EncryptionContext.builder().build(); - } - - @Test - public void simpleMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider( - encryptionKey, macKey, Collections.emptyMap()); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - - assertEquals( - encryptionKey, - prov.getDecryptionMaterials(ctx(Collections.emptyMap())) - .getDecryptionKey()); - assertEquals( - macKey, - prov.getDecryptionMaterials(ctx(Collections.emptyMap())) - .getVerificationKey()); - } - - @Test - public void simpleSig() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, sigPair, Collections.emptyMap()); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(sigPair.getPrivate(), prov.getEncryptionMaterials(ctx).getSigningKey()); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(Collections.emptyMap())).getDecryptionKey()); - assertEquals( - sigPair.getPublic(), - prov.getDecryptionMaterials(ctx(Collections.emptyMap())) - .getVerificationKey()); - } - - @Test - public void equalDescMac() { - - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(description)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(description)).getVerificationKey()); - - } - - @Test - public void supersetDescMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - Map superSet = new HashMap<>(description); - superSet.put("NewValue", "super!"); - - assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(superSet)).getDecryptionKey()); - assertEquals(macKey, prov.getDecryptionMaterials(ctx(superSet)).getVerificationKey()); - } - - @Test - public void subsetDescMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - assertNull(prov.getDecryptionMaterials(ctx(Collections.emptyMap()))); - } - - @Test - public void noMatchDescMac() { - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - assertEquals(encryptionKey, prov.getEncryptionMaterials(ctx).getEncryptionKey()); - assertEquals(macKey, prov.getEncryptionMaterials(ctx).getSigningKey()); - assertTrue(prov.getEncryptionMaterials(ctx).getMaterialDescription().entrySet().containsAll(description.entrySet())); - - Map noMatch = new HashMap<>(); - noMatch.put("NewValue", "no match!"); - - assertNull(prov.getDecryptionMaterials(ctx(noMatch))); - } - - @Test - public void testRefresh() { - // This does nothing, make sure we don't throw and exception. - SymmetricStaticProvider prov = new SymmetricStaticProvider(encryptionKey, macKey, description); - prov.refresh(); - } - - @SuppressWarnings("unused") - private static EncryptionContext ctx(EncryptionMaterials mat) { - return ctx(mat.getMaterialDescription()); - } - - private static EncryptionContext ctx(Map desc) { - return EncryptionContext.builder() - .materialDescription(desc).build(); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/MetaStoreTests.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/MetaStoreTests.java deleted file mode 100644 index 40ae26ee..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/encryption/providers/store/MetaStoreTests.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.store; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.fail; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.DynamoDbEncryptor; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.exceptions.DynamoDbEncryptionException; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.DecryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.materials.EncryptionMaterials; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.EncryptionMaterialsProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.providers.SymmetricStaticProvider; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.AttributeValueBuilder; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.LocalDynamoDb; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; - -public class MetaStoreTests { - private static final String SOURCE_TABLE_NAME = "keystoreTable"; - private static final String DESTINATION_TABLE_NAME = "keystoreDestinationTable"; - private static final String MATERIAL_NAME = "material"; - private static final SecretKey AES_KEY = new SecretKeySpec(new byte[] { 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, "AES"); - private static final SecretKey TARGET_AES_KEY = new SecretKeySpec(new byte[] { 0, - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 }, "AES"); - private static final SecretKey HMAC_KEY = new SecretKeySpec(new byte[] { 0, - 1, 2, 3, 4, 5, 6, 7 }, "HmacSHA256"); - private static final SecretKey TARGET_HMAC_KEY = new SecretKeySpec(new byte[] { 0, - 2, 4, 6, 8, 10, 12, 14 }, "HmacSHA256"); - private static final EncryptionMaterialsProvider BASE_PROVIDER = new SymmetricStaticProvider(AES_KEY, HMAC_KEY); - private static final EncryptionMaterialsProvider TARGET_BASE_PROVIDER = new SymmetricStaticProvider(TARGET_AES_KEY, TARGET_HMAC_KEY); - private static final DynamoDbEncryptor ENCRYPTOR = - DynamoDbEncryptor.builder().encryptionMaterialsProvider(BASE_PROVIDER).build(); - private static final DynamoDbEncryptor TARGET_ENCRYPTOR = - DynamoDbEncryptor.builder().encryptionMaterialsProvider(TARGET_BASE_PROVIDER).build(); - - private final LocalDynamoDb localDynamoDb = new LocalDynamoDb(); - private final LocalDynamoDb targetLocalDynamoDb = new LocalDynamoDb(); - private DynamoDbClient client; - private DynamoDbClient targetClient; - private MetaStore store; - private MetaStore targetStore; - private EncryptionContext ctx; - - private static class TestExtraDataSupplier implements MetaStore.ExtraDataSupplier { - - private final Map attributeValueMap; - private final Set signedOnlyFieldNames; - - TestExtraDataSupplier(final Map attributeValueMap, - final Set signedOnlyFieldNames) { - this.attributeValueMap = attributeValueMap; - this.signedOnlyFieldNames = signedOnlyFieldNames; - } - - @Override - public Map getAttributes(String materialName, long version) { - return this.attributeValueMap; - } - - @Override - public Set getSignedOnlyFieldNames() { - return this.signedOnlyFieldNames; - } - } - - @BeforeMethod - public void setup() { - localDynamoDb.start(); - targetLocalDynamoDb.start(); - client = localDynamoDb.createClient(); - targetClient = targetLocalDynamoDb.createClient(); - - MetaStore.createTable(client, SOURCE_TABLE_NAME, ProvisionedThroughput.builder() - .readCapacityUnits(1L) - .writeCapacityUnits(1L) - .build()); - //Creating Targeted DynamoDB Object - MetaStore.createTable(targetClient, DESTINATION_TABLE_NAME, ProvisionedThroughput.builder() - .readCapacityUnits(1L) - .writeCapacityUnits(1L) - .build()); - store = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR); - targetStore = new MetaStore(targetClient, DESTINATION_TABLE_NAME, TARGET_ENCRYPTOR); - ctx = EncryptionContext.builder().build(); - } - - @AfterMethod - public void stopLocalDynamoDb() { - localDynamoDb.stop(); - targetLocalDynamoDb.stop(); - } - - @Test - public void testNoMaterials() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - } - - @Test - public void singleMaterial() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void singleMaterialExplicitAccess() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.getProvider(MATERIAL_NAME); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void singleMaterialExplicitAccessWithVersion() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.getProvider(MATERIAL_NAME, 0); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void singleMaterialWithImplicitCreation() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov = store.getProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void twoDifferentMaterials() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.newProvider(MATERIAL_NAME); - assertEquals(1, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - assertEquals(0, store.getVersionFromMaterialDescription(eMat.getMaterialDescription())); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - try { - prov2.getDecryptionMaterials(ctx(eMat)); - fail("Missing expected exception"); - } catch (final DynamoDbEncryptionException ex) { - // Expected Exception - } - final EncryptionMaterials eMat2 = prov2.getEncryptionMaterials(ctx); - assertEquals(1, store.getVersionFromMaterialDescription(eMat2.getMaterialDescription())); - } - - @Test - public void getOrCreateCollision() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = store.getOrCreate(MATERIAL_NAME, 0); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void getOrCreateWithContextSupplier() { - final Map attributeValueMap = new HashMap<>(); - attributeValueMap.put("CustomKeyId", AttributeValueBuilder.ofS("testCustomKeyId")); - attributeValueMap.put("KeyToken", AttributeValueBuilder.ofS("testKeyToken")); - - final Set signedOnlyAttributes = new HashSet<>(); - signedOnlyAttributes.add("CustomKeyId"); - - final TestExtraDataSupplier extraDataSupplier = new TestExtraDataSupplier( - attributeValueMap, signedOnlyAttributes); - - final MetaStore metaStore = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR, extraDataSupplier); - - assertEquals(-1, metaStore.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov1 = metaStore.getOrCreate(MATERIAL_NAME, 0); - assertEquals(0, metaStore.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = metaStore.getOrCreate(MATERIAL_NAME, 0); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test - public void replicateIntermediateKeysTest() { - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - - store.replicate(MATERIAL_NAME, 0, targetStore); - assertEquals(0, targetStore.getMaxVersion(MATERIAL_NAME)); - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final DecryptionMaterials dMat = targetStore.getProvider(MATERIAL_NAME, 0).getDecryptionMaterials(ctx(eMat)); - - assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test(expectedExceptions = IndexOutOfBoundsException.class) - public void replicateIntermediateKeysWhenMaterialNotFoundTest() { - store.replicate(MATERIAL_NAME, 0, targetStore); - } - - @Test - public void newProviderCollision() throws InterruptedException { - final SlowNewProvider slowProv = new SlowNewProvider(); - assertEquals(-1, store.getMaxVersion(MATERIAL_NAME)); - assertEquals(-1, slowProv.slowStore.getMaxVersion(MATERIAL_NAME)); - - slowProv.start(); - Thread.sleep(100); - final EncryptionMaterialsProvider prov1 = store.newProvider(MATERIAL_NAME); - slowProv.join(); - assertEquals(0, store.getMaxVersion(MATERIAL_NAME)); - assertEquals(0, slowProv.slowStore.getMaxVersion(MATERIAL_NAME)); - final EncryptionMaterialsProvider prov2 = slowProv.result; - - final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx); - final SecretKey encryptionKey = eMat.getEncryptionKey(); - assertNotNull(encryptionKey); - - final DecryptionMaterials dMat = prov2.getDecryptionMaterials(ctx(eMat)); - assertEquals(encryptionKey, dMat.getDecryptionKey()); - assertEquals(eMat.getSigningKey(), dMat.getVerificationKey()); - } - - @Test(expectedExceptions= IndexOutOfBoundsException.class) - public void invalidVersion() { - store.getProvider(MATERIAL_NAME, 1000); - } - - @Test(expectedExceptions= IllegalArgumentException.class) - public void invalidSignedOnlyField() { - final Map attributeValueMap = new HashMap<>(); - attributeValueMap.put("enc", AttributeValueBuilder.ofS("testEncryptionKey")); - - final Set signedOnlyAttributes = new HashSet<>(); - signedOnlyAttributes.add("enc"); - - final TestExtraDataSupplier extraDataSupplier = new TestExtraDataSupplier( - attributeValueMap, signedOnlyAttributes); - - new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR, extraDataSupplier); - } - - private static EncryptionContext ctx(final EncryptionMaterials mat) { - return EncryptionContext.builder() - .materialDescription(mat.getMaterialDescription()).build(); - } - - private class SlowNewProvider extends Thread { - public volatile EncryptionMaterialsProvider result; - public ProviderStore slowStore = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR) { - @Override - public EncryptionMaterialsProvider newProvider(final String materialName) { - final long nextId = getMaxVersion(materialName) + 1; - try { - Thread.sleep(1000); - } catch (final InterruptedException e) { - // Ignored - } - return getOrCreate(materialName, nextId); - } - }; - - @Override - public void run() { - result = slowStore.newProvider(MATERIAL_NAME); - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/AttributeValueMarshallerTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/AttributeValueMarshallerTest.java deleted file mode 100644 index e0988162..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/AttributeValueMarshallerTest.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.startsWith; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.fail; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.AttributeValueMarshaller.marshall; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.internal.AttributeValueMarshaller.unmarshall; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static java.util.Collections.unmodifiableList; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.testng.annotations.Test; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.testing.AttributeValueBuilder; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class AttributeValueMarshallerTest { - @Test(expectedExceptions = IllegalArgumentException.class) - public void testEmpty() { - AttributeValue av = AttributeValue.builder().build(); - marshall(av); - } - - @Test - public void testNumber() { - AttributeValue av = AttributeValue.builder().n("1337").build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testString() { - AttributeValue av = AttributeValue.builder().s("1337").build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testByteBuffer() { - AttributeValue av = AttributeValue.builder().b(SdkBytes.fromByteArray(new byte[] {0, 1, 2, 3, 4, 5})).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - // We can't use straight .equals for comparison because Attribute Values represents Sets - // as Lists and so incorrectly does an ordered comparison - - @Test - public void testNumberS() { - AttributeValue av = AttributeValue.builder().ns(unmodifiableList(Arrays.asList("1337", "1", "5"))).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testNumberSOrdering() { - AttributeValue av1 = AttributeValue.builder().ns(unmodifiableList(Arrays.asList("1337", "1", "5"))).build(); - AttributeValue av2 = AttributeValue.builder().ns(unmodifiableList(Arrays.asList("1", "5", "1337"))).build(); - assertAttributesAreEqual(av1, av2); - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - assertEquals(buff1, buff2); - } - - @Test - public void testStringS() { - AttributeValue av = AttributeValue.builder().ss(unmodifiableList(Arrays.asList("Bob", "Ann", "5"))).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testStringSOrdering() { - AttributeValue av1 = AttributeValue.builder().ss(unmodifiableList(Arrays.asList("Bob", "Ann", "5"))).build(); - AttributeValue av2 = AttributeValue.builder().ss(unmodifiableList(Arrays.asList("Ann", "Bob", "5"))).build(); - assertAttributesAreEqual(av1, av2); - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - assertEquals(buff1, buff2); - } - - @Test - public void testByteBufferS() { - AttributeValue av = AttributeValue.builder().bs(unmodifiableList( - Arrays.asList(SdkBytes.fromByteArray(new byte[] {0, 1, 2, 3, 4, 5}), - SdkBytes.fromByteArray(new byte[] {5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7})))).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testByteBufferSOrdering() { - AttributeValue av1 = AttributeValue.builder().bs(unmodifiableList( - Arrays.asList(SdkBytes.fromByteArray(new byte[] {0, 1, 2, 3, 4, 5}), - SdkBytes.fromByteArray(new byte[] {5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7})))).build(); - AttributeValue av2 = AttributeValue.builder().bs(unmodifiableList( - Arrays.asList(SdkBytes.fromByteArray(new byte[] {5, 4, 3, 2, 1, 0, 0, 0, 5, 6, 7}), - SdkBytes.fromByteArray(new byte[]{0, 1, 2, 3, 4, 5})))).build(); - - assertAttributesAreEqual(av1, av2); - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - assertEquals(buff1, buff2); - } - - @Test - public void testBoolTrue() { - AttributeValue av = AttributeValue.builder().bool(Boolean.TRUE).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testBoolFalse() { - AttributeValue av = AttributeValue.builder().bool(Boolean.FALSE).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testNULL() { - AttributeValue av = AttributeValue.builder().nul(Boolean.TRUE).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test(expectedExceptions = NullPointerException.class) - public void testActualNULL() { - unmarshall(marshall(null)); - } - - @Test - public void testEmptyList() { - AttributeValue av = AttributeValue.builder().l(emptyList()).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testListOfString() { - AttributeValue av = - AttributeValue.builder().l(singletonList(AttributeValue.builder().s("StringValue").build())).build(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testList() { - AttributeValue av = AttributeValueBuilder.ofL( - AttributeValueBuilder.ofS("StringValue"), - AttributeValueBuilder.ofN("1000"), - AttributeValueBuilder.ofBool(Boolean.TRUE)); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testListWithNull() { - final AttributeValue av = AttributeValueBuilder.ofL( - AttributeValueBuilder.ofS("StringValue"), - AttributeValueBuilder.ofN("1000"), - AttributeValueBuilder.ofBool(Boolean.TRUE), - null); - - try { - marshall(av); - } catch (NullPointerException e) { - assertThat(e.getMessage(), - startsWith("Encountered null list entry value while marshalling attribute value")); - } - } - - @Test - public void testListDuplicates() { - AttributeValue av = AttributeValueBuilder.ofL( - AttributeValueBuilder.ofN("1000"), - AttributeValueBuilder.ofN("1000"), - AttributeValueBuilder.ofN("1000"), - AttributeValueBuilder.ofN("1000")); - AttributeValue result = unmarshall(marshall(av)); - assertAttributesAreEqual(av, result); - assertEquals(4, result.l().size()); - } - - @Test - public void testComplexList() { - final List list1 = Arrays.asList( - AttributeValueBuilder.ofS("StringValue"), - AttributeValueBuilder.ofN("1000"), - AttributeValueBuilder.ofBool(Boolean.TRUE)); - final List list22 = Arrays.asList( - AttributeValueBuilder.ofS("AWS"), - AttributeValueBuilder.ofN("-3700"), - AttributeValueBuilder.ofBool(Boolean.FALSE)); - final List list2 = Arrays.asList( - AttributeValueBuilder.ofL(list22), - AttributeValueBuilder.ofNull()); - AttributeValue av = AttributeValueBuilder.ofL( - AttributeValueBuilder.ofS("StringValue1"), - AttributeValueBuilder.ofL(list1), - AttributeValueBuilder.ofN("50"), - AttributeValueBuilder.ofL(list2)); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testEmptyMap() { - Map map = new HashMap<>(); - AttributeValue av = AttributeValueBuilder.ofM(map); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testSimpleMap() { - Map map = new HashMap<>(); - map.put("KeyValue", AttributeValueBuilder.ofS("ValueValue")); - AttributeValue av = AttributeValueBuilder.ofM(map); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - @Test - public void testSimpleMapWithNull() { - final Map map = new HashMap<>(); - map.put("KeyValue", AttributeValueBuilder.ofS("ValueValue")); - map.put("NullKeyValue", null); - - final AttributeValue av = AttributeValueBuilder.ofM(map); - - try { - marshall(av); - fail("NullPointerException should have been thrown"); - } catch (NullPointerException e) { - assertThat(e.getMessage(), startsWith("Encountered null map value for key NullKeyValue while marshalling " - + "attribute value")); - } - } - - @Test - public void testMapOrdering() { - LinkedHashMap m1 = new LinkedHashMap<>(); - LinkedHashMap m2 = new LinkedHashMap<>(); - - m1.put("Value1", AttributeValueBuilder.ofN("1")); - m1.put("Value2", AttributeValueBuilder.ofBool(Boolean.TRUE)); - - m2.put("Value2", AttributeValueBuilder.ofBool(Boolean.TRUE)); - m2.put("Value1", AttributeValueBuilder.ofN("1")); - - AttributeValue av1 = AttributeValueBuilder.ofM(m1); - AttributeValue av2 = AttributeValueBuilder.ofM(m2); - - ByteBuffer buff1 = marshall(av1); - ByteBuffer buff2 = marshall(av2); - assertEquals(buff1, buff2); - assertAttributesAreEqual(av1, unmarshall(buff1)); - assertAttributesAreEqual(av1, unmarshall(buff2)); - assertAttributesAreEqual(av2, unmarshall(buff1)); - assertAttributesAreEqual(av2, unmarshall(buff2)); - } - - @Test - public void testComplexMap() { - AttributeValue av = buildComplexAttributeValue(); - assertAttributesAreEqual(av, unmarshall(marshall(av))); - } - - // This test ensures that an AttributeValue marshalled by an older - // version of this library still unmarshalls correctly. It also - // ensures that old and new marshalling is identical. - @Test - public void testVersioningCompatibility() { - AttributeValue newObject = buildComplexAttributeValue(); - byte[] oldBytes = Base64.getDecoder().decode(COMPLEX_ATTRIBUTE_MARSHALLED); - byte[] newBytes = marshall(newObject).array(); - assertThat(oldBytes, is(newBytes)); - - AttributeValue oldObject = unmarshall(ByteBuffer.wrap(oldBytes)); - assertAttributesAreEqual(oldObject, newObject); - } - - private static final String COMPLEX_ATTRIBUTE_MARSHALLED = "AE0AAAADAHM" + - "AAAAJSW5uZXJMaXN0AEwAAAAGAHMAAAALQ29tcGxleExpc3QAbgAAAAE1AGIAA" + - "AAGAAECAwQFAEwAAAAFAD8BAAAAAABMAAAAAQA/AABNAAAAAwBzAAAABFBpbms" + - "AcwAAAAVGbG95ZABzAAAABFRlc3QAPwEAcwAAAAdWZXJzaW9uAG4AAAABMQAAA" + - "E0AAAADAHMAAAAETGlzdABMAAAABQBuAAAAATUAbgAAAAE0AG4AAAABMwBuAAA" + - "AATIAbgAAAAExAHMAAAADTWFwAE0AAAABAHMAAAAGTmVzdGVkAD8BAHMAAAAEV" + - "HJ1ZQA/AQBzAAAACVNpbmdsZU1hcABNAAAAAQBzAAAAA0ZPTwBzAAAAA0JBUgB" + - "zAAAACVN0cmluZ1NldABTAAAAAwAAAANiYXIAAAADYmF6AAAAA2Zvbw=="; - - private static AttributeValue buildComplexAttributeValue() { - Map floydMap = new HashMap<>(); - floydMap.put("Pink", AttributeValueBuilder.ofS("Floyd")); - floydMap.put("Version", AttributeValueBuilder.ofN("1")); - floydMap.put("Test", AttributeValueBuilder.ofBool(Boolean.TRUE)); - List floydList = Arrays.asList( - AttributeValueBuilder.ofBool(Boolean.TRUE), - AttributeValueBuilder.ofNull(), - AttributeValueBuilder.ofNull(), - AttributeValueBuilder.ofL(AttributeValueBuilder.ofBool(Boolean.FALSE)), - AttributeValueBuilder.ofM(floydMap) - ); - - List nestedList = Arrays.asList( - AttributeValueBuilder.ofN("5"), - AttributeValueBuilder.ofN("4"), - AttributeValueBuilder.ofN("3"), - AttributeValueBuilder.ofN("2"), - AttributeValueBuilder.ofN("1") - ); - Map nestedMap = new HashMap<>(); - nestedMap.put("True", AttributeValueBuilder.ofBool(Boolean.TRUE)); - nestedMap.put("List", AttributeValueBuilder.ofL(nestedList)); - nestedMap.put("Map", AttributeValueBuilder.ofM( - Collections.singletonMap("Nested", - AttributeValueBuilder.ofBool(Boolean.TRUE)))); - - List innerList = Arrays.asList( - AttributeValueBuilder.ofS("ComplexList"), - AttributeValueBuilder.ofN("5"), - AttributeValueBuilder.ofB(new byte[] {0, 1, 2, 3, 4, 5}), - AttributeValueBuilder.ofL(floydList), - AttributeValueBuilder.ofNull(), - AttributeValueBuilder.ofM(nestedMap) - ); - - Map result = new HashMap<>(); - result.put("SingleMap", AttributeValueBuilder.ofM( - Collections.singletonMap("FOO", AttributeValueBuilder.ofS("BAR")))); - result.put("InnerList", AttributeValueBuilder.ofL(innerList)); - result.put("StringSet", AttributeValueBuilder.ofSS("foo", "bar", "baz")); - return AttributeValue.builder().m(Collections.unmodifiableMap(result)).build(); - } - - private void assertAttributesAreEqual(AttributeValue o1, AttributeValue o2) { - assertEquals(o1.b(), o2.b()); - assertSetsEqual(o1.bs(), o2.bs()); - assertEquals(o1.n(), o2.n()); - assertSetsEqual(o1.ns(), o2.ns()); - assertEquals(o1.s(), o2.s()); - assertSetsEqual(o1.ss(), o2.ss()); - assertEquals(o1.bool(), o2.bool()); - assertEquals(o1.nul(), o2.nul()); - - if (o1.l() != null) { - assertNotNull(o2.l()); - final List l1 = o1.l(); - final List l2 = o2.l(); - assertEquals(l1.size(), l2.size()); - for (int x = 0; x < l1.size(); ++x) { - assertAttributesAreEqual(l1.get(x), l2.get(x)); - } - } - - if (o1.m() != null) { - assertNotNull(o2.m()); - final Map m1 = o1.m(); - final Map m2 = o2.m(); - assertEquals(m1.size(), m2.size()); - for (Map.Entry entry : m1.entrySet()) { - assertAttributesAreEqual(entry.getValue(), m2.get(entry.getKey())); - } - } - } - - private void assertSetsEqual(Collection c1, Collection c2) { - assertFalse(c1 == null ^ c2 == null); - if (c1 != null) { - Set s1 = new HashSet<>(c1); - Set s2 = new HashSet<>(c2); - assertEquals(s1, s2); - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/ByteBufferInputStreamTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/ByteBufferInputStreamTest.java deleted file mode 100644 index 71b90c19..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/ByteBufferInputStreamTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import org.testng.annotations.Test; - -public class ByteBufferInputStreamTest { - - @Test - public void testRead() throws IOException { - ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - for (int x = 0; x < 10; ++x) { - assertEquals(10 - x, bis.available()); - assertEquals(x, bis.read()); - } - assertEquals(0, bis.available()); - bis.close(); - } - - @Test - public void testReadByteArray() throws IOException { - ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - assertEquals(10, bis.available()); - - byte[] buff = new byte[4]; - - int len = bis.read(buff); - assertEquals(4, len); - assertEquals(6, bis.available()); - assertThat(buff, is(new byte[] {0, 1, 2, 3})); - - len = bis.read(buff); - assertEquals(4, len); - assertEquals(2, bis.available()); - assertThat(buff, is(new byte[] {4, 5, 6, 7})); - - len = bis.read(buff); - assertEquals(2, len); - assertEquals(0, bis.available()); - assertThat(buff, is(new byte[] {8, 9, 6, 7})); - bis.close(); - } - - @Test - public void testSkip() throws IOException { - ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.wrap(new byte[]{(byte) 0xFA, 15, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - assertEquals(13, bis.available()); - assertEquals(0xFA, bis.read()); - assertEquals(12, bis.available()); - bis.skip(2); - assertEquals(10, bis.available()); - for (int x = 0; x < 10; ++x) { - assertEquals(x, bis.read()); - } - assertEquals(0, bis.available()); - assertEquals(-1, bis.read()); - bis.close(); - } - - @Test - public void testMarkSupported() throws IOException { - try (ByteBufferInputStream bis = new ByteBufferInputStream(ByteBuffer.allocate(0))) { - assertFalse(bis.markSupported()); - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/HkdfTests.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/HkdfTests.java deleted file mode 100644 index b73e2ca2..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/HkdfTests.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -import org.testng.annotations.Test; - -public class HkdfTests { - private static final testCase[] testCases = new testCase[] { - new testCase( - "HmacSHA256", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" - + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"), - fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"), - fromHex("3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865")), - new testCase( - "HmacSHA256", - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d" - + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b" - + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29" - + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37" - + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45" - + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"), - fromCHex("\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d" - + "\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b" - + "\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89" - + "\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97" - + "\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5" - + "\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf"), - fromCHex("\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd" - + "\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb" - + "\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9" - + "\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7" - + "\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5" - + "\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff"), - fromHex("B11E398DC80327A1C8E7F78C596A4934" - + "4F012EDA2D4EFAD8A050CC4C19AFA97C" - + "59045A99CAC7827271CB41C65E590E09" - + "DA3275600C2F09B8367793A9ACA3DB71" - + "CC30C58179EC3E87C14C01D5C1F3434F" + "1D87")), - new testCase( - "HmacSHA256", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" - + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - new byte[0], new byte[0], - fromHex("8DA4E775A563C18F715F802A063C5A31" - + "B8A11F5C5EE1879EC3454E5F3C738D2D" - + "9D201395FAA4B61A96C8")), - new testCase( - "HmacSHA1", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"), - fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"), - fromHex("085A01EA1B10F36933068B56EFA5AD81" - + "A4F14B822F5B091568A9CDD4F155FDA2" - + "C22E422478D305F3F896")), - new testCase( - "HmacSHA1", - fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d" - + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b" - + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29" - + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37" - + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45" - + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"), - fromCHex("\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6A\\x6B\\x6C\\x6D" - + "\\x6E\\x6F\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7A\\x7B" - + "\\x7C\\x7D\\x7E\\x7F\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89" - + "\\x8A\\x8B\\x8C\\x8D\\x8E\\x8F\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97" - + "\\x98\\x99\\x9A\\x9B\\x9C\\x9D\\x9E\\x9F\\xA0\\xA1\\xA2\\xA3\\xA4\\xA5" - + "\\xA6\\xA7\\xA8\\xA9\\xAA\\xAB\\xAC\\xAD\\xAE\\xAF"), - fromCHex("\\xB0\\xB1\\xB2\\xB3\\xB4\\xB5\\xB6\\xB7\\xB8\\xB9\\xBA\\xBB\\xBC\\xBD" - + "\\xBE\\xBF\\xC0\\xC1\\xC2\\xC3\\xC4\\xC5\\xC6\\xC7\\xC8\\xC9\\xCA\\xCB" - + "\\xCC\\xCD\\xCE\\xCF\\xD0\\xD1\\xD2\\xD3\\xD4\\xD5\\xD6\\xD7\\xD8\\xD9" - + "\\xDA\\xDB\\xDC\\xDD\\xDE\\xDF\\xE0\\xE1\\xE2\\xE3\\xE4\\xE5\\xE6\\xE7" - + "\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\\xF0\\xF1\\xF2\\xF3\\xF4\\xF5" - + "\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF"), - fromHex("0BD770A74D1160F7C9F12CD5912A06EB" - + "FF6ADCAE899D92191FE4305673BA2FFE" - + "8FA3F1A4E5AD79F3F334B3B202B2173C" - + "486EA37CE3D397ED034C7F9DFEB15C5E" - + "927336D0441F4C4300E2CFF0D0900B52D3B4")), - new testCase( - "HmacSHA1", - fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b" - + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"), - new byte[0], new byte[0], - fromHex("0AC1AF7002B3D761D1E55298DA9D0506" - + "B9AE52057220A306E07B6B87E8DF21D0")), - new testCase( - "HmacSHA1", - fromCHex("\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c" - + "\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c"), - null, new byte[0], - fromHex("2C91117204D745F3500D636A62F64F0A" - + "B3BAE548AA53D423B0D1F27EBBA6F5E5" - + "673A081D70CCE7ACFC48")) }; - - @Test - public void rfc5869Tests() throws Exception { - for (int x = 0; x < testCases.length; x++) { - testCase trial = testCases[x]; - System.out.println("Test case A." + (x + 1)); - Hkdf kdf = Hkdf.getInstance(trial.algo); - kdf.init(trial.ikm, trial.salt); - byte[] result = kdf.deriveKey(trial.info, trial.expected.length); - assertThat(result, is(trial.expected)); - } - } - - @Test - public void nullTests() throws Exception { - testCase trial = testCases[0]; - Hkdf kdf = Hkdf.getInstance(trial.algo); - kdf.init(trial.ikm, trial.salt); - // Just ensuring no exceptions are thrown - kdf.deriveKey((String)null, 16); - kdf.deriveKey((byte[]) null, 16); - } - - @Test - public void defaultSalt() throws Exception { - // Tests all the different ways to get the default salt - - testCase trial = testCases[0]; - Hkdf kdf1 = Hkdf.getInstance(trial.algo); - kdf1.init(trial.ikm, null); - Hkdf kdf2 = Hkdf.getInstance(trial.algo); - kdf2.init(trial.ikm, new byte[0]); - Hkdf kdf3 = Hkdf.getInstance(trial.algo); - kdf3.init(trial.ikm); - Hkdf kdf4 = Hkdf.getInstance(trial.algo); - kdf4.init(trial.ikm, new byte[32]); - - byte[] key1 = kdf1.deriveKey("Test", 16); - byte[] key2 = kdf2.deriveKey("Test", 16); - byte[] key3 = kdf3.deriveKey("Test", 16); - byte[] key4 = kdf4.deriveKey("Test", 16); - - assertThat(key1, is(key2)); - assertThat(key1, is(key3)); - assertThat(key1, is(key4)); - } - - private static byte[] fromHex(String data) { - byte[] result = new byte[data.length() / 2]; - for (int x = 0; x < result.length; x++) { - result[x] = (byte) Integer.parseInt( - data.substring(2 * x, 2 * x + 2), 16); - } - return result; - } - - private static byte[] fromCHex(String data) { - byte[] result = new byte[data.length() / 4]; - for (int x = 0; x < result.length; x++) { - result[x] = (byte) Integer.parseInt( - data.substring(4 * x + 2, 4 * x + 4), 16); - } - return result; - } - - private static class testCase { - public final String algo; - public final byte[] ikm; - public final byte[] salt; - public final byte[] info; - public final byte[] expected; - - public testCase(String algo, byte[] ikm, byte[] salt, byte[] info, - byte[] expected) { - super(); - this.algo = algo; - this.ikm = ikm; - this.salt = salt; - this.info = info; - this.expected = expected; - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/LRUCacheTest.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/LRUCacheTest.java deleted file mode 100644 index 3062a831..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/internal/LRUCacheTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2015-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.internal; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import org.testng.annotations.Test; - -public class LRUCacheTest { - @Test - public void test() { - final LRUCache cache = new LRUCache(3); - assertEquals(0, cache.size()); - assertEquals(3, cache.getMaxSize()); - cache.add("k1", "v1"); - assertTrue(cache.size() == 1); - cache.add("k1", "v11"); - assertTrue(cache.size() == 1); - cache.add("k2", "v2"); - assertTrue(cache.size() == 2); - cache.add("k3", "v3"); - assertTrue(cache.size() == 3); - assertEquals("v11", cache.get("k1")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - cache.add("k4", "v4"); - assertTrue(cache.size() == 3); - assertNull(cache.get("k1")); - assertEquals("v4", cache.get("k4")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - assertTrue(cache.size() == 3); - cache.add("k5", "v5"); - assertNull(cache.get("k4")); - assertEquals("v5", cache.get("k5")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - cache.clear(); - assertEquals(0, cache.size()); - } - - @Test - public void testListener() { - final Map removed = new HashMap(); - final LRUCache cache = new LRUCache(3, - new LRUCache.RemovalListener() { - @Override - public void onRemoval(final Entry entry) { - removed.put(entry.getKey(), entry.getValue()); - } - }); - assertTrue(cache.size() == 0); - cache.add("k1", "v1"); - assertTrue(cache.size() == 1); - cache.add("k1", "v11"); - assertTrue(cache.size() == 1); - cache.add("k2", "v2"); - assertTrue(cache.size() == 2); - cache.add("k3", "v3"); - assertTrue(cache.size() == 3); - assertEquals("v11", cache.get("k1")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - assertTrue(removed.isEmpty()); - cache.add("k4", "v4"); - assertTrue(cache.size() == 3); - assertNull(cache.get("k1")); - assertEquals(1, removed.size()); - assertEquals("v11", removed.get("k1")); - removed.clear(); - assertEquals("v4", cache.get("k4")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - assertTrue(cache.size() == 3); - cache.add("k5", "v5"); - assertEquals(1, removed.size()); - assertEquals("v4", removed.get("k4")); - removed.clear(); - assertNull(cache.get("k4")); - assertEquals("v5", cache.get("k5")); - assertEquals("v2", cache.get("k2")); - assertEquals("v3", cache.get("k3")); - cache.clear(); - assertEquals(0, cache.size()); - assertEquals(3, removed.size()); - assertEquals("v5", removed.get("k5")); - assertEquals("v2", removed.get("k2")); - assertEquals("v3", removed.get("k3")); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testZeroSize() { - new LRUCache(0); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testIllegalArgument() { - new LRUCache(-1); - } - - @Test - public void testSingleEntry() { - final LRUCache cache = new LRUCache(1); - assertTrue(cache.size() == 0); - cache.add("k1", "v1"); - assertTrue(cache.size() == 1); - cache.add("k1", "v11"); - assertTrue(cache.size() == 1); - assertEquals("v11", cache.get("k1")); - - cache.add("k2", "v2"); - assertTrue(cache.size() == 1); - assertEquals("v2", cache.get("k2")); - assertNull(cache.get("k1")); - - cache.add("k3", "v3"); - assertTrue(cache.size() == 1); - assertEquals("v3", cache.get("k3")); - assertNull(cache.get("k2")); - } - -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/AttrMatcher.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/AttrMatcher.java deleted file mode 100644 index 122364e6..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/AttrMatcher.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.testing; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class AttrMatcher extends BaseMatcher> { - private final Map expected; - private final boolean invert; - - public static AttrMatcher invert(Map expected) { - return new AttrMatcher(expected, true); - } - - public static AttrMatcher match(Map expected) { - return new AttrMatcher(expected, false); - } - - public AttrMatcher(Map expected, boolean invert) { - this.expected = expected; - this.invert = invert; - } - - @Override - public boolean matches(Object item) { - @SuppressWarnings("unchecked") - Map actual = (Map)item; - if (!expected.keySet().equals(actual.keySet())) { - return invert; - } - for (String key: expected.keySet()) { - AttributeValue e = expected.get(key); - AttributeValue a = actual.get(key); - if (!attrEquals(a, e)) { - return invert; - } - } - return !invert; - } - - public static boolean attrEquals(AttributeValue e, AttributeValue a) { - if (!isEqual(e.b(), a.b()) || - !isEqual(e.bool(), a.bool()) || - !isSetEqual(e.bs(), a.bs()) || - !isEqual(e.n(), a.n()) || - !isSetEqual(e.ns(), a.ns()) || - !isEqual(e.nul(), a.nul()) || - !isEqual(e.s(), a.s()) || - !isSetEqual(e.ss(), a.ss())) { - return false; - } - // Recursive types need special handling - if (e.m() == null ^ a.m() == null) { - return false; - } else if (e.m() != null) { - if (!e.m().keySet().equals(a.m().keySet())) { - return false; - } - for (final String key : e.m().keySet()) { - if (!attrEquals(e.m().get(key), a.m().get(key))) { - return false; - } - } - } - if (e.l() == null ^ a.l() == null) { - return false; - } else if (e.l() != null) { - if (e.l().size() != a.l().size()) { - return false; - } - for (int x = 0; x < e.l().size(); x++) { - if (!attrEquals(e.l().get(x), a.l().get(x))) { - return false; - } - } - } - return true; - } - - @Override - public void describeTo(Description description) { } - - private static boolean isEqual(Object o1, Object o2) { - if(o1 == null ^ o2 == null) { - return false; - } - if (o1 == o2) - return true; - return o1.equals(o2); - } - - private static boolean isSetEqual(Collection c1, Collection c2) { - if(c1 == null ^ c2 == null) { - return false; - } - if (c1 != null) { - Set s1 = new HashSet(c1); - Set s2 = new HashSet(c2); - if(!s1.equals(s2)) { - return false; - } - } - return true; - } -} \ No newline at end of file diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/AttributeValueBuilder.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/AttributeValueBuilder.java deleted file mode 100644 index 3ff7e4df..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/AttributeValueBuilder.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.testing; - -import java.util.List; -import java.util.Map; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * Static helper methods to construct standard AttributeValues in a more compact way than specifying the full builder - * chain. - */ -public final class AttributeValueBuilder { - private AttributeValueBuilder() { - // Static helper class - } - - public static AttributeValue ofS(String value) { - return AttributeValue.builder().s(value).build(); - } - - public static AttributeValue ofN(String value) { - return AttributeValue.builder().n(value).build(); - } - - public static AttributeValue ofB(byte [] value) { - return AttributeValue.builder().b(SdkBytes.fromByteArray(value)).build(); - } - - public static AttributeValue ofBool(Boolean value) { - return AttributeValue.builder().bool(value).build(); - } - - public static AttributeValue ofNull() { - return AttributeValue.builder().nul(true).build(); - } - - public static AttributeValue ofL(List values) { - return AttributeValue.builder().l(values).build(); - } - - public static AttributeValue ofL(AttributeValue ...values) { - return AttributeValue.builder().l(values).build(); - } - - public static AttributeValue ofM(Map valueMap) { - return AttributeValue.builder().m(valueMap).build(); - } - - public static AttributeValue ofSS(String ...values) { - return AttributeValue.builder().ss(values).build(); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/EncryptionTestHelper.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/EncryptionTestHelper.java deleted file mode 100644 index 2460ad88..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/EncryptionTestHelper.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.testing; - -import static java.util.stream.Collectors.toMap; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.EncryptionAction.ENCRYPT_AND_SIGN; -import static software.amazon.cryptools.dynamodbencryptionclientsdk2.EncryptionAction.SIGN_ONLY; - -import java.util.Collection; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Function; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.DynamoDbEncryptionClient; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.DynamoDbEncryptionConfiguration; -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.EncryptionContext; - -public final class EncryptionTestHelper { - private EncryptionTestHelper() { - // Static helper class - } - - public static Map encryptAllFieldsExcept(DynamoDbEncryptionClient encryptionClient, - Map record, - EncryptionContext encryptionContext, - Collection overriddenFields) { - return doSomethingExcept(encryptionClient::encryptRecord, record, encryptionContext, overriddenFields); - } - - public static Map decryptAllFieldsExcept(DynamoDbEncryptionClient encryptionClient, - Map record, - EncryptionContext encryptionContext, - Collection overriddenFields) { - return doSomethingExcept(encryptionClient::decryptRecord, record, encryptionContext, overriddenFields); - } - - private static Map doSomethingExcept( - BiFunction, DynamoDbEncryptionConfiguration, Map> operation, - Map record, - EncryptionContext encryptionContext, - Collection overriddenFields) { - - DynamoDbEncryptionConfiguration encryptionConfiguration = DynamoDbEncryptionConfiguration.builder() - .defaultEncryptionAction(ENCRYPT_AND_SIGN) - .encryptionContext(encryptionContext) - .addEncryptionActionOverrides(overriddenFields.stream().collect(toMap(Function.identity(), - ignored -> SIGN_ONLY))) - .build(); - - return operation.apply(record, encryptionConfiguration); - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/FakeKMS.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/FakeKMS.java deleted file mode 100644 index d05aff41..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/FakeKMS.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.testing; - -import java.nio.ByteBuffer; -import java.security.SecureRandom; -import java.time.Instant; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.kms.KmsClient; -import software.amazon.awssdk.services.kms.model.CreateKeyRequest; -import software.amazon.awssdk.services.kms.model.CreateKeyResponse; -import software.amazon.awssdk.services.kms.model.DecryptRequest; -import software.amazon.awssdk.services.kms.model.DecryptResponse; -import software.amazon.awssdk.services.kms.model.EncryptRequest; -import software.amazon.awssdk.services.kms.model.EncryptResponse; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyRequest; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyResponse; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyWithoutPlaintextRequest; -import software.amazon.awssdk.services.kms.model.GenerateDataKeyWithoutPlaintextResponse; -import software.amazon.awssdk.services.kms.model.InvalidCiphertextException; -import software.amazon.awssdk.services.kms.model.KeyMetadata; -import software.amazon.awssdk.services.kms.model.KeyUsageType; - -public class FakeKMS implements KmsClient { - private static final SecureRandom rnd = new SecureRandom(); - private static final String ACCOUNT_ID = "01234567890"; - private final Map results_ = new HashMap<>(); - - @Override - public CreateKeyResponse createKey(CreateKeyRequest createKeyRequest) { - String keyId = UUID.randomUUID().toString(); - String arn = "arn:aws:testing:kms:" + ACCOUNT_ID + ":key/" + keyId; - return CreateKeyResponse.builder() - .keyMetadata(KeyMetadata.builder().awsAccountId(ACCOUNT_ID) - .creationDate(Instant.now()) - .description(createKeyRequest.description()) - .enabled(true) - .keyId(keyId) - .keyUsage(KeyUsageType.ENCRYPT_DECRYPT) - .arn(arn) - .build()) - .build(); - } - - @Override - public DecryptResponse decrypt(DecryptRequest decryptRequest) { - DecryptResponse result = results_.get(new DecryptMapKey(decryptRequest)); - if (result != null) { - return result; - } else { - throw InvalidCiphertextException.create("Invalid Ciphertext", new RuntimeException()); - } - } - - @Override - public EncryptResponse encrypt(EncryptRequest encryptRequest) { - final byte[] cipherText = new byte[512]; - rnd.nextBytes(cipherText); - DecryptResponse.Builder dec = DecryptResponse.builder(); - dec.keyId(encryptRequest.keyId()) - .plaintext(SdkBytes.fromByteBuffer(encryptRequest.plaintext().asByteBuffer().asReadOnlyBuffer())); - ByteBuffer ctBuff = ByteBuffer.wrap(cipherText); - - results_.put(new DecryptMapKey(ctBuff, encryptRequest.encryptionContext()), dec.build()); - - return EncryptResponse.builder() - .ciphertextBlob(SdkBytes.fromByteBuffer(ctBuff)) - .keyId(encryptRequest.keyId()) - .build(); - } - - @Override - public GenerateDataKeyResponse generateDataKey(GenerateDataKeyRequest generateDataKeyRequest) { - byte[] pt; - if (generateDataKeyRequest.keySpec() != null) { - if (generateDataKeyRequest.keySpec().toString().contains("256")) { - pt = new byte[32]; - } else if (generateDataKeyRequest.keySpec().toString().contains("128")) { - pt = new byte[16]; - } else { - throw new UnsupportedOperationException(); - } - } else { - pt = new byte[generateDataKeyRequest.numberOfBytes()]; - } - rnd.nextBytes(pt); - ByteBuffer ptBuff = ByteBuffer.wrap(pt); - EncryptResponse encryptresponse = encrypt(EncryptRequest.builder() - .keyId(generateDataKeyRequest.keyId()) - .plaintext(SdkBytes.fromByteBuffer(ptBuff)) - .encryptionContext(generateDataKeyRequest.encryptionContext()) - .build()); - return GenerateDataKeyResponse.builder().keyId(generateDataKeyRequest.keyId()) - .ciphertextBlob(encryptresponse.ciphertextBlob()) - .plaintext(SdkBytes.fromByteBuffer(ptBuff)) - .build(); - } - - @Override - public GenerateDataKeyWithoutPlaintextResponse generateDataKeyWithoutPlaintext( - GenerateDataKeyWithoutPlaintextRequest req) { - GenerateDataKeyResponse generateDataKey = generateDataKey(GenerateDataKeyRequest.builder() - .encryptionContext(req.encryptionContext()).numberOfBytes(req.numberOfBytes()).build()); - return GenerateDataKeyWithoutPlaintextResponse.builder().ciphertextBlob( - generateDataKey.ciphertextBlob()).keyId(req.keyId()).build(); - } - - public Map getSingleEc() { - if (results_.size() != 1) { - throw new IllegalStateException("Unexpected number of ciphertexts"); - } - for (final DecryptMapKey k : results_.keySet()) { - return k.ec; - } - throw new IllegalStateException("Unexpected number of ciphertexts"); - } - - @Override - public String serviceName() { - return KmsClient.SERVICE_NAME; - } - - @Override - public void close() { - // do nothing - } - - private static class DecryptMapKey { - private final ByteBuffer cipherText; - private final Map ec; - - public DecryptMapKey(DecryptRequest req) { - cipherText = req.ciphertextBlob().asByteBuffer(); - if (req.encryptionContext() != null) { - ec = Collections.unmodifiableMap(new HashMap<>(req.encryptionContext())); - } else { - ec = Collections.emptyMap(); - } - } - - public DecryptMapKey(ByteBuffer ctBuff, Map ec) { - cipherText = ctBuff.asReadOnlyBuffer(); - if (ec != null) { - this.ec = Collections.unmodifiableMap(new HashMap<>(ec)); - } else { - this.ec = Collections.emptyMap(); - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((cipherText == null) ? 0 : cipherText.hashCode()); - result = prime * result + ((ec == null) ? 0 : ec.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DecryptMapKey other = (DecryptMapKey) obj; - if (cipherText == null) { - if (other.cipherText != null) - return false; - } else if (!cipherText.equals(other.cipherText)) - return false; - if (ec == null) { - if (other.ec != null) - return false; - } else if (!ec.equals(other.ec)) - return false; - return true; - } - - @Override - public String toString() { - return "DecryptMapKey [cipherText=" + cipherText + ", ec=" + ec + "]"; - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/LocalDynamoDb.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/LocalDynamoDb.java deleted file mode 100644 index 7eaee680..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/LocalDynamoDb.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.testing; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; - -import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; -import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; - -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - -/** - * Wrapper for a local DynamoDb server used in testing. Each instance of this class will find a new port to run on, - * so multiple instances can be safely run simultaneously. Each instance of this service uses memory as a storage medium - * and is thus completely ephemeral; no data will be persisted between stops and starts. - * - * LocalDynamoDb localDynamoDb = new LocalDynamoDb(); - * localDynamoDb.start(); // Start the service running locally on host - * DynamoDbClient dynamoDbClient = localDynamoDb.createClient(); - * ... // Do your testing with the client - * localDynamoDb.stop(); // Stop the service and free up resources - * - * If possible it's recommended to keep a single running instance for all your tests, as it can be slow to teardown - * and create new servers for every test, but there have been observed problems when dropping tables between tests for - * this scenario, so it's best to write your tests to be resilient to tables that already have data in them. - */ -public class LocalDynamoDb { - private DynamoDBProxyServer server; - private int port; - - /** - * Start the local DynamoDb service and run in background - */ - public void start() { - port = getFreePort(); - String portString = Integer.toString(port); - - try { - server = createServer(portString); - server.start(); - } catch (Exception e) { - throw propagate(e); - } - } - - /** - * Create a standard AWS v2 SDK client pointing to the local DynamoDb instance - * @return A DynamoDbClient pointing to the local DynamoDb instance - */ - public DynamoDbClient createClient() { - String endpoint = String.format("http://localhost:%d", port); - return DynamoDbClient.builder() - .endpointOverride(URI.create(endpoint)) - // The region is meaningless for local DynamoDb but required for client builder validation - .region(Region.US_EAST_1) - .credentialsProvider(StaticCredentialsProvider.create( - AwsBasicCredentials.create("dummy-key", "dummy-secret"))) - .build(); - } - - /** - * If you require a client object that can be mocked or spied using standard mocking frameworks, then you must call - * this method to create the client instead. Only some methods are supported by this client, but it is easy to add - * new ones. - * @return A mockable/spyable DynamoDbClient pointing to the Local DynamoDB service. - */ - public DynamoDbClient createLimitedWrappedClient() { - return new WrappedDynamoDbClient(createClient()); - } - - /** - * Stops the local DynamoDb service and frees up resources it is using. - */ - public void stop() { - try { - server.stop(); - } catch (Exception e) { - throw propagate(e); - } - } - - private DynamoDBProxyServer createServer(String portString) throws Exception { - return ServerRunner.createServerFromCommandLineArgs( - new String[]{ - "-inMemory", - "-port", portString - }); - } - - private int getFreePort() { - try { - ServerSocket socket = new ServerSocket(0); - int port = socket.getLocalPort(); - socket.close(); - return port; - } catch (IOException ioe) { - throw propagate(ioe); - } - } - - private static RuntimeException propagate(Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException)e; - } - throw new RuntimeException(e); - } - - /** - * This class can wrap any other implementation of a DynamoDbClient. The default implementation of the real - * DynamoDbClient is a final class, therefore it cannot be easily spied upon unless you first wrap it in a class - * like this. If there's a method you need it to support, just add it to the wrapper here. - */ - private static class WrappedDynamoDbClient implements DynamoDbClient { - private final DynamoDbClient wrappedClient; - - private WrappedDynamoDbClient(DynamoDbClient wrappedClient) { - this.wrappedClient = wrappedClient; - } - - @Override - public String serviceName() { - return wrappedClient.serviceName(); - } - - @Override - public void close() { - wrappedClient.close(); - } - - @Override - public PutItemResponse putItem(PutItemRequest putItemRequest) { - return wrappedClient.putItem(putItemRequest); - } - - @Override - public GetItemResponse getItem(GetItemRequest getItemRequest) { - return wrappedClient.getItem(getItemRequest); - } - - @Override - public QueryResponse query(QueryRequest queryRequest) { - return wrappedClient.query(queryRequest); - } - - @Override - public CreateTableResponse createTable(CreateTableRequest createTableRequest) { - return wrappedClient.createTable(createTableRequest); - } - } -} diff --git a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/TestDelegatedKey.java b/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/TestDelegatedKey.java deleted file mode 100644 index c19c5565..00000000 --- a/sdk2/src/test/java/software/amazon/cryptools/dynamodbencryptionclientsdk2/testing/TestDelegatedKey.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2014-2019 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. - */ -package software.amazon.cryptools.dynamodbencryptionclientsdk2.testing; - -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; - -import software.amazon.cryptools.dynamodbencryptionclientsdk2.encryption.DelegatedKey; - -public class TestDelegatedKey implements DelegatedKey { - private static final long serialVersionUID = 1L; - - private final Key realKey; - - public TestDelegatedKey(Key key) { - this.realKey = key; - } - - @Override - public String getAlgorithm() { - return "DELEGATED:" + realKey.getAlgorithm(); - } - - @Override - public byte[] getEncoded() { - return realKey.getEncoded(); - } - - @Override - public String getFormat() { - return realKey.getFormat(); - } - - @Override - public byte[] encrypt(byte[] plainText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException { - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.ENCRYPT_MODE, realKey); - byte[] iv = cipher.getIV(); - byte[] result = new byte[cipher.getOutputSize(plainText.length) + iv.length + 1]; - result[0] = (byte) iv.length; - System.arraycopy(iv, 0, result, 1, iv.length); - try { - cipher.doFinal(plainText, 0, plainText.length, result, iv.length + 1); - } catch (ShortBufferException e) { - throw new RuntimeException(e); - } - return result; - } - - @Override - public byte[] decrypt(byte[] cipherText, byte[] additionalAssociatedData, String algorithm) - throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidAlgorithmParameterException { - final byte ivLength = cipherText[0]; - IvParameterSpec iv = new IvParameterSpec(cipherText, 1, ivLength); - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.DECRYPT_MODE, realKey, iv); - return cipher.doFinal(cipherText, ivLength + 1, cipherText.length - ivLength - 1); - } - - @Override - public byte[] wrap(Key key, byte[] additionalAssociatedData, String algorithm) throws InvalidKeyException, - NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException { - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.WRAP_MODE, realKey); - return cipher.wrap(key); - } - - @Override - public Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType, - byte[] additionalAssociatedData, String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException { - Cipher cipher = Cipher.getInstance(extractAlgorithm(algorithm)); - cipher.init(Cipher.UNWRAP_MODE, realKey); - return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType); - } - - @Override - public byte[] sign(byte[] dataToSign, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException { - Mac mac = Mac.getInstance(extractAlgorithm(algorithm)); - mac.init(realKey); - return mac.doFinal(dataToSign); - } - - @Override - public boolean verify(byte[] dataToSign, byte[] signature, String algorithm) { - try { - byte[] expected = sign(dataToSign, extractAlgorithm(algorithm)); - return MessageDigest.isEqual(expected, signature); - } catch (GeneralSecurityException ex) { - return false; - } - } - - private String extractAlgorithm(String alg) { - if (alg.startsWith(getAlgorithm())) { - return alg.substring(10); - } else { - return alg; - } - } -}