Skip to content

Commit 6c23b31

Browse files
Introducing replication feature for Intermediate Keys
1 parent bc3e168 commit 6c23b31

File tree

2 files changed

+95
-29
lines changed
  • src
    • main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store
    • test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store

2 files changed

+95
-29
lines changed

src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStore.java

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,32 @@ public long getVersionFromMaterialDescription(final Map<String, String> descript
142142
throw new IllegalArgumentException("No meta id found");
143143
}
144144
}
145+
146+
/**
147+
* This API retrieves the intermediate keys from the source region and replicates it in the target region.
148+
* @param materialName
149+
* @param version
150+
* @param targetMetaStore
151+
*/
152+
public void replicate(final String materialName, final long version, final MetaStore targetMetaStore) {
153+
try {
154+
final Map<String, AttributeValue> ddbKey = new HashMap<String, AttributeValue>();
155+
ddbKey.put(DEFAULT_HASH_KEY, new AttributeValue().withS(materialName));
156+
ddbKey.put(DEFAULT_RANGE_KEY, new AttributeValue().withN(Long.toString(version)));
157+
final Map<String, AttributeValue> item = ddbGet(ddbKey);
158+
if (item == null || item.isEmpty()) {
159+
throw new IndexOutOfBoundsException("No material found: " + materialName + "#" + version);
160+
}
161+
162+
final Map<String, AttributeValue> plainText = getPlainText(item);
163+
final Map<String, AttributeValue> encryptedText = targetMetaStore.getEncryptedText(plainText);
164+
final PutItemRequest put = new PutItemRequest().withTableName(targetMetaStore.tableName).withItem(encryptedText)
165+
.withExpected(doesNotExist);
166+
targetMetaStore.ddb.putItem(put);
167+
} catch (ConditionalCheckFailedException e) {
168+
//Item already present.
169+
}
170+
}
145171
/**
146172
* Creates a DynamoDB Table with the correct properties to be used with a ProviderStore.
147173
*/
@@ -187,36 +213,43 @@ private Map<String, AttributeValue> encryptKeys(final String name, final long ve
187213
plaintext
188214
.put(INTEGRITY_KEY_FIELD, new AttributeValue().withB(ByteBuffer.wrap(integrityKey.getEncoded())));
189215
plaintext.put(INTEGRITY_ALGORITHM_FIELD, new AttributeValue().withS(integrityKey.getAlgorithm()));
216+
return getEncryptedText(plaintext);
217+
}
218+
219+
private EncryptionMaterialsProvider decryptProvider(final Map<String, AttributeValue> item) {
220+
final Map<String, AttributeValue> plaintext = getPlainText(item);
190221

222+
final String type = plaintext.get(MATERIAL_TYPE_VERSION).getS();
223+
final SecretKey encryptionKey;
224+
final SecretKey integrityKey;
225+
// This switch statement is to make future extensibility easier and more obvious
226+
switch (type) {
227+
case "0": // Only currently supported type
228+
encryptionKey = new SecretKeySpec(plaintext.get(ENCRYPTION_KEY_FIELD).getB().array(),
229+
plaintext.get(ENCRYPTION_ALGORITHM_FIELD).getS());
230+
integrityKey = new SecretKeySpec(plaintext.get(INTEGRITY_KEY_FIELD).getB().array(), plaintext
231+
.get(INTEGRITY_ALGORITHM_FIELD).getS());
232+
break;
233+
default:
234+
throw new IllegalStateException("Unsupported material type: " + type);
235+
}
236+
return new WrappedMaterialsProvider(encryptionKey, encryptionKey, integrityKey,
237+
buildDescription(plaintext));
238+
}
239+
240+
private Map<String, AttributeValue> getPlainText(Map<String, AttributeValue> item) {
191241
try {
192-
return encryptor.encryptAllFieldsExcept(plaintext, ddbCtx, DEFAULT_HASH_KEY,
193-
DEFAULT_RANGE_KEY);
242+
return encryptor.decryptAllFieldsExcept(item,
243+
ddbCtx, DEFAULT_HASH_KEY, DEFAULT_RANGE_KEY);
194244
} catch (final GeneralSecurityException e) {
195245
throw new AmazonClientException(e);
196246
}
197247
}
198248

