From 216150eea754cefde3e9c50b513b7f1187655872 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 22 Jul 2021 21:47:53 +0530 Subject: [PATCH 01/13] fix : NPE if reading record without a signature fields --- .../encryption/DynamoDBEncryptor.java | 16 ++++++++++++++++ .../encryption/DynamoDBEncryptorTest.java | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 5aa5a718..0e084829 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -237,6 +237,16 @@ public Map<String, AttributeValue> decryptRecord( if (attributeFlags.isEmpty()) { return itemAttributes; } + + if (!itemAttributes.containsKey(signatureFieldName) + && !itemAttributes.containsKey(materialDescriptionFieldName)) { + // Check if any key from the received raw document is marked with EncryptionFlags in model + if (isAnyKeyMarkedWithEncryptionFlags(itemAttributes.keySet(), attributeFlags)) { + throw new IllegalArgumentException("Bad data, missing " + signatureFieldName); + } else { + return itemAttributes; + } + } // Copy to avoid changing anyone elses objects itemAttributes = new HashMap<String, AttributeValue>(itemAttributes); @@ -291,6 +301,12 @@ public Map<String, AttributeValue> decryptRecord( return itemAttributes; } + private boolean isAnyKeyMarkedWithEncryptionFlags(Set<String> keysToCheck, + Map<String, Set<EncryptionFlags>> attributeFlags) { + return keysToCheck.stream() + .anyMatch(key -> !attributeFlags.get(key).isEmpty()); + } + /** * Returns the encrypted (and signed) record, which is a map of item attributes. There is no side * effect on the input parameters upon calling this method. diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index 6c219e52..bb30bcb6 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -15,6 +15,7 @@ package com.amazonaws.services.dynamodbv2.datamodeling.encryption; import static com.amazonaws.services.dynamodbv2.datamodeling.encryption.utils.EncryptionContextOperators.overrideEncryptionContextTableName; +import static java.util.stream.Collectors.toMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; @@ -23,6 +24,7 @@ import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; +import static org.testng.collections.Sets.newHashSet; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; @@ -498,6 +500,19 @@ public void toByteArray() throws ReflectiveOperationException { assertToByteArray("Direct", expected, buff); } + @Test + public void testDecryptWithMissingSignatureFields() throws GeneralSecurityException { + Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = attribs.keySet() + .stream() + .collect(toMap(k -> k, k -> newHashSet())); + + Map<String, AttributeValue> decryptedAttributes = + encryptor.decryptRecord( + attribs, attributeWithEmptyEncryptionFlags, context); + + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + private void assertToByteArray( final String msg, final byte[] expected, final ByteBuffer testValue) throws ReflectiveOperationException { From 94b03559247838d618dcfd2b9b08b4bf739aaf89 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Fri, 30 Jul 2021 11:17:05 +0530 Subject: [PATCH 02/13] Fix code formatting --- .../datamodeling/encryption/DynamoDBEncryptor.java | 7 +++---- .../datamodeling/encryption/DynamoDBEncryptorTest.java | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 0e084829..375d1090 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -301,10 +301,9 @@ public Map<String, AttributeValue> decryptRecord( return itemAttributes; } - private boolean isAnyKeyMarkedWithEncryptionFlags(Set<String> keysToCheck, - Map<String, Set<EncryptionFlags>> attributeFlags) { - return keysToCheck.stream() - .anyMatch(key -> !attributeFlags.get(key).isEmpty()); + private boolean isAnyKeyMarkedWithEncryptionFlags( + Set<String> keysToCheck, Map<String, Set<EncryptionFlags>> attributeFlags) { + return keysToCheck.stream().anyMatch(key -> !attributeFlags.get(key).isEmpty()); } /** diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index bb30bcb6..8cea3d7a 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -502,13 +502,11 @@ public void toByteArray() throws ReflectiveOperationException { @Test public void testDecryptWithMissingSignatureFields() throws GeneralSecurityException { - Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = attribs.keySet() - .stream() - .collect(toMap(k -> k, k -> newHashSet())); + Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = + attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet())); Map<String, AttributeValue> decryptedAttributes = - encryptor.decryptRecord( - attribs, attributeWithEmptyEncryptionFlags, context); + encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); assertThat(decryptedAttributes, AttrMatcher.match(attribs)); } From e4c2a955517cf9ad2f3b0eccfae9b073b2764bd9 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Sun, 1 Aug 2021 21:15:56 +0530 Subject: [PATCH 03/13] Add integration test for missing signature fields in document. --- .../MissingSignatureFieldsITCase.java | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java new file mode 100644 index 00000000..e6114d65 --- /dev/null +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java @@ -0,0 +1,112 @@ +/* + * Copyright 2015 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 com.amazonaws.services.dynamodbv2.mapper.integration; + +import static org.testng.Assert.assertEquals; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; +import com.amazonaws.services.dynamodbv2.mapper.encryption.StringAttributeTestClass; +import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.PutItemRequest; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** Test reading of document which does not contain signature field. */ +public class MissingSignatureFieldsITCase extends DynamoDBMapperCryptoIntegrationTestBase { + + private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; + private static final String STRING_ATTRIBUTE = "stringAttribute"; + private static final List<Map<String, AttributeValue>> attrs = new LinkedList<>(); + + // Test data + static { + for (int i = 0; i < 5; i++) { + Map<String, AttributeValue> attr = new HashMap<String, AttributeValue>(); + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + attrs.add(attr); + } + } + + @BeforeClass + public static void setUp() throws Exception { + DynamoDBMapperCryptoIntegrationTestBase.setUp(); + for (Map<String, AttributeValue> attr : attrs) { + dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); + } + } + + @Test + public void testLoadWithMissingSignatureFields() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + + for (Map<String, AttributeValue> attr : attrs) { + AllDoNotTouchTable load = util.load(AllDoNotTouchTable.class, attr.get(KEY_NAME).getS()); + assertEquals(load.getKey(), attr.get(KEY_NAME).getS()); + assertEquals(load.getStringAttribute(), attr.get(STRING_ATTRIBUTE).getS()); + } + } + + @Test(expectedExceptions = DynamoDBMappingException.class) + public void testLoadWithBadMissingSignatureFields() { + TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) + .load(StringAttributeTestClass.class, attrs.get(0).get(KEY_NAME).getS()); + } + + @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") + public static final class AllDoNotTouchTable { + + private String key; + + private String stringAttribute; + + @DynamoDBHashKey + @DoNotTouch + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @DynamoDBAttribute + @DoNotTouch + public String getStringAttribute() { + return stringAttribute; + } + + public void setStringAttribute(String stringAttribute) { + this.stringAttribute = stringAttribute; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AllDoNotTouchTable that = (AllDoNotTouchTable) o; + return key.equals(that.key) && stringAttribute.equals(that.stringAttribute); + } + } +} From fbbe055c32a838af526321f0d1aed105443a40b8 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Wed, 11 Aug 2021 12:07:43 +0530 Subject: [PATCH 04/13] Updated decrypt logic to be less nested. Improve unit tests --- .../encryption/DynamoDBEncryptor.java | 22 ++++++++------- .../encryption/DynamoDBEncryptorTest.java | 27 ++++++++++++++++++- .../MissingSignatureFieldsITCase.java | 18 ++++++++++--- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 375d1090..e2caf93b 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -237,15 +237,15 @@ public Map<String, AttributeValue> decryptRecord( if (attributeFlags.isEmpty()) { return itemAttributes; } - + if (!isDecryptionRequired(itemAttributes.keySet(), attributeFlags)) { + return itemAttributes; + } if (!itemAttributes.containsKey(signatureFieldName) && !itemAttributes.containsKey(materialDescriptionFieldName)) { - // Check if any key from the received raw document is marked with EncryptionFlags in model - if (isAnyKeyMarkedWithEncryptionFlags(itemAttributes.keySet(), attributeFlags)) { - throw new IllegalArgumentException("Bad data, missing " + signatureFieldName); - } else { - return itemAttributes; - } + throw new IllegalArgumentException( + String.format( + "Record did not contain encryption metadata fields: '%s', '%s'.", + signatureFieldName, materialDescriptionFieldName)); } // Copy to avoid changing anyone elses objects itemAttributes = new HashMap<String, AttributeValue>(itemAttributes); @@ -301,9 +301,11 @@ public Map<String, AttributeValue> decryptRecord( return itemAttributes; } - private boolean isAnyKeyMarkedWithEncryptionFlags( - Set<String> keysToCheck, Map<String, Set<EncryptionFlags>> attributeFlags) { - return keysToCheck.stream().anyMatch(key -> !attributeFlags.get(key).isEmpty()); + private boolean isDecryptionRequired( + Set<String> attributeNamesToCheck, Map<String, Set<EncryptionFlags>> attributeFlags) { + return attributeNamesToCheck.stream() + .filter(attributeFlags::containsKey) + .anyMatch(attributeName -> !attributeFlags.get(attributeName).isEmpty()); } /** diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index 8cea3d7a..c40b14d1 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -501,7 +501,8 @@ public void toByteArray() throws ReflectiveOperationException { } @Test - public void testDecryptWithMissingSignatureFields() throws GeneralSecurityException { + public void testDecryptWithMissingSignatureAndMaterialDescFields() + throws GeneralSecurityException { Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet())); @@ -511,6 +512,30 @@ public void testDecryptWithMissingSignatureFields() throws GeneralSecurityExcept assertThat(decryptedAttributes, AttrMatcher.match(attribs)); } + @Test( + expectedExceptions = SignatureException.class, + expectedExceptionsMessageRegExp = "Bad signature") + public void testDecryptWithMissingSignatureField() throws GeneralSecurityException { + Map<String, AttributeValue> encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.remove(encryptor.getSignatureFieldName()); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + + @Test( + expectedExceptions = SignatureException.class, + expectedExceptionsMessageRegExp = "Bad signature") + public void testDecryptWithMissingMaterialDescField() throws GeneralSecurityException { + Map<String, AttributeValue> encryptedAttributes = + encryptor.encryptAllFieldsExcept(attribs, context); + assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); + encryptedAttributes.remove(encryptor.getMaterialDescriptionFieldName()); + encryptor.decryptAllFieldsExcept( + encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + } + private void assertToByteArray( final String msg, final byte[] expected, final ByteBuffer testValue) throws ReflectiveOperationException { diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java index e6114d65..6d46012e 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java @@ -20,7 +20,6 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; -import com.amazonaws.services.dynamodbv2.mapper.encryption.StringAttributeTestClass; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; @@ -68,14 +67,17 @@ public void testLoadWithMissingSignatureFields() { } } - @Test(expectedExceptions = DynamoDBMappingException.class) + @Test( + expectedExceptions = DynamoDBMappingException.class, + expectedExceptionsMessageRegExp = + "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") public void testLoadWithBadMissingSignatureFields() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(StringAttributeTestClass.class, attrs.get(0).get(KEY_NAME).getS()); + .load(EncryptedTable.class, attrs.get(0).get(KEY_NAME).getS()); } @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static final class AllDoNotTouchTable { + public static class AllDoNotTouchTable { private String key; @@ -109,4 +111,12 @@ public boolean equals(Object o) { return key.equals(that.key) && stringAttribute.equals(that.stringAttribute); } } + + public static final class EncryptedTable extends AllDoNotTouchTable { + @Override + @DynamoDBAttribute + public String getStringAttribute() { + return super.getStringAttribute(); + } + } } From b9816f0af5fcc109061e0984c1eb81801fe12768 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Wed, 11 Aug 2021 16:26:16 +0530 Subject: [PATCH 05/13] Add IT test for missing "MaterialDesc" fields. --- .../MissingSignatureFieldsITCase.java | 83 +++++++++++++------ 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java index 6d46012e..5dca96e8 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java @@ -13,6 +13,7 @@ package com.amazonaws.services.dynamodbv2.mapper.integration; import static org.testng.Assert.assertEquals; +import static org.testng.collections.Lists.newArrayList; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; @@ -20,12 +21,14 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; +import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; + import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -35,45 +38,77 @@ public class MissingSignatureFieldsITCase extends DynamoDBMapperCryptoIntegratio private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; private static final String STRING_ATTRIBUTE = "stringAttribute"; - private static final List<Map<String, AttributeValue>> attrs = new LinkedList<>(); - + private static Map<String, AttributeValue> attrsWithNoEncryptionFlags = new HashMap<>(); + private static Map<String, AttributeValue> attrsWithEncryptionFlags = new HashMap<>(); + private static Map<String, AttributeValue> attrsWithMissingSignatureEncryptionFlag = new HashMap<>(); + private static Map<String, AttributeValue> attrsWithMissingMaterialDescEncryptionFlag = new HashMap<>(); // Test data static { - for (int i = 0; i < 5; i++) { - Map<String, AttributeValue> attr = new HashMap<String, AttributeValue>(); - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - attrs.add(attr); - } + newArrayList(attrsWithNoEncryptionFlags, + attrsWithEncryptionFlags, + attrsWithMissingSignatureEncryptionFlag, + attrsWithMissingMaterialDescEncryptionFlag) + .forEach(attr -> { + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + }); } @BeforeClass public static void setUp() throws Exception { + System.setProperty("sqlite4java.library.path", "target/test-lib"); DynamoDBMapperCryptoIntegrationTestBase.setUp(); - for (Map<String, AttributeValue> attr : attrs) { - dynamo.putItem(new PutItemRequest(TABLE_NAME, attr)); - } + DynamoDBEncryptor encryptor = + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + EncryptionContext context = + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + // Insert the data + dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithNoEncryptionFlags)); + + attrsWithEncryptionFlags = encryptor.encryptAllFieldsExcept(attrsWithEncryptionFlags, context, KEY_NAME); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithEncryptionFlags)); + + attrsWithMissingSignatureEncryptionFlag = encryptor.encryptAllFieldsExcept(attrsWithMissingSignatureEncryptionFlag, context, KEY_NAME); + attrsWithMissingSignatureEncryptionFlag.remove(encryptor.getSignatureFieldName()); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithMissingSignatureEncryptionFlag)); + + attrsWithMissingMaterialDescEncryptionFlag = encryptor.encryptAllFieldsExcept(attrsWithMissingMaterialDescEncryptionFlag, context, KEY_NAME); + attrsWithMissingSignatureEncryptionFlag.remove(encryptor.getMaterialDescriptionFieldName()); + dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithMissingMaterialDescEncryptionFlag)); } @Test - public void testLoadWithMissingSignatureFields() { + public void testLoadWithMissingSignatureAndMaterialDescFields() { DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + AllDoNotTouchTable load = util.load(AllDoNotTouchTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); - for (Map<String, AttributeValue> attr : attrs) { - AllDoNotTouchTable load = util.load(AllDoNotTouchTable.class, attr.get(KEY_NAME).getS()); - assertEquals(load.getKey(), attr.get(KEY_NAME).getS()); - assertEquals(load.getStringAttribute(), attr.get(STRING_ATTRIBUTE).getS()); - } + assertEquals(load.getKey(), attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); + assertEquals(load.getStringAttribute(), attrsWithNoEncryptionFlags.get(STRING_ATTRIBUTE).getS()); } @Test( expectedExceptions = DynamoDBMappingException.class, - expectedExceptionsMessageRegExp = - "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") - public void testLoadWithBadMissingSignatureFields() { + expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") + public void testLoadWithBadMissingSignatureField() { + TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) + .load(EncryptedTable.class, attrsWithMissingSignatureEncryptionFlag.get(KEY_NAME).getS()); + } + + @Test( + expectedExceptions = DynamoDBMappingException.class, + expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") + public void testLoadWithBadMissingMaterialDescField() { + TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) + .load(EncryptedTable.class, attrsWithMissingMaterialDescEncryptionFlag.get(KEY_NAME).getS()); + } + + @Test( + expectedExceptions = DynamoDBMappingException.class, + expectedExceptionsMessageRegExp = "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") + public void testLoadWithBadMissingSignatureNMaterialDescFields() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, attrs.get(0).get(KEY_NAME).getS()); + .load(EncryptedTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); } @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") From c5d3354dcdc7e86f51a4def2edd5a304a50c895d Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Wed, 11 Aug 2021 18:17:56 +0530 Subject: [PATCH 06/13] Fix code format. --- .../MissingSignatureFieldsITCase.java | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java index 5dca96e8..2c8e8899 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java @@ -27,7 +27,6 @@ import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; - import java.util.HashMap; import java.util.Map; import org.testng.annotations.BeforeClass; @@ -40,40 +39,48 @@ public class MissingSignatureFieldsITCase extends DynamoDBMapperCryptoIntegratio private static final String STRING_ATTRIBUTE = "stringAttribute"; private static Map<String, AttributeValue> attrsWithNoEncryptionFlags = new HashMap<>(); private static Map<String, AttributeValue> attrsWithEncryptionFlags = new HashMap<>(); - private static Map<String, AttributeValue> attrsWithMissingSignatureEncryptionFlag = new HashMap<>(); - private static Map<String, AttributeValue> attrsWithMissingMaterialDescEncryptionFlag = new HashMap<>(); + private static Map<String, AttributeValue> attrsWithMissingSignatureEncryptionFlag = + new HashMap<>(); + private static Map<String, AttributeValue> attrsWithMissingMaterialDescEncryptionFlag = + new HashMap<>(); // Test data static { - newArrayList(attrsWithNoEncryptionFlags, + newArrayList( + attrsWithNoEncryptionFlags, attrsWithEncryptionFlags, attrsWithMissingSignatureEncryptionFlag, attrsWithMissingMaterialDescEncryptionFlag) - .forEach(attr -> { - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + .forEach( + attr -> { + attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); + attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); }); } @BeforeClass public static void setUp() throws Exception { - System.setProperty("sqlite4java.library.path", "target/test-lib"); DynamoDBMapperCryptoIntegrationTestBase.setUp(); DynamoDBEncryptor encryptor = - DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); + DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); EncryptionContext context = - new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); + new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); // Insert the data dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithNoEncryptionFlags)); - attrsWithEncryptionFlags = encryptor.encryptAllFieldsExcept(attrsWithEncryptionFlags, context, KEY_NAME); + attrsWithEncryptionFlags = + encryptor.encryptAllFieldsExcept(attrsWithEncryptionFlags, context, KEY_NAME); dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithEncryptionFlags)); - attrsWithMissingSignatureEncryptionFlag = encryptor.encryptAllFieldsExcept(attrsWithMissingSignatureEncryptionFlag, context, KEY_NAME); + attrsWithMissingSignatureEncryptionFlag = + encryptor.encryptAllFieldsExcept( + attrsWithMissingSignatureEncryptionFlag, context, KEY_NAME); attrsWithMissingSignatureEncryptionFlag.remove(encryptor.getSignatureFieldName()); dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithMissingSignatureEncryptionFlag)); - attrsWithMissingMaterialDescEncryptionFlag = encryptor.encryptAllFieldsExcept(attrsWithMissingMaterialDescEncryptionFlag, context, KEY_NAME); + attrsWithMissingMaterialDescEncryptionFlag = + encryptor.encryptAllFieldsExcept( + attrsWithMissingMaterialDescEncryptionFlag, context, KEY_NAME); attrsWithMissingSignatureEncryptionFlag.remove(encryptor.getMaterialDescriptionFieldName()); dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithMissingMaterialDescEncryptionFlag)); } @@ -81,10 +88,12 @@ public static void setUp() throws Exception { @Test public void testLoadWithMissingSignatureAndMaterialDescFields() { DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - AllDoNotTouchTable load = util.load(AllDoNotTouchTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); + AllDoNotTouchTable load = + util.load(AllDoNotTouchTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); assertEquals(load.getKey(), attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); - assertEquals(load.getStringAttribute(), attrsWithNoEncryptionFlags.get(STRING_ATTRIBUTE).getS()); + assertEquals( + load.getStringAttribute(), attrsWithNoEncryptionFlags.get(STRING_ATTRIBUTE).getS()); } @Test( @@ -92,23 +101,25 @@ public void testLoadWithMissingSignatureAndMaterialDescFields() { expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") public void testLoadWithBadMissingSignatureField() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, attrsWithMissingSignatureEncryptionFlag.get(KEY_NAME).getS()); + .load(EncryptedTable.class, attrsWithMissingSignatureEncryptionFlag.get(KEY_NAME).getS()); } @Test( - expectedExceptions = DynamoDBMappingException.class, - expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") + expectedExceptions = DynamoDBMappingException.class, + expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") public void testLoadWithBadMissingMaterialDescField() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, attrsWithMissingMaterialDescEncryptionFlag.get(KEY_NAME).getS()); + .load( + EncryptedTable.class, attrsWithMissingMaterialDescEncryptionFlag.get(KEY_NAME).getS()); } @Test( - expectedExceptions = DynamoDBMappingException.class, - expectedExceptionsMessageRegExp = "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") + expectedExceptions = DynamoDBMappingException.class, + expectedExceptionsMessageRegExp = + "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") public void testLoadWithBadMissingSignatureNMaterialDescFields() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); + .load(EncryptedTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); } @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") From fe0b48ca9bd66bdad40894e48164e83b9a4fd582 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 12 Aug 2021 16:38:36 +0530 Subject: [PATCH 07/13] Remove unwanted if condition and add better unit tests. --- .../encryption/DynamoDBEncryptor.java | 7 +- .../encryption/DynamoDBEncryptorTest.java | 38 ++++++- .../MissingSignatureFieldsITCase.java | 100 ++++++++++-------- 3 files changed, 93 insertions(+), 52 deletions(-) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index e2caf93b..215cf06b 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -234,10 +234,7 @@ public Map<String, AttributeValue> decryptRecord( Map<String, Set<EncryptionFlags>> attributeFlags, EncryptionContext context) throws GeneralSecurityException { - if (attributeFlags.isEmpty()) { - return itemAttributes; - } - if (!isDecryptionRequired(itemAttributes.keySet(), attributeFlags)) { + if (!itemContainsFieldsToDecryptOrSign(itemAttributes.keySet(), attributeFlags)) { return itemAttributes; } if (!itemAttributes.containsKey(signatureFieldName) @@ -301,7 +298,7 @@ public Map<String, AttributeValue> decryptRecord( return itemAttributes; } - private boolean isDecryptionRequired( + private boolean itemContainsFieldsToDecryptOrSign( Set<String> attributeNamesToCheck, Map<String, Set<EncryptionFlags>> attributeFlags) { return attributeNamesToCheck.stream() .filter(attributeFlags::containsKey) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index c40b14d1..aca6a76d 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -56,6 +56,7 @@ import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; +import org.mockito.internal.util.collections.Sets; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; @@ -508,7 +509,38 @@ public void testDecryptWithMissingSignatureAndMaterialDescFields() Map<String, AttributeValue> decryptedAttributes = encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + + /* + Test decrypt with a map that contains a new key (not included in attribs) with an encryption flag set that contains ENCRYPT and SIGN. + */ + @Test + public void testDecryptWithPlainTextItemAndAdditionNewAttributeHavingEncryptionFlag() + throws GeneralSecurityException { + Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = + attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet())); + attributeWithEmptyEncryptionFlags.put( + "newAttribute", Sets.newSet(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN)); + Map<String, AttributeValue> decryptedAttributes = + encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); + assertThat(decryptedAttributes, AttrMatcher.match(attribs)); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = + "Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") + public void testDecryptWithPlainTextItemAndAttributeHavingEncryptionFlag() + throws GeneralSecurityException { + Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = + attribs.keySet().stream() + .collect( + toMap(k -> k, k -> Sets.newSet(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN))); + + Map<String, AttributeValue> decryptedAttributes = + encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); assertThat(decryptedAttributes, AttrMatcher.match(attribs)); } @@ -520,8 +552,7 @@ public void testDecryptWithMissingSignatureField() throws GeneralSecurityExcepti encryptor.encryptAllFieldsExcept(attribs, context); assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); encryptedAttributes.remove(encryptor.getSignatureFieldName()); - encryptor.decryptAllFieldsExcept( - encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + encryptor.decryptAllFieldsExcept(encryptedAttributes, context, Collections.emptyList()); } @Test( @@ -532,8 +563,7 @@ public void testDecryptWithMissingMaterialDescField() throws GeneralSecurityExce encryptor.encryptAllFieldsExcept(attribs, context); assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); encryptedAttributes.remove(encryptor.getMaterialDescriptionFieldName()); - encryptor.decryptAllFieldsExcept( - encryptedAttributes, context, attribs.keySet().toArray(new String[0])); + encryptor.decryptAllFieldsExcept(encryptedAttributes, context, Collections.emptyList()); } private void assertToByteArray( diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java index 2c8e8899..1632a041 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java @@ -13,6 +13,7 @@ package com.amazonaws.services.dynamodbv2.mapper.integration; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; import static org.testng.collections.Lists.newArrayList; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; @@ -34,27 +35,22 @@ /** Test reading of document which does not contain signature field. */ public class MissingSignatureFieldsITCase extends DynamoDBMapperCryptoIntegrationTestBase { - - private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; private static final String STRING_ATTRIBUTE = "stringAttribute"; - private static Map<String, AttributeValue> attrsWithNoEncryptionFlags = new HashMap<>(); - private static Map<String, AttributeValue> attrsWithEncryptionFlags = new HashMap<>(); - private static Map<String, AttributeValue> attrsWithMissingSignatureEncryptionFlag = - new HashMap<>(); - private static Map<String, AttributeValue> attrsWithMissingMaterialDescEncryptionFlag = - new HashMap<>(); + private static Map<String, AttributeValue> plaintextItem = new HashMap<>(); + private static Map<String, AttributeValue> encryptedItem = new HashMap<>(); + private static Map<String, AttributeValue> encryptedItemMissingSignature = new HashMap<>(); + private static Map<String, AttributeValue> encryptedItemMissingMatDesc = new HashMap<>(); // Test data static { newArrayList( - attrsWithNoEncryptionFlags, - attrsWithEncryptionFlags, - attrsWithMissingSignatureEncryptionFlag, - attrsWithMissingMaterialDescEncryptionFlag) + plaintextItem, + encryptedItem, + encryptedItemMissingSignature, + encryptedItemMissingMatDesc) .forEach( attr -> { attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - attr.put(ORIGINAL_NAME_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); }); } @@ -66,34 +62,41 @@ public static void setUp() throws Exception { EncryptionContext context = new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); // Insert the data - dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithNoEncryptionFlags)); - - attrsWithEncryptionFlags = - encryptor.encryptAllFieldsExcept(attrsWithEncryptionFlags, context, KEY_NAME); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithEncryptionFlags)); - - attrsWithMissingSignatureEncryptionFlag = - encryptor.encryptAllFieldsExcept( - attrsWithMissingSignatureEncryptionFlag, context, KEY_NAME); - attrsWithMissingSignatureEncryptionFlag.remove(encryptor.getSignatureFieldName()); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithMissingSignatureEncryptionFlag)); - - attrsWithMissingMaterialDescEncryptionFlag = - encryptor.encryptAllFieldsExcept( - attrsWithMissingMaterialDescEncryptionFlag, context, KEY_NAME); - attrsWithMissingSignatureEncryptionFlag.remove(encryptor.getMaterialDescriptionFieldName()); - dynamo.putItem(new PutItemRequest(TABLE_NAME, attrsWithMissingMaterialDescEncryptionFlag)); + dynamo.putItem(new PutItemRequest(TABLE_NAME, plaintextItem)); + + encryptedItem = encryptor.encryptAllFieldsExcept(encryptedItem, context, KEY_NAME); + dynamo.putItem(new PutItemRequest(TABLE_NAME, encryptedItem)); + + encryptedItemMissingSignature = + encryptor.encryptAllFieldsExcept(encryptedItemMissingSignature, context, KEY_NAME); + encryptedItemMissingSignature.remove(encryptor.getSignatureFieldName()); + dynamo.putItem(new PutItemRequest(TABLE_NAME, encryptedItemMissingSignature)); + + encryptedItemMissingMatDesc = + encryptor.encryptAllFieldsExcept(encryptedItemMissingMatDesc, context, KEY_NAME); + encryptedItemMissingMatDesc.remove(encryptor.getMaterialDescriptionFieldName()); + dynamo.putItem(new PutItemRequest(TABLE_NAME, encryptedItemMissingMatDesc)); } @Test - public void testLoadWithMissingSignatureAndMaterialDescFields() { + public void testLoadWithPlaintextItem() { DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); - AllDoNotTouchTable load = - util.load(AllDoNotTouchTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); + UntouchedTable load = util.load(UntouchedTable.class, plaintextItem.get(KEY_NAME).getS()); - assertEquals(load.getKey(), attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); - assertEquals( - load.getStringAttribute(), attrsWithNoEncryptionFlags.get(STRING_ATTRIBUTE).getS()); + assertEquals(load.getKey(), plaintextItem.get(KEY_NAME).getS()); + assertEquals(load.getStringAttribute(), plaintextItem.get(STRING_ATTRIBUTE).getS()); + } + + @Test + public void testLoadWithPlaintextItemWithModelHavingNewEncryptedAttribute() { + DynamoDBMapper util = TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo); + UntouchedWithNewEncryptedAttributeTable load = + util.load( + UntouchedWithNewEncryptedAttributeTable.class, plaintextItem.get(KEY_NAME).getS()); + + assertEquals(load.getKey(), plaintextItem.get(KEY_NAME).getS()); + assertEquals(load.getStringAttribute(), plaintextItem.get(STRING_ATTRIBUTE).getS()); + assertNull(load.getNewAttribute()); } @Test( @@ -101,7 +104,7 @@ public void testLoadWithMissingSignatureAndMaterialDescFields() { expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") public void testLoadWithBadMissingSignatureField() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, attrsWithMissingSignatureEncryptionFlag.get(KEY_NAME).getS()); + .load(EncryptedTable.class, encryptedItemMissingSignature.get(KEY_NAME).getS()); } @Test( @@ -109,8 +112,7 @@ public void testLoadWithBadMissingSignatureField() { expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") public void testLoadWithBadMissingMaterialDescField() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load( - EncryptedTable.class, attrsWithMissingMaterialDescEncryptionFlag.get(KEY_NAME).getS()); + .load(EncryptedTable.class, encryptedItemMissingMatDesc.get(KEY_NAME).getS()); } @Test( @@ -119,11 +121,11 @@ public void testLoadWithBadMissingMaterialDescField() { "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") public void testLoadWithBadMissingSignatureNMaterialDescFields() { TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, attrsWithNoEncryptionFlags.get(KEY_NAME).getS()); + .load(EncryptedTable.class, plaintextItem.get(KEY_NAME).getS()); } @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") - public static class AllDoNotTouchTable { + public static class UntouchedTable { private String key; @@ -153,12 +155,24 @@ public void setStringAttribute(String stringAttribute) { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - AllDoNotTouchTable that = (AllDoNotTouchTable) o; + UntouchedTable that = (UntouchedTable) o; return key.equals(that.key) && stringAttribute.equals(that.stringAttribute); } } - public static final class EncryptedTable extends AllDoNotTouchTable { + public static final class UntouchedWithNewEncryptedAttributeTable extends UntouchedTable { + private String newAttribute; + + public String getNewAttribute() { + return newAttribute; + } + + public void setNewAttribute(String newAttribute) { + this.newAttribute = newAttribute; + } + } + + public static final class EncryptedTable extends UntouchedTable { @Override @DynamoDBAttribute public String getStringAttribute() { From 7808be0f8faafcb11f0e7ae3073895662f215a09 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 12 Aug 2021 16:44:14 +0530 Subject: [PATCH 08/13] Rename Test class to better represent the intent. --- ...dsITCase.java => MissingSignatureMatDescFieldsITCase.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/{MissingSignatureFieldsITCase.java => MissingSignatureMatDescFieldsITCase.java} (97%) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java similarity index 97% rename from sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java rename to sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java index 1632a041..d27f2fbb 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java @@ -33,8 +33,8 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -/** Test reading of document which does not contain signature field. */ -public class MissingSignatureFieldsITCase extends DynamoDBMapperCryptoIntegrationTestBase { +/** Test reading of document which does not contain signature meta fields. */ +public class MissingSignatureMatDescFieldsITCase extends DynamoDBMapperCryptoIntegrationTestBase { private static final String STRING_ATTRIBUTE = "stringAttribute"; private static Map<String, AttributeValue> plaintextItem = new HashMap<>(); private static Map<String, AttributeValue> encryptedItem = new HashMap<>(); From 3a2cf4ffa3b2ee14cee44efe311fd7eb77f0b0a1 Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 12 Aug 2021 20:59:01 +0530 Subject: [PATCH 09/13] Fix Unit test --- .../datamodeling/encryption/DynamoDBEncryptorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index aca6a76d..49c38caf 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -556,8 +556,8 @@ public void testDecryptWithMissingSignatureField() throws GeneralSecurityExcepti } @Test( - expectedExceptions = SignatureException.class, - expectedExceptionsMessageRegExp = "Bad signature") + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Algorithm does not exist") public void testDecryptWithMissingMaterialDescField() throws GeneralSecurityException { Map<String, AttributeValue> encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context); From 0ea193c4e92fd956a49e5e20df578f34d10c77ba Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 19 Aug 2021 11:45:40 +0530 Subject: [PATCH 10/13] Remove `if` condition which throw a new error. --- .../encryption/DynamoDBEncryptor.java | 7 -- .../encryption/DynamoDBEncryptorTest.java | 38 ---------- .../MissingSignatureMatDescFieldsITCase.java | 70 +------------------ 3 files changed, 2 insertions(+), 113 deletions(-) diff --git a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java index 215cf06b..3a40c1d4 100644 --- a/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java +++ b/sdk1/src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java @@ -237,13 +237,6 @@ public Map<String, AttributeValue> decryptRecord( if (!itemContainsFieldsToDecryptOrSign(itemAttributes.keySet(), attributeFlags)) { return itemAttributes; } - if (!itemAttributes.containsKey(signatureFieldName) - && !itemAttributes.containsKey(materialDescriptionFieldName)) { - throw new IllegalArgumentException( - String.format( - "Record did not contain encryption metadata fields: '%s', '%s'.", - signatureFieldName, materialDescriptionFieldName)); - } // Copy to avoid changing anyone elses objects itemAttributes = new HashMap<String, AttributeValue>(itemAttributes); diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index 49c38caf..f315572d 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -528,44 +528,6 @@ public void testDecryptWithPlainTextItemAndAdditionNewAttributeHavingEncryptionF assertThat(decryptedAttributes, AttrMatcher.match(attribs)); } - @Test( - expectedExceptions = IllegalArgumentException.class, - expectedExceptionsMessageRegExp = - "Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") - public void testDecryptWithPlainTextItemAndAttributeHavingEncryptionFlag() - throws GeneralSecurityException { - Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = - attribs.keySet().stream() - .collect( - toMap(k -> k, k -> Sets.newSet(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN))); - - Map<String, AttributeValue> decryptedAttributes = - encryptor.decryptRecord(attribs, attributeWithEmptyEncryptionFlags, context); - assertThat(decryptedAttributes, AttrMatcher.match(attribs)); - } - - @Test( - expectedExceptions = SignatureException.class, - expectedExceptionsMessageRegExp = "Bad signature") - public void testDecryptWithMissingSignatureField() throws GeneralSecurityException { - Map<String, AttributeValue> encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getSignatureFieldName()); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, Collections.emptyList()); - } - - @Test( - expectedExceptions = IllegalArgumentException.class, - expectedExceptionsMessageRegExp = "Algorithm does not exist") - public void testDecryptWithMissingMaterialDescField() throws GeneralSecurityException { - Map<String, AttributeValue> encryptedAttributes = - encryptor.encryptAllFieldsExcept(attribs, context); - assertThat(encryptedAttributes, AttrMatcher.invert(attribs)); - encryptedAttributes.remove(encryptor.getMaterialDescriptionFieldName()); - encryptor.decryptAllFieldsExcept(encryptedAttributes, context, Collections.emptyList()); - } - private void assertToByteArray( final String msg, final byte[] expected, final ByteBuffer testValue) throws ReflectiveOperationException { diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java index d27f2fbb..35308b81 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java @@ -14,18 +14,13 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; -import static org.testng.collections.Lists.newArrayList; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; -import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DoNotTouch; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; -import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.mapper.encryption.TestDynamoDBMapperFactory; -import com.amazonaws.services.dynamodbv2.mapper.encryption.TestEncryptionMaterialsProvider; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; import java.util.HashMap; @@ -37,45 +32,17 @@ public class MissingSignatureMatDescFieldsITCase extends DynamoDBMapperCryptoIntegrationTestBase { private static final String STRING_ATTRIBUTE = "stringAttribute"; private static Map<String, AttributeValue> plaintextItem = new HashMap<>(); - private static Map<String, AttributeValue> encryptedItem = new HashMap<>(); - private static Map<String, AttributeValue> encryptedItemMissingSignature = new HashMap<>(); - private static Map<String, AttributeValue> encryptedItemMissingMatDesc = new HashMap<>(); // Test data static { - newArrayList( - plaintextItem, - encryptedItem, - encryptedItemMissingSignature, - encryptedItemMissingMatDesc) - .forEach( - attr -> { - attr.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); - attr.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); - }); + plaintextItem.put(KEY_NAME, new AttributeValue().withS("" + startKey++)); + plaintextItem.put(STRING_ATTRIBUTE, new AttributeValue().withS("" + startKey++)); } @BeforeClass public static void setUp() throws Exception { DynamoDBMapperCryptoIntegrationTestBase.setUp(); - DynamoDBEncryptor encryptor = - DynamoDBEncryptor.getInstance(new TestEncryptionMaterialsProvider()); - EncryptionContext context = - new EncryptionContext.Builder().withHashKeyName(KEY_NAME).withTableName(TABLE_NAME).build(); // Insert the data dynamo.putItem(new PutItemRequest(TABLE_NAME, plaintextItem)); - - encryptedItem = encryptor.encryptAllFieldsExcept(encryptedItem, context, KEY_NAME); - dynamo.putItem(new PutItemRequest(TABLE_NAME, encryptedItem)); - - encryptedItemMissingSignature = - encryptor.encryptAllFieldsExcept(encryptedItemMissingSignature, context, KEY_NAME); - encryptedItemMissingSignature.remove(encryptor.getSignatureFieldName()); - dynamo.putItem(new PutItemRequest(TABLE_NAME, encryptedItemMissingSignature)); - - encryptedItemMissingMatDesc = - encryptor.encryptAllFieldsExcept(encryptedItemMissingMatDesc, context, KEY_NAME); - encryptedItemMissingMatDesc.remove(encryptor.getMaterialDescriptionFieldName()); - dynamo.putItem(new PutItemRequest(TABLE_NAME, encryptedItemMissingMatDesc)); } @Test @@ -99,31 +66,6 @@ public void testLoadWithPlaintextItemWithModelHavingNewEncryptedAttribute() { assertNull(load.getNewAttribute()); } - @Test( - expectedExceptions = DynamoDBMappingException.class, - expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") - public void testLoadWithBadMissingSignatureField() { - TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, encryptedItemMissingSignature.get(KEY_NAME).getS()); - } - - @Test( - expectedExceptions = DynamoDBMappingException.class, - expectedExceptionsMessageRegExp = "java.security.SignatureException: Bad signature") - public void testLoadWithBadMissingMaterialDescField() { - TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, encryptedItemMissingMatDesc.get(KEY_NAME).getS()); - } - - @Test( - expectedExceptions = DynamoDBMappingException.class, - expectedExceptionsMessageRegExp = - "java.lang.IllegalArgumentException: Record did not contain encryption metadata fields: '\\*amzn-ddb-map-sig\\*', '\\*amzn-ddb-map-desc\\*'.") - public void testLoadWithBadMissingSignatureNMaterialDescFields() { - TestDynamoDBMapperFactory.createDynamoDBMapper(dynamo) - .load(EncryptedTable.class, plaintextItem.get(KEY_NAME).getS()); - } - @DynamoDBTable(tableName = "aws-java-sdk-util-crypto") public static class UntouchedTable { @@ -171,12 +113,4 @@ public void setNewAttribute(String newAttribute) { this.newAttribute = newAttribute; } } - - public static final class EncryptedTable extends UntouchedTable { - @Override - @DynamoDBAttribute - public String getStringAttribute() { - return super.getStringAttribute(); - } - } } From 13de83015c6752163bef17c9df373f3ccbd36cea Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 19 Aug 2021 23:28:35 +0530 Subject: [PATCH 11/13] Rename IT class to be better --- ...natureMatDescFieldsITCase.java => PlaintextItemITCase.java} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/{MissingSignatureMatDescFieldsITCase.java => PlaintextItemITCase.java} (95%) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java similarity index 95% rename from sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java rename to sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java index 35308b81..8ff61482 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/MissingSignatureMatDescFieldsITCase.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/mapper/integration/PlaintextItemITCase.java @@ -28,8 +28,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -/** Test reading of document which does not contain signature meta fields. */ -public class MissingSignatureMatDescFieldsITCase extends DynamoDBMapperCryptoIntegrationTestBase { +public class PlaintextItemITCase extends DynamoDBMapperCryptoIntegrationTestBase { private static final String STRING_ATTRIBUTE = "stringAttribute"; private static Map<String, AttributeValue> plaintextItem = new HashMap<>(); // Test data From e2807d8a3883d7034e8e656713b1fa78d468a0bc Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Thu, 19 Aug 2021 23:31:56 +0530 Subject: [PATCH 12/13] Rename unit test method to be better --- .../datamodeling/encryption/DynamoDBEncryptorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index f315572d..93a3dd84 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -502,7 +502,7 @@ public void toByteArray() throws ReflectiveOperationException { } @Test - public void testDecryptWithMissingSignatureAndMaterialDescFields() + public void testDecryptWithPlaintextItem() throws GeneralSecurityException { Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet())); From 9fde1f0010d44a6612acae283457c894826ee59a Mon Sep 17 00:00:00 2001 From: Bhuvan Gupta <bhuvagup@amazon.com> Date: Fri, 20 Aug 2021 00:02:56 +0530 Subject: [PATCH 13/13] Correct formatting --- .../datamodeling/encryption/DynamoDBEncryptorTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java index 93a3dd84..6ec4aefd 100644 --- a/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java +++ b/sdk1/src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java @@ -502,8 +502,7 @@ public void toByteArray() throws ReflectiveOperationException { } @Test - public void testDecryptWithPlaintextItem() - throws GeneralSecurityException { + public void testDecryptWithPlaintextItem() throws GeneralSecurityException { Map<String, Set<EncryptionFlags>> attributeWithEmptyEncryptionFlags = attribs.keySet().stream().collect(toMap(k -> k, k -> newHashSet()));