Skip to content

Commit a176b7f

Browse files
authored
feat: support context_and_sign in enhanced client (#724)
* feat: support SignAndIncludeInEncryptionContext in enhanced client
1 parent 09398d1 commit a176b7f

File tree

6 files changed

+143
-3
lines changed

6 files changed

+143
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.BeanTableSchemaAttributeTag;
8+
9+
@Target({ElementType.METHOD})
10+
@Retention(RetentionPolicy.RUNTIME)
11+
@BeanTableSchemaAttributeTag(EncryptionAttributeTags.class)
12+
public @interface DynamoDbEncryptionSignAndIncludeInEncryptionContext {
13+
}

DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryption.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import static software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient.DoNothingTag.CUSTOM_DDB_ENCRYPTION_DO_NOTHING_PREFIX;
2626
import static software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient.SignOnlyTag.CUSTOM_DDB_ENCRYPTION_SIGN_ONLY_PREFIX;
27+
import static software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient.SignAndIncludeInEncryptionContextTag.CUSTOM_DDB_ENCRYPTION_SIGN_AND_INCLUDE_PREFIX;
2728

2829
public class DynamoDbEnhancedClientEncryption {
2930
public static DynamoDbEncryptionInterceptor CreateDynamoDbEncryptionInterceptor(
@@ -66,6 +67,7 @@ private static DynamoDbTableEncryptionConfig getTableConfig(
6667

6768
TableSchema<?> topTableSchema = configWithSchema.schemaOnEncrypt();
6869
Set<String> signOnlyAttributes = getSignOnlyAttributes(topTableSchema);
70+
Set<String> signAndIncludeAttributes = getSignAndIncludeInEncryptionContextAttributes(topTableSchema);
6971
Set<String> doNothingAttributes = getDoNothingAttributes(topTableSchema);
7072
Set<String> keyAttributes = attributeNamesUsedInIndices(topTableSchema.tableMetadata());
7173

@@ -81,17 +83,34 @@ private static DynamoDbTableEncryptionConfig getTableConfig(
8183
"Cannot use @DynamoDbEncryptionDoNothing and @DynamoDbEncryptionSignOnly on same attribute. Found on Table Name: %s",
8284
tableName))
8385
.build();
86+
} else if (!Collections.disjoint(signOnlyAttributes, signAndIncludeAttributes)) {
87+
throw DynamoDbEncryptionException.builder()
88+
.message(String.format(
89+
"Cannot use @DynamoDbEncryptionSignAndIncludeInEncryptionContext and @DynamoDbEncryptionSignOnly on same attribute. Found on Table Name: %s",
90+
tableName))
91+
.build();
92+
} else if (!Collections.disjoint(doNothingAttributes, signAndIncludeAttributes)) {
93+
throw DynamoDbEncryptionException.builder()
94+
.message(String.format(
95+
"Cannot use @DynamoDbEncryptionSignAndIncludeInEncryptionContext and @DynamoDbEncryptionDoNothing on same attribute. Found on Table Name: %s",
96+
tableName))
97+
.build();
8498
}
8599

86100
List<String> attributeNames = topTableSchema.attributeNames();
87101
StringBuilder path = new StringBuilder();
88102
path.append(tableName).append(".");
89103
for (String attributeName : attributeNames) {
90104
if (keyAttributes.contains(attributeName)) {
91-
// key attributes are always SIGN_ONLY
92-
actions.put(attributeName, CryptoAction.SIGN_ONLY);
105+
if (signAndIncludeAttributes.isEmpty()) {
106+
actions.put(attributeName, CryptoAction.SIGN_ONLY);
107+
} else {
108+
actions.put(attributeName, CryptoAction.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT);
109+
}
93110
} else if (signOnlyAttributes.contains(attributeName)) {
94111
actions.put(attributeName, CryptoAction.SIGN_ONLY);
112+
} else if (signAndIncludeAttributes.contains(attributeName)) {
113+
actions.put(attributeName, CryptoAction.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT);
95114
} else if (doNothingAttributes.contains(attributeName)) {
96115
actions.put(attributeName, CryptoAction.DO_NOTHING);
97116
} else {
@@ -142,6 +161,13 @@ private static Set<String> getSignOnlyAttributes(TableSchema<?> tableSchema) {
142161
.orElseGet(HashSet::new);
143162
}
144163

164+
@SuppressWarnings("unchecked")
165+
private static Set<String> getSignAndIncludeInEncryptionContextAttributes(TableSchema<?> tableSchema) {
166+
return tableSchema.tableMetadata()
167+
.customMetadataObject(CUSTOM_DDB_ENCRYPTION_SIGN_AND_INCLUDE_PREFIX, Set.class)
168+
.orElseGet(HashSet::new);
169+
}
170+
145171
@SuppressWarnings("unchecked")
146172
private static Set<String> getDoNothingAttributes(TableSchema<?> tableSchema) {
147173
return tableSchema.tableMetadata()
@@ -186,6 +212,16 @@ private static void scanForIgnoredEncryptionTags(
186212
attributePath.append(signOnlyAttributes.toArray()[0])))
187213
.build();
188214
}
215+
Set<String> signAndIncludeAttributes = getSignAndIncludeInEncryptionContextAttributes(subTableSchema);
216+
if (signAndIncludeAttributes.size() > 0) {
217+
throw DynamoDbEncryptionException.builder()
218+
.message(String.format(
219+
"Detected DynamoDbEncryption Tag %s on a nested attribute with Path %s. " +
220+
"This is NOT Supported at this time!",
221+
CUSTOM_DDB_ENCRYPTION_SIGN_AND_INCLUDE_PREFIX,
222+
attributePath.append(signAndIncludeAttributes.toArray()[0])))
223+
.build();
224+
}
189225
Set<String> doNothingAttributes = getDoNothingAttributes(subTableSchema);
190226
if (doNothingAttributes.size() > 0) {
191227
throw DynamoDbEncryptionException.builder()

DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/EncryptionAttributeTags.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public static StaticAttributeTag attributeTagFor(DynamoDbEncryptionSignOnly anno
1010
return new SignOnlyTag();
1111
}
1212

13+
public static StaticAttributeTag attributeTagFor(DynamoDbEncryptionSignAndIncludeInEncryptionContext annotation) {
14+
return new SignAndIncludeInEncryptionContextTag();
15+
}
16+
1317
public static StaticAttributeTag attributeTagFor(DynamoDbEncryptionDoNothing annotation) {
1418
return new DoNothingTag();
1519
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient;
2+
3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.function.Consumer;
6+
7+
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
8+
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTag;
9+
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableMetadata;
10+
11+
public class SignAndIncludeInEncryptionContextTag implements StaticAttributeTag {
12+
public static final String CUSTOM_DDB_ENCRYPTION_SIGN_AND_INCLUDE_PREFIX = "DynamoDbEncryption:SignAndIncludeInEncryptionContext";
13+
14+
@Override
15+
public Consumer<StaticTableMetadata.Builder> modifyMetadata(String attributeName, AttributeValueType attributeValueType) {
16+
return metadata -> metadata
17+
.addCustomMetadataObject(CUSTOM_DDB_ENCRYPTION_SIGN_AND_INCLUDE_PREFIX, Collections.singleton(attributeName));
18+
}
19+
}

DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/enhancedclient/DynamoDbEnhancedClientEncryptionTest.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class DynamoDbEnhancedClientEncryptionTest {
3232
public void TestMultipleTables() {
3333
TableSchema<SimpleClass> simpleSchema = TableSchema.fromBean(SimpleClass.class);
3434
TableSchema<SignOnlyClass> signOnlySchema = TableSchema.fromBean(SignOnlyClass.class);
35+
TableSchema<SignAndIncludeInEncryptionContextClass> signAndIncludeSchema = TableSchema.fromBean(SignAndIncludeInEncryptionContextClass.class);
3536
Map<String, DynamoDbEnhancedTableEncryptionConfig> tableConfigs = new HashMap<>();
3637
tableConfigs.put("SimpleClassTestTable",
3738
DynamoDbEnhancedTableEncryptionConfig.builder()
@@ -46,13 +47,19 @@ public void TestMultipleTables() {
4647
.keyring(createKmsKeyring())
4748
.schemaOnEncrypt(signOnlySchema)
4849
.build());
50+
tableConfigs.put("SignAndIncludeInEncryptionContextClassTestTable",
51+
DynamoDbEnhancedTableEncryptionConfig.builder()
52+
.logicalTableName("SignAndIncludeInEncryptionContextClassTestTable")
53+
.keyring(createKmsKeyring())
54+
.schemaOnEncrypt(signAndIncludeSchema)
55+
.build());
4956
DynamoDbEncryptionInterceptor interceptor =
5057
DynamoDbEnhancedClientEncryption.CreateDynamoDbEncryptionInterceptor(
5158
CreateDynamoDbEncryptionInterceptorInput.builder()
5259
.tableEncryptionConfigs(tableConfigs)
5360
.build()
5461
);
55-
assertEquals(2, interceptor.config().tableEncryptionConfigs().size());
62+
assertEquals(3, interceptor.config().tableEncryptionConfigs().size());
5663

5764
DynamoDbTableEncryptionConfig simpleConfig = interceptor.config().tableEncryptionConfigs().get("SimpleClassTestTable");
5865
assertEquals(CryptoAction.DO_NOTHING, simpleConfig.attributeActionsOnEncrypt().get("doNothing"));
@@ -66,6 +73,12 @@ public void TestMultipleTables() {
6673
assertEquals(CryptoAction.SIGN_ONLY, signOnlyConfig.attributeActionsOnEncrypt().get("sort_key"));
6774
assertEquals(CryptoAction.SIGN_ONLY, signOnlyConfig.attributeActionsOnEncrypt().get("attr1"));
6875
assertEquals(CryptoAction.SIGN_ONLY, signOnlyConfig.attributeActionsOnEncrypt().get("attr2"));
76+
77+
DynamoDbTableEncryptionConfig signAndIncludeConfig = interceptor.config().tableEncryptionConfigs().get("SignAndIncludeInEncryptionContextClassTestTable");
78+
assertEquals(CryptoAction.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT, signAndIncludeConfig.attributeActionsOnEncrypt().get("partition_key"));
79+
assertEquals(CryptoAction.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT, signAndIncludeConfig.attributeActionsOnEncrypt().get("sort_key"));
80+
assertEquals(CryptoAction.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT, signAndIncludeConfig.attributeActionsOnEncrypt().get("attr1"));
81+
assertEquals(CryptoAction.SIGN_ONLY, signAndIncludeConfig.attributeActionsOnEncrypt().get("attr2"));
6982
}
7083

7184
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient.validdatamodels;
2+
3+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute;
4+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
5+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
6+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;
7+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient.DynamoDbEncryptionSignOnly;
8+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.enhancedclient.DynamoDbEncryptionSignAndIncludeInEncryptionContext;
9+
10+
@DynamoDbBean
11+
public class SignAndIncludeInEncryptionContextClass {
12+
13+
private String partitionKey;
14+
private int sortKey;
15+
private String attr1;
16+
private String attr2;
17+
18+
@DynamoDbPartitionKey
19+
@DynamoDbAttribute(value = "partition_key")
20+
public String getPartitionKey() {
21+
return this.partitionKey;
22+
}
23+
24+
public void setPartitionKey(String partitionKey) {
25+
this.partitionKey = partitionKey;
26+
}
27+
28+
@DynamoDbSortKey
29+
@DynamoDbAttribute(value = "sort_key")
30+
public int getSortKey() {
31+
return this.sortKey;
32+
}
33+
34+
public void setSortKey(int sortKey) {
35+
this.sortKey = sortKey;
36+
}
37+
38+
@DynamoDbEncryptionSignAndIncludeInEncryptionContext
39+
public String getAttr1() {
40+
return this.attr1;
41+
}
42+
43+
public void setAttr1(String attr1) {
44+
this.attr1 = attr1;
45+
}
46+
47+
@DynamoDbEncryptionSignOnly
48+
public String getAttr2() {
49+
return this.attr2;
50+
}
51+
52+
public void setAttr2(String attr2) {
53+
this.attr2 = attr2;
54+
}
55+
}

0 commit comments

Comments
 (0)