199-
private EncryptionMaterialsProvider decryptProvider(final Map<String, AttributeValue> item) {
249+
private Map<String, AttributeValue> getEncryptedText(Map<String, AttributeValue> plaintext) {
200250
try {
201-
final Map<String, AttributeValue> plaintext = encryptor.decryptAllFieldsExcept(item,
202-
ddbCtx, DEFAULT_HASH_KEY, DEFAULT_RANGE_KEY);
203-
204-
final String type = plaintext.get(MATERIAL_TYPE_VERSION).getS();
205-
final SecretKey encryptionKey;
206-
final SecretKey integrityKey;
207-
// This switch statement is to make future extensibility easier and more obvious
208-
switch (type) {
209-
case "0": // Only currently supported type
210-
encryptionKey = new SecretKeySpec(plaintext.get(ENCRYPTION_KEY_FIELD).getB().array(),
211-
plaintext.get(ENCRYPTION_ALGORITHM_FIELD).getS());
212-
integrityKey = new SecretKeySpec(plaintext.get(INTEGRITY_KEY_FIELD).getB().array(), plaintext
213-
.get(INTEGRITY_ALGORITHM_FIELD).getS());
214-
break;
215-
default:
216-
throw new IllegalStateException("Unsupported material type: " + type);
217-
}
218-
return new WrappedMaterialsProvider(encryptionKey, encryptionKey, integrityKey,
219-
buildDescription(plaintext));
251+
return encryptor.encryptAllFieldsExcept(plaintext, ddbCtx, DEFAULT_HASH_KEY,
252+
DEFAULT_RANGE_KEY);
220253
} catch (final GeneralSecurityException e) {
221254
throw new AmazonClientException(e);
222255
}

src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/providers/store/MetaStoreTests.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,40 @@
3636
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials;
3737
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.EncryptionMaterialsProvider;
3838
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.SymmetricStaticProvider;
39-
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store.MetaStore;
40-
import com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers.store.ProviderStore;
4139
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
4240

4341
public class MetaStoreTests {
44-
private static final String TABLE_NAME = "keystoreTable";
42+
private static final String SOURCE_TABLE_NAME = "keystoreTable";
43+
private static final String DESTINATION_TABLE_NAME = "keystoreDestinationTable";
4544
private static final String MATERIAL_NAME = "material";
4645
private static final SecretKey AES_KEY = new SecretKeySpec(new byte[] { 0,
4746
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, "AES");
47+
private static final SecretKey TARGET_AES_KEY = new SecretKeySpec(new byte[] { 0,
48+
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 }, "AES");
4849
private static final SecretKey HMAC_KEY = new SecretKeySpec(new byte[] { 0,
4950
1, 2, 3, 4, 5, 6, 7 }, "HmacSHA256");
51+
private static final SecretKey TARGET_HMAC_KEY = new SecretKeySpec(new byte[] { 0,
52+
2, 4, 6, 8, 10, 12, 14 }, "HmacSHA256");
5053
private static final EncryptionMaterialsProvider BASE_PROVIDER = new SymmetricStaticProvider(AES_KEY, HMAC_KEY);
54+
private static final EncryptionMaterialsProvider TARGET_BASE_PROVIDER = new SymmetricStaticProvider(TARGET_AES_KEY, TARGET_HMAC_KEY);
5155
private static final DynamoDBEncryptor ENCRYPTOR = DynamoDBEncryptor.getInstance(BASE_PROVIDER);
56+
private static final DynamoDBEncryptor TARGET_ENCRYPTOR = DynamoDBEncryptor.getInstance(TARGET_BASE_PROVIDER);
5257

5358
private AmazonDynamoDB client;
54-
private ProviderStore store;
59+
private AmazonDynamoDB targetClient;
60+
private MetaStore store;
61+
private MetaStore targetStore;
5562
private EncryptionContext ctx;
5663

5764
@Before
5865
public void setup() {
5966
client = synchronize(DynamoDBEmbedded.create(), AmazonDynamoDB.class);
60-
MetaStore.createTable(client, TABLE_NAME, new ProvisionedThroughput(1L, 1L));
61-
store = new MetaStore(client, TABLE_NAME, ENCRYPTOR);
67+
targetClient = synchronize(DynamoDBEmbedded.create(), AmazonDynamoDB.class);
68+
MetaStore.createTable(client, SOURCE_TABLE_NAME, new ProvisionedThroughput(1L, 1L));
69+
//Creating Targeted DynamoDB Object
70+
MetaStore.createTable(targetClient, DESTINATION_TABLE_NAME, new ProvisionedThroughput(1L, 1L));
71+
store = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR);
72+
targetStore = new MetaStore(targetClient, DESTINATION_TABLE_NAME, TARGET_ENCRYPTOR);
6273
ctx = new EncryptionContext.Builder().build();
6374
}
6475

@@ -172,6 +183,28 @@ public void getOrCreateCollision() {
172183
assertEquals(eMat.getSigningKey(), dMat.getVerificationKey());
173184
}
174185

186+
@Test
187+
public void replicateIntermediateKeysTest() {
188+
assertEquals(-1, store.getMaxVersion(MATERIAL_NAME));
189+
190+
final EncryptionMaterialsProvider prov1 = store.getOrCreate(MATERIAL_NAME, 0);
191+
assertEquals(0, store.getMaxVersion(MATERIAL_NAME));
192+
193+
store.replicate(MATERIAL_NAME, 0, targetStore);
194+
assertEquals(0, targetStore.getMaxVersion(MATERIAL_NAME));
195+
196+
final EncryptionMaterials eMat = prov1.getEncryptionMaterials(ctx);
197+
final DecryptionMaterials dMat = targetStore.getProvider(MATERIAL_NAME, 0).getDecryptionMaterials(ctx(eMat));
198+
199+
assertEquals(eMat.getEncryptionKey(), dMat.getDecryptionKey());
200+
assertEquals(eMat.getSigningKey(), dMat.getVerificationKey());
201+
}
202+
203+
@Test(expected = IndexOutOfBoundsException.class)
204+
public void replicateIntermediateKeysWhenMaterialNotFoundTest() {
205+
store.replicate(MATERIAL_NAME, 0, targetStore);
206+
}
207+
175208
@Test
176209
public void newProviderCollision() throws InterruptedException {
177210
final SlowNewProvider slowProv = new SlowNewProvider();
@@ -207,7 +240,7 @@ private static EncryptionContext ctx(final EncryptionMaterials mat) {
207240

208241
private class SlowNewProvider extends Thread {
209242
public volatile EncryptionMaterialsProvider result;
210-
public ProviderStore slowStore = new MetaStore(client, TABLE_NAME, ENCRYPTOR) {
243+
public ProviderStore slowStore = new MetaStore(client, SOURCE_TABLE_NAME, ENCRYPTOR) {
211244
@Override
212245
public EncryptionMaterialsProvider newProvider(final String materialName) {
213246
final long nextId = getMaxVersion(materialName) + 1;

0 commit comments

Comments
 (0)