Skip to content

Commit 9338557

Browse files
committed
Respect signing algorithm injected through materialDescription by akshays@
1 parent 10849c1 commit 9338557

File tree

8 files changed

+264
-52
lines changed

8 files changed

+264
-52
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646
<version>4.8.1</version>
4747
<scope>test</scope>
4848
</dependency>
49+
50+
<dependency>
51+
<groupId>org.bouncycastle</groupId>
52+
<artifactId>bcprov-ext-jdk15on</artifactId>
53+
<version>1.50</version>
54+
<scope>test</scope>
55+
</dependency>
4956
</dependencies>
5057

5158
<build>

src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptor.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public class DynamoDBEncryptor {
7070
private final String symmetricEncryptionModeHeader;
7171
private final String signingAlgorithmHeader;
7272

73+
public static final String DEFAULT_SIGNING_ALGORITHM_HEADER = DEFAULT_DESCRIPTION_BASE + "signingAlg";
74+
7375
protected DynamoDBEncryptor(EncryptionMaterialsProvider provider, String descriptionBase) {
7476
this.encryptionMaterialsProvider = provider;
7577
this.descriptionBase = descriptionBase;
@@ -306,8 +308,10 @@ public Map<String, AttributeValue> encryptRecord(
306308

307309
// The description must be stored after encryption because its data
308310
// is necessary for proper decryption.
309-
DynamoDBSigner signer = DynamoDBSigner.getInstance(DEFAULT_SIGNATURE_ALGORITHM, rnd);
310-
if (materials.getSigningKey() instanceof PrivateKey) {
311+
final String signingAlgo = materialDescription.get(signingAlgorithmHeader);
312+
DynamoDBSigner signer = DynamoDBSigner.getInstance(signingAlgo == null ? DEFAULT_SIGNATURE_ALGORITHM : signingAlgo, rnd);
313+
314+
if (materials.getSigningKey() instanceof PrivateKey ) {
311315
materialDescription.put(signingAlgorithmHeader, signer.getSigningAlgorithm());
312316
}
313317
if (!materialDescription.isEmpty()) {
@@ -483,6 +487,9 @@ protected static AttributeValue marshallDescription(Map<String, String> descript
483487
}
484488
}
485489

490+
public String getSigningAlgorithmHeader() {
491+
return signingAlgorithmHeader;
492+
}
486493
/**
487494
* @see #marshallDescription(Map)
488495
*/

src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBSigner.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@ class DynamoDBSigner {
5454
private final SecretKey hmacComparisonKey;
5555
private final String signingAlgorithm;
5656

57+
/**
58+
* @param signingAlgorithm
59+
* is the algorithm used for asymmetric signing (ex:
60+
* SHA256withRSA). This is ignored for symmetric HMACs as that
61+
* algorithm is fully specified by the key.
62+
*/
5763
static DynamoDBSigner getInstance(String signingAlgorithm, SecureRandom rnd) {
5864
DynamoDBSigner result = cache.get(signingAlgorithm);
5965
if (result == null) {
60-
// TODO: Support more signing algorithms.
61-
if (!signingAlgorithm.equals("SHA256withRSA")) {
62-
throw new UnsupportedOperationException("Only SHA256withRSA signatures currently supported.");
63-
}
6466
result = new DynamoDBSigner(signingAlgorithm, rnd);
6567
cache.putIfAbsent(signingAlgorithm, result);
6668
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
*/
1515
package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers;
1616

17+
import java.security.GeneralSecurityException;
1718
import java.security.KeyPair;
1819
import java.security.KeyStore;
1920
import java.security.KeyStore.Entry;
2021
import java.security.KeyStore.PrivateKeyEntry;
2122
import java.security.KeyStore.ProtectionParameter;
22-
import java.security.KeyStore.TrustedCertificateEntry;
23-
import java.security.GeneralSecurityException;
2423
import java.security.KeyStore.SecretKeyEntry;
24+
import java.security.KeyStore.TrustedCertificateEntry;
2525
import java.security.KeyStoreException;
2626
import java.security.NoSuchAlgorithmException;
2727
import java.security.PrivateKey;

src/main/java/com/amazonaws/services/dynamodbv2/datamodeling/internal/AttributeValueMarshaller.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,16 @@ public static ByteBuffer marshall(AttributeValue attributeValue) {
110110
out.writeInt(bytes.length);
111111
out.write(bytes);
112112
}
113+
} else if (attributeValue.getBOOL() != null) {
114+
throw new UnsupportedOperationException("BOOL data type has yet to be supported");
115+
} else if (attributeValue.getNULL() != null) {
116+
throw new UnsupportedOperationException("NULL data type has yet to be supported");
117+
} else if (attributeValue.getL() != null) {
118+
throw new UnsupportedOperationException("List data type has yet to be supported");
119+
} else if (attributeValue.getM() != null) {
120+
throw new UnsupportedOperationException("Map data type has yet to be supported");
113121
} else {
114-
out.writeChar('\0');
122+
throw new IllegalArgumentException("A seemingly empty AttributeValue is indicative of invalid input or potential errors");
115123
}
116124
out.close();
117125
return ByteBuffer.wrap(resultBytes.toByteArray());

src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222

2323
import java.nio.ByteBuffer;
2424
import java.security.GeneralSecurityException;
25+
import java.security.InvalidAlgorithmParameterException;
2526
import java.security.KeyPair;
2627
import java.security.KeyPairGenerator;
28+
import java.security.NoSuchAlgorithmException;
29+
import java.security.NoSuchProviderException;
2730
import java.security.SecureRandom;
31+
import java.security.Security;
2832
import java.security.SignatureException;
2933
import java.util.Collection;
3034
import java.util.Collections;
@@ -38,6 +42,9 @@
3842
import javax.crypto.KeyGenerator;
3943
import javax.crypto.SecretKey;
4044

45+
import org.bouncycastle.jce.ECNamedCurveTable;
46+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
47+
import org.bouncycastle.jce.spec.ECParameterSpec;
4148
import org.junit.Assert;
4249
import org.junit.Before;
4350
import org.junit.BeforeClass;
@@ -250,6 +257,37 @@ public void RsaSignedOnlyBadSignature() throws GeneralSecurityException {
250257
encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0]));
251258
}
252259

260+
@Test
261+
public void EcdsaSignedOnly() throws GeneralSecurityException {
262+
263+
encryptor = DynamoDBEncryptor.getInstance(getMaterialProviderwithECDSA());
264+
265+
Map<String, AttributeValue> encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0]));
266+
assertThat(encryptedAttributes, AttrMatcher.invert(attribs));
267+
Map<String, AttributeValue> decryptedAttributes =
268+
encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0]));
269+
assertThat(decryptedAttributes, AttrMatcher.match(attribs));
270+
271+
// Make sure keys and version are not encrypted
272+
assertAttrEquals(attribs.get("hashKey"), encryptedAttributes.get("hashKey"));
273+
assertAttrEquals(attribs.get("rangeKey"), encryptedAttributes.get("rangeKey"));
274+
assertAttrEquals(attribs.get("version"), encryptedAttributes.get("version"));
275+
276+
// Make sure String has not been encrypted (we'll assume the others are correct as well)
277+
assertAttrEquals(attribs.get("stringValue"), encryptedAttributes.get("stringValue"));
278+
}
279+
280+
@Test(expected=SignatureException.class)
281+
public void EcdsaSignedOnlyBadSignature() throws GeneralSecurityException {
282+
283+
encryptor = DynamoDBEncryptor.getInstance(getMaterialProviderwithECDSA());
284+
285+
Map<String, AttributeValue> encryptedAttributes = encryptor.encryptAllFieldsExcept(attribs, context, attribs.keySet().toArray(new String[0]));
286+
assertThat(encryptedAttributes, AttrMatcher.invert(attribs));
287+
encryptedAttributes.get("hashKey").setN("666");
288+
encryptor.decryptAllFieldsExcept(encryptedAttributes, context, attribs.keySet().toArray(new String[0]));
289+
}
290+
253291
private void assertAttrEquals(AttributeValue o1, AttributeValue o2) {
254292
Assert.assertEquals(o1.getB(), o2.getB());
255293
assertSetsEqual(o1.getBS(), o2.getBS());
@@ -268,6 +306,18 @@ private <T> void assertSetsEqual(Collection<T> c1, Collection<T> c2) {
268306
}
269307
}
270308

309+
private EncryptionMaterialsProvider getMaterialProviderwithECDSA()
310+
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException {
311+
Security.addProvider(new BouncyCastleProvider());
312+
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1");
313+
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
314+
g.initialize(ecSpec, new SecureRandom());
315+
KeyPair keypair = g.generateKeyPair();
316+
Map<String, String> description = new HashMap<String, String>();
317+
description.put(DynamoDBEncryptor.DEFAULT_SIGNING_ALGORITHM_HEADER, "SHA384withECDSA");
318+
return new SymmetricStaticProvider(null, keypair, description);
319+
}
320+
271321
private static final class InstrumentedEncryptionMaterialsProvider implements EncryptionMaterialsProvider {
272322
private final EncryptionMaterialsProvider delegate;
273323
private final ConcurrentHashMap<String, AtomicInteger> calls = new ConcurrentHashMap<>();

0 commit comments

Comments
 (0)