diff --git a/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/kms-nonbmp-hashkey-1.json b/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/kms-nonbmp-hashkey-1.json new file mode 100644 index 0000000000..773f9bbe92 --- /dev/null +++ b/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/kms-nonbmp-hashkey-1.json @@ -0,0 +1,15 @@ +{ + "TableName": [ + { + "hashKey": { + "S": "test😀Ψkey" + }, + "*amzn-ddb-map-desc*": { + "B": "AAAAAAAAABBhbXpuLWRkYi1lbnYta2V5AAAA4EFRRUJBSGhBODR3blhqRUpkQmJCQnlsUlVGY1paSzJqN3h3aDZVeUxvTDI4blErMEZBQUFBSDR3ZkFZSktvWklodmNOQVFjR29HOHdiUUlCQURCb0Jna3Foa2lHOXcwQkJ3RXdIZ1lKWUlaSUFXVURCQUV1TUJFRUREWlBYZGxDVHY2VlBCVUNFZ0lCRUlBN045Q1RjRHowSzd1RVNUUGxxU3MxdjRZQTVEU2tJQmVWc0lpN2FQTllVeW9xMm4xazZJUUlINnZGc3pmeFZSeFdLdUhxejZ0MkNtV1BlUFU9AAAAD2F3cy1rbXMtZWMtYXR0cgAAAAYqa2V5cyoAAAAQYW16bi1kZGItZW52LWFsZwAAAAdBRVMvMjU2AAAAEGFtem4tZGRiLXNpZy1hbGcAAAAOSG1hY1NIQTI1Ni8yNTYAAAARYW16bi1kZGItd3JhcC1hbGcAAAADa21zAAAAFWFtem4tZGRiLW1hcC1zeW0tbW9kZQAAABEvQ0JDL1BLQ1M1UGFkZGluZw==" + }, + "*amzn-ddb-map-sig*": { + "B": "+01tpVk8ZvuIFh+ZA02PkhFhEBs2vYePEgcM/qZ6JFw=" + } + } + ] +} diff --git a/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/static-aes-hmac-nonascii-table-1.json b/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/static-aes-hmac-nonascii-table-1.json new file mode 100644 index 0000000000..ce9c3d7026 --- /dev/null +++ b/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/static-aes-hmac-nonascii-table-1.json @@ -0,0 +1,36 @@ +{ + "TableName": [ + { + "rangeKey": { + "N": "7" + }, + "hashKey": { + "N": "5" + }, + "stringValue": { + "B": "leseMgRescbtfi/CZCmfW0Lk1jkv6Md5cqAtBTEP1AQyTB25+xVF7UjWR1DbeA8Y" + }, + "*amzn-ddb-map-desc*": { + "B": "AAAAAAAAABVhbXpuLWRkYi1tYXAtc3ltLW1vZGUAAAARL0NCQy9QS0NTNVBhZGRpbmc=" + }, + "intSet": { + "B": "rf/00OwnLb/FlqMWDjvcdiX1AqzRQfLOuSSaTDZX7nSQrkDyqyxHxKYPWGnZOPOI3P2fOjVvYV0BSlmCIvP5kQ==" + }, + "byteArrayValue": { + "B": "DwzxA9QJrucdFE8nPIEaiAdggInKUH4CvumuBNZxcQc=" + }, + "intValue": { + "B": "b6tg6Vm7pzdcmlT0pc/4q/cecHZaiENJG/Hv+IU+6Lg=" + }, + "stringSet": { + "B": "HHIUXDDb2gMeGMfUs9cW0CtvPohceTRyOQ1GI/voY6vImFe1AJKNCy5Shx/PmMYH+rEzjryVsd8EVmcCco0PGA==" + }, + "version": { + "N": "0" + }, + "*amzn-ddb-map-sig*": { + "B": "EQXY4c3O2NSyRpPPuR9RzZ+CnLxn7jgb12RRmivNYZA=" + } + } + ] +} diff --git a/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/static-aes-hmac-null-table-1.json b/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/static-aes-hmac-null-table-1.json new file mode 100644 index 0000000000..328f4b1c64 --- /dev/null +++ b/DynamoDbEncryption/runtimes/java/src/test/resources/vectors/encrypted_item/ciphertext/java/static-aes-hmac-null-table-1.json @@ -0,0 +1,36 @@ +{ + "TableName": [ + { + "rangeKey": { + "N": "7" + }, + "hashKey": { + "N": "5" + }, + "stringValue": { + "B": "6Z0SJgkzw3Ij0RNpHzrdH5o9+3tGJVVr3G66OL4+q7nfh/7AEg3wHnRuBiO2z3F/" + }, + "*amzn-ddb-map-desc*": { + "B": "AAAAAAAAABVhbXpuLWRkYi1tYXAtc3ltLW1vZGUAAAARL0NCQy9QS0NTNVBhZGRpbmc=" + }, + "intSet": { + "B": "E2PmJ8DFDYXCYlAssQzJCFPkeoZqyDDpXI0qJe5iXIIkGE1Ltn+J++OzKDZ+Gvkvno1eNERWplptL22HH2+kjQ==" + }, + "byteArrayValue": { + "B": "263KRNe2DxnFKltNMXgxr95crOtbOziyqaI2rVdiUco=" + }, + "intValue": { + "B": "5rYFQHJpwVusKp4zIcJkNgQpzrz3Gwk+sb3aQsmJWRs=" + }, + "stringSet": { + "B": "Yii7PxCMKEefXMIEXeLl20YMm7WhKxxNlCJzv18QCLhmrVEL15xvTzAkcPMXVud7YIJFx1XzcVTjK0wwn5htSw==" + }, + "version": { + "N": "0" + }, + "*amzn-ddb-map-sig*": { + "B": "I/fHzON+4U01BsJA8hU6ebKn+TftiuaVAGDsn/ykAO8=" + } + } + ] +} diff --git a/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/LegacyTestVectors.java b/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/LegacyTestVectors.java index aebb82e414..7bab27e430 100644 --- a/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/LegacyTestVectors.java +++ b/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/LegacyTestVectors.java @@ -397,6 +397,47 @@ public static BaseClass attributeMapToBaseClass( return value; } + public static Map baseClassToV1AttrMap( + BaseClass obj + ) { + Map map = new HashMap<>(); + map.put( + HASH_KEY, + new AttributeValue().withN(String.valueOf(obj.getHashKey())) + ); + map.put( + RANGE_KEY, + new AttributeValue().withN(String.valueOf(obj.getRangeKey())) + ); + map.put( + "version", + new AttributeValue().withN(String.valueOf(obj.getVersion())) + ); + map.put( + "intValue", + new AttributeValue().withN(String.valueOf(obj.getIntValue())) + ); + map.put("stringValue", new AttributeValue().withS(obj.getStringValue())); + map.put( + "byteArrayValue", + new AttributeValue() + .withB(java.nio.ByteBuffer.wrap(obj.getByteArrayValue())) + ); + map.put("stringSet", new AttributeValue().withSS(obj.getStringSet())); + map.put( + "intSet", + new AttributeValue() + .withNS( + obj + .getIntSet() + .stream() + .map(String::valueOf) + .collect(java.util.stream.Collectors.toList()) + ) + ); + return map; + } + public static Map< String, software.amazon.awssdk.services.dynamodb.model.AttributeValue diff --git a/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java b/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java index c37fdf9643..4efb542194 100644 --- a/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java +++ b/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticIT.java @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazonaws.services.dynamodbv2.datamodeling; +import static com.amazonaws.services.dynamodbv2.datamodeling.LegacyTestVectors.baseClassToV1AttrMap; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNull; @@ -107,18 +108,18 @@ public class TransformerHolisticIT { private static final PrivateKey rsaPriv; private static final PublicKey rsaPub; private static final KeyPair rsaPair; - private static final EncryptionMaterialsProvider symProv; + public static final EncryptionMaterialsProvider symProv; private static final EncryptionMaterialsProvider asymProv; private static final EncryptionMaterialsProvider symWrappedProv; protected static final String HASH_KEY = "hashKey"; protected static final String RANGE_KEY = "rangeKey"; - protected static final String BASE_CLASS_TABLE_NAME = "TableName"; + public static final String BASE_CLASS_TABLE_NAME = "TableName"; private static final String RSA = "RSA"; private AmazonDynamoDB client; - private static AWSKMS kmsClient = AWSKMSClientBuilder.standard().build(); + public static AWSKMS kmsClient = AWSKMSClientBuilder.standard().build(); - private static Map keyDataMap = new HashMap<>(); + public static Map keyDataMap = new HashMap<>(); // AttributeEncryptor *must* be used with SaveBehavior.CLOBBER to avoid the risk of data // corruption. @@ -127,7 +128,7 @@ public class TransformerHolisticIT { .builder() .withSaveBehavior(SaveBehavior.CLOBBER) .build(); - private static final BaseClass ENCRYPTED_TEST_VALUE = new BaseClass(); + public static final BaseClass ENCRYPTED_TEST_VALUE = new BaseClass(); private static final Mixed MIXED_TEST_VALUE = new Mixed(); private static final SignOnly SIGNED_TEST_VALUE = new SignOnly(); private static final Untouched UNTOUCHED_TEST_VALUE = new Untouched(); @@ -139,7 +140,7 @@ public class TransformerHolisticIT { private static final String TEST_VECTOR_MANIFEST_DIR = "/vectors/encrypted_item/"; - private static final String SCENARIO_MANIFEST_PATH = + public static final String SCENARIO_MANIFEST_PATH = TEST_VECTOR_MANIFEST_DIR + "scenarios.json"; private static final String JAVA_DIR = "java"; @@ -326,6 +327,28 @@ public void setUp() { createCiphertextTables(client); } + // This test configures EC with various combination. This is different from other tests as other test run in same hardcoded EC. + @Test + public void decryptWithECConfigTest() + throws IOException, GeneralSecurityException { + final String nullTableNameInECCipherFile = + "file://ciphertext/java/static-aes-hmac-null-table-1.json"; + final String nonBMPinECCipherFile = + "file://ciphertext/java/kms-nonbmp-hashkey-1.json"; + final String nonAsciiTableInECCipherFile = + "file://ciphertext/java/static-aes-hmac-nonascii-table-1.json"; + + TransformerHolisticITHelper.decryptNullTableNameInEC( + nullTableNameInECCipherFile + ); + TransformerHolisticITHelper.decryptNonBmpHashKeyVector( + nonBMPinECCipherFile + ); + TransformerHolisticITHelper.decryptNonAsciiTableNameVector( + nonAsciiTableInECCipherFile + ); + } + @Test(dataProvider = "getDecryptTestVectors") public void decryptTestVector(Scenario scenario) throws IOException { client = DynamoDBEmbedded.create().amazonDynamoDB(); @@ -1353,7 +1376,7 @@ private void putDataFromFile(AmazonDynamoDB client, String filename) } } - private Map< + public static Map< String, List> > getCiphertextManifestFromFile(String filename) throws IOException { @@ -1365,7 +1388,7 @@ > getCiphertextManifestFromFile(String filename) throws IOException { ); } - private static T getManifestFromFile( + public static T getManifestFromFile( String filename, TypeReference typeRef ) throws IOException { @@ -1380,7 +1403,7 @@ private static T getManifestFromFile( return (T) manifestMapper.readValue(manifestFile, typeRef); } - private static void loadKeyData(String filename) throws IOException { + static void loadKeyData(String filename) throws IOException { keyDataMap = getManifestFromFile( TEST_VECTOR_MANIFEST_DIR + stripFilePath(filename), diff --git a/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticITHelper.java b/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticITHelper.java new file mode 100644 index 0000000000..40e816a5b3 --- /dev/null +++ b/DynamoDbEncryption/runtimes/java/src/test/sdkv1/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticITHelper.java @@ -0,0 +1,167 @@ +package com.amazonaws.services.dynamodbv2.datamodeling; + +import static com.amazonaws.services.dynamodbv2.datamodeling.LegacyTestVectors.baseClassToV1AttrMap; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.BASE_CLASS_TABLE_NAME; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.ENCRYPTED_TEST_VALUE; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.SCENARIO_MANIFEST_PATH; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.getCiphertextManifestFromFile; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.getManifestFromFile; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.keyDataMap; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.kmsClient; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.loadKeyData; +import static com.amazonaws.services.dynamodbv2.datamodeling.TransformerHolisticIT.symProv; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.DynamoDBEncryptor; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags; +import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.DirectKmsMaterialProvider; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.fasterxml.jackson.core.type.TypeReference; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class TransformerHolisticITHelper { + + public static void decryptNullTableNameInEC( + String nullTableNameInECCipherFile + ) throws IOException, GeneralSecurityException { + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(symProv); + EncryptionContext ctx = new EncryptionContext.Builder() + .withTableName(null) + .withHashKeyName("hashKey") + .withRangeKeyName("rangeKey") + .build(); + + Map expectedAttrMap = baseClassToV1AttrMap( + ENCRYPTED_TEST_VALUE + ); + Map> actions = new HashMap<>(); + for (String attr : expectedAttrMap.keySet()) { + switch (attr) { + case "hashKey": + case "rangeKey": + case "version": + actions.put(attr, EnumSet.of(EncryptionFlags.SIGN)); + break; + default: + actions.put( + attr, + EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN) + ); + break; + } + } + + Map>> manifest = + getCiphertextManifestFromFile(nullTableNameInECCipherFile); + + for (Map encryptedItem : manifest.get( + BASE_CLASS_TABLE_NAME + )) { + Map decrypted = encryptor.decryptRecord( + encryptedItem, + actions, + ctx + ); + assertTrue( + new com.amazonaws.services.dynamodbv2.testing.DdbRecordMatcher( + expectedAttrMap, + false + ) + .matches(decrypted) + ); + } + } + + public static void decryptNonBmpHashKeyVector(String nonBMPinECCipherFile) + throws IOException, GeneralSecurityException { + com.amazonaws.services.dynamodbv2.testing.ScenarioManifest scenarioManifest = + getManifestFromFile( + SCENARIO_MANIFEST_PATH, + new TypeReference< + com.amazonaws.services.dynamodbv2.testing.ScenarioManifest + >() {} + ); + loadKeyData(scenarioManifest.keyDataPath); + + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance( + new DirectKmsMaterialProvider( + kmsClient, + keyDataMap.get("awsKmsUsWest2").keyId + ) + ); + EncryptionContext ctx = new EncryptionContext.Builder() + .withTableName("HashKeyOnly") + .withHashKeyName("hashKey") + .build(); + + Map> actions = new HashMap<>(); + actions.put("hashKey", EnumSet.of(EncryptionFlags.SIGN)); + + String nonBmpKey = "test\uD83D\uDE00Ψkey"; + Map>> manifest = + getCiphertextManifestFromFile(nonBMPinECCipherFile); + for (Map encryptedItem : manifest.get( + BASE_CLASS_TABLE_NAME + )) { + Map decrypted = encryptor.decryptRecord( + encryptedItem, + actions, + ctx + ); + assertEquals(nonBmpKey, decrypted.get("hashKey").getS()); + } + } + + public static void decryptNonAsciiTableNameVector( + String nonAsciiTableInECCipherFile + ) throws IOException, GeneralSecurityException { + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(symProv); + com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext ctx = + new com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext.Builder() + .withTableName("テーブル\uD83D\uDE00") + .withHashKeyName("hashKey") + .withRangeKeyName("rangeKey") + .build(); + + Map expectedAttrMap = baseClassToV1AttrMap( + ENCRYPTED_TEST_VALUE + ); + Map< + String, + Set< + com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags + > + > actions = new HashMap<>(); + for (String attr : expectedAttrMap.keySet()) { + switch (attr) { + case "hashKey": + case "rangeKey": + case "version": + actions.put( + attr, + EnumSet.of( + com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags.SIGN + ) + ); + break; + default: + actions.put( + attr, + EnumSet.of( + com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags.ENCRYPT, + com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionFlags.SIGN + ) + ); + break; + } + } + } +} diff --git a/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticIT.java b/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticIT.java index 8e7f600b20..c17a0f44f6 100644 --- a/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticIT.java +++ b/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticIT.java @@ -16,7 +16,10 @@ import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.providers.WrappedMaterialsProvider; import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.providers.store.MetaStore; import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.providers.store.ProviderStore; -import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.*; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.AttributeValueDeserializer; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.DdbRecordMatcher; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.LocalDynamoDb; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.ScenarioManifest; import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.ScenarioManifest.KeyData; import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.ScenarioManifest.Keys; import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.ScenarioManifest.Scenario; @@ -92,26 +95,28 @@ public class HolisticIT { private static final PrivateKey rsaPriv; private static final PublicKey rsaPub; private static final KeyPair rsaPair; - private static final EncryptionMaterialsProvider symProv; + public static final EncryptionMaterialsProvider symProv; private static final EncryptionMaterialsProvider asymProv; private static final EncryptionMaterialsProvider symWrappedProv; private static final String HASH_KEY = "hashKey"; private static final String RANGE_KEY = "rangeKey"; private static final String RSA = "RSA"; - private static final String tableName = "TableName"; - final EnumSet signOnly = EnumSet.of(EncryptionFlags.SIGN); - final EnumSet encryptAndSign = EnumSet.of( + public static final String tableName = "TableName"; + public static final EnumSet signOnly = EnumSet.of( + EncryptionFlags.SIGN + ); + public static final EnumSet encryptAndSign = EnumSet.of( EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN ); private final LocalDynamoDb localDynamoDb = new LocalDynamoDb(); private DynamoDbClient client; - private static KmsClient kmsClient = KmsClient.builder().build(); + public static KmsClient kmsClient = KmsClient.builder().build(); - private static Map keyDataMap = new HashMap<>(); + public static Map keyDataMap = new HashMap<>(); - private static final Map ENCRYPTED_TEST_VALUE = + public static final Map ENCRYPTED_TEST_VALUE = new HashMap<>(); private static final Map MIXED_TEST_VALUE = new HashMap<>(); @@ -131,7 +136,7 @@ public class HolisticIT { private static final String TEST_VECTOR_MANIFEST_DIR = "/vectors/encrypted_item/"; - private static final String SCENARIO_MANIFEST_PATH = + public static final String SCENARIO_MANIFEST_PATH = TEST_VECTOR_MANIFEST_DIR + "scenarios.json"; private static final String JAVA_DIR = "java"; @@ -535,6 +540,28 @@ public static Object[][] getDecryptTestVectors() throws IOException { .toArray(Object[][]::new); } + // This test configures EC with various combination. This is different from other tests as other test run in same hardcoded EC. + @Test + public void decryptWithECConfigTest() + throws IOException, GeneralSecurityException { + final String nullTableNameInECCipherFile = + "file://ciphertext/java/static-aes-hmac-null-table-1.json"; + final String nonBMPinECCipherFile = + "file://ciphertext/java/kms-nonbmp-hashkey-1.json"; + final String nonAsciiTableInECCipherFile = + "file://ciphertext/java/static-aes-hmac-nonascii-table-1.json"; + + HolisticITHelper.decryptNullTableNameInEC(nullTableNameInECCipherFile); + // decryptNonBmpHashKeyVectorAndWithNullKeyId uses non bmp hash key and also does not use any KMS key id which proves + // that KMS key id is fetched from message format + HolisticITHelper.decryptNonBmpHashKeyVectorAndWithNullKeyId( + nonBMPinECCipherFile + ); + HolisticITHelper.decryptNonAsciiTableNameVector( + nonAsciiTableInECCipherFile + ); + } + @Test(dataProvider = "getDecryptTestVectors") public void decryptTestVector(Scenario scenario) throws IOException, GeneralSecurityException { @@ -890,7 +917,7 @@ private void putDataFromFile(DynamoDbClient localDynamoDb, String filename) } } - private Map< + public static Map< String, List> > getCiphertextManifestFromFile(String filename) throws IOException { @@ -902,7 +929,7 @@ > getCiphertextManifestFromFile(String filename) throws IOException { ); } - private static T getManifestFromFile( + public static T getManifestFromFile( String filename, TypeReference typeRef ) throws IOException { @@ -917,7 +944,7 @@ private static T getManifestFromFile( return (T) manifestMapper.readValue(manifestFile, typeRef); } - private static void loadKeyData(String filename) throws IOException { + public static void loadKeyData(String filename) throws IOException { keyDataMap = getManifestFromFile( TEST_VECTOR_MANIFEST_DIR + stripFilePath(filename), @@ -1124,7 +1151,22 @@ private void assertVersionCompatibility( new DdbRecordMatcher(ENCRYPTED_TEST_VALUE, false).matches(decryptedRecord) ); - assertEquals("Foo", getItems(hashKey1, "HashKeyOnly").get("hashKey").s()); + // Atleast decrypt one item with only hash key. + EncryptionContext hashOnlyContext = EncryptionContext + .builder() + .tableName("HashKeyOnly") + .hashKeyName("hashKey") + .build(); + Map> hashOnlyActions = new HashMap<>(); + hashOnlyActions.put("hashKey", signOnly); + + Map decryptedHashOnlyRecord = + encryptor.decryptRecord( + getItems(hashKey1, "HashKeyOnly"), + hashOnlyActions, + hashOnlyContext + ); + assertEquals("Foo", decryptedHashOnlyRecord.get("hashKey").s()); assertEquals("Bar", getItems(hashKey2, "HashKeyOnly").get("hashKey").s()); assertEquals("Baz", getItems(hashKey3, "HashKeyOnly").get("hashKey").s()); diff --git a/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticITHelper.java b/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticITHelper.java new file mode 100644 index 0000000000..9ac539b162 --- /dev/null +++ b/DynamoDbEncryption/runtimes/java/src/test/sdkv2/com/amazonaws/services/dynamodbv2/datamodeling/sdkv2/HolisticITHelper.java @@ -0,0 +1,138 @@ +package com.amazonaws.services.dynamodbv2.datamodeling.sdkv2; + +import static com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.HolisticIT.*; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.DynamoDBEncryptor; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.EncryptionContext; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.EncryptionFlags; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.encryption.providers.DirectKmsMaterialsProvider; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.DdbRecordMatcher; +import com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.ScenarioManifest; +import com.fasterxml.jackson.core.type.TypeReference; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +public class HolisticITHelper { + + public static void decryptNonAsciiTableNameVector( + String nonAsciiTableInECCipherFile + ) throws IOException, GeneralSecurityException { + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(symProv); + EncryptionContext ctx = EncryptionContext + .builder() + .tableName("テーブル\uD83D\uDE00") + .hashKeyName("hashKey") + .rangeKeyName("rangeKey") + .build(); + + Map> actions = new HashMap<>(); + for (String attr : ENCRYPTED_TEST_VALUE.keySet()) { + switch (attr) { + case "hashKey": + case "rangeKey": + case "version": + actions.put(attr, signOnly); + break; + default: + actions.put(attr, encryptAndSign); + break; + } + } + + Map>> manifest = + getCiphertextManifestFromFile(nonAsciiTableInECCipherFile); + for (Map encryptedItem : manifest.get(tableName)) { + Map decrypted = encryptor.decryptRecord( + encryptedItem, + actions, + ctx + ); + assertTrue( + new DdbRecordMatcher(ENCRYPTED_TEST_VALUE, false).matches(decrypted) + ); + } + } + + public static void decryptNullTableNameInEC( + String nullTableNameInECCipherFile + ) throws IOException, GeneralSecurityException { + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(symProv); + EncryptionContext ctx = EncryptionContext + .builder() + .tableName(null) + .hashKeyName("hashKey") + .rangeKeyName("rangeKey") + .build(); + + Map> actions = new HashMap<>(); + for (String attr : ENCRYPTED_TEST_VALUE.keySet()) { + switch (attr) { + case "hashKey": + case "rangeKey": + case "version": + actions.put(attr, signOnly); + break; + default: + actions.put(attr, encryptAndSign); + break; + } + } + + Map>> manifest = + getCiphertextManifestFromFile(nullTableNameInECCipherFile); + for (Map encryptedItem : manifest.get(tableName)) { + Map decrypted = encryptor.decryptRecord( + encryptedItem, + actions, + ctx + ); + assertTrue( + new DdbRecordMatcher(ENCRYPTED_TEST_VALUE, false).matches(decrypted) + ); + } + } + + public static void decryptNonBmpHashKeyVectorAndWithNullKeyId( + String nonBMPinECCipherFile + ) throws IOException, GeneralSecurityException { + ScenarioManifest scenarioManifest = getManifestFromFile( + SCENARIO_MANIFEST_PATH, + new TypeReference< + com.amazonaws.services.dynamodbv2.datamodeling.sdkv2.testing.ScenarioManifest + >() {} + ); + loadKeyData(scenarioManifest.keyDataPath); + + DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance( + new DirectKmsMaterialsProvider(kmsClient) + ); + EncryptionContext ctx = EncryptionContext + .builder() + .tableName("HashKeyOnly") + .hashKeyName("hashKey") + .build(); + + Map> actions = new HashMap<>(); + actions.put("hashKey", signOnly); + + String nonBmpKey = "test\uD83D\uDE00Ψkey"; + + Map>> manifest = + getCiphertextManifestFromFile(nonBMPinECCipherFile); + for (Map encryptedItem : manifest.get(tableName)) { + Map decrypted = encryptor.decryptRecord( + encryptedItem, + actions, + ctx + ); + assertEquals(nonBmpKey, decrypted.get("hashKey").s()); + } + } +}