Skip to content

Commit 623f6f5

Browse files
Make EncryptionMaterials, DecryptionMaterials and KeyringTrace immutable
1 parent 6e473a1 commit 623f6f5

19 files changed

+243
-203
lines changed

src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,7 @@ private EncryptionMaterials getEncryptionMaterialsForKeyring(EncryptionMaterials
137137
.setTrailingSignatureKey(signingKey)
138138
.build();
139139

140-
keyring.onEncrypt(encryptionMaterials);
141-
142-
return encryptionMaterials;
140+
return keyring.onEncrypt(encryptionMaterials);
143141
}
144142

145143
private DecryptionMaterials getDecryptionMaterialsForKeyring(DecryptionMaterialsRequest request) {
@@ -152,13 +150,13 @@ private DecryptionMaterials getDecryptionMaterialsForKeyring(DecryptionMaterials
152150
.setTrailingSignatureKey(verificationKey)
153151
.build();
154152

155-
keyring.onDecrypt(decryptionMaterials, request.getEncryptedDataKeys());
153+
final DecryptionMaterials result = keyring.onDecrypt(decryptionMaterials, request.getEncryptedDataKeys());
156154

157-
if(!decryptionMaterials.hasCleartextDataKey()) {
155+
if(!result.hasCleartextDataKey()) {
158156
throw new CannotUnwrapDataKeyException("Could not decrypt any data keys");
159157
}
160158

161-
return decryptionMaterials;
159+
return result;
162160
}
163161

164162
private PrivateKey getSigningKey(CryptoAlgorithm algorithmSuite, Map<String, String> encryptionContext) {

src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@ public interface Keyring {
2727
/**
2828
* Attempt to encrypt either the given data key (if present) or one that may be generated
2929
*
30-
* @param encryptionMaterials Materials needed for encryption that the keyring may modify.
30+
* @param encryptionMaterials Materials needed for encryption.
31+
* @return Encryption materials with added information provided by this keyring.
3132
*/
32-
void onEncrypt(EncryptionMaterials encryptionMaterials);
33+
EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials);
3334

3435
/**
3536
* Attempt to decrypt the encrypted data keys
3637
*
37-
* @param decryptionMaterials Materials needed for decryption that the keyring may modify.
38+
* @param decryptionMaterials Materials needed for decryption.
3839
* @param encryptedDataKeys List of encrypted data keys.
40+
* @return Decryption materials with added information provided by this keyring.
3941
*/
40-
void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys);
42+
DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys);
4143

4244
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/KeyringTrace.java

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,34 @@
1717
import org.apache.commons.lang3.builder.ToStringStyle;
1818

1919
import java.util.ArrayList;
20-
import java.util.Collections;
2120
import java.util.List;
21+
import java.util.Objects;
22+
23+
import static java.util.Collections.emptyList;
24+
import static java.util.Collections.unmodifiableList;
2225

2326
/**
2427
* A keyring trace containing all of the actions that keyrings have taken on a set of encryption materials.
2528
*/
2629
public class KeyringTrace {
2730

28-
private final List<KeyringTraceEntry> entries = new ArrayList<>();
31+
private final List<KeyringTraceEntry> entries;
32+
public static final KeyringTrace EMPTY_TRACE = new KeyringTrace(emptyList());
2933

30-
/**
31-
* Add a new entry to the keyring trace.
32-
*
33-
* @param keyNamespace The namespace for the key.
34-
* @param keyName The name of the key.
35-
* @param flags A set of one or more KeyringTraceFlag enums
36-
* indicating what actions were taken by a keyring.
37-
*/
38-
public void add(String keyNamespace, String keyName, KeyringTraceFlag... flags) {
39-
add(new KeyringTraceEntry(keyNamespace, keyName, flags));
34+
public KeyringTrace(final List<KeyringTraceEntry> entries) {
35+
this.entries = unmodifiableList(new ArrayList<>(entries));
4036
}
4137

4238
/**
43-
* Add a new entry to the keyring trace.
39+
* Creates a new instance of {@code KeyringTrace} with the provided {@link KeyringTraceEntry}.
4440
*
45-
* @param entry The entry to add.
41+
* @param entry The entry to include in the new {@code KeyringTrace}.
42+
* @return The new {@code KeyringTrace} instance.
4643
*/
47-
public void add(KeyringTraceEntry entry) {
48-
entries.add(entry);
44+
public KeyringTrace with(KeyringTraceEntry entry) {
45+
final List<KeyringTraceEntry> newEntries = new ArrayList<>(entries);
46+
newEntries.add(entry);
47+
return new KeyringTrace(newEntries);
4948
}
5049

5150
/**
@@ -56,7 +55,7 @@ public void add(KeyringTraceEntry entry) {
5655
* @return An unmodifiable list of `KeyringTraceEntry`s
5756
*/
5857
public List<KeyringTraceEntry> getEntries() {
59-
return Collections.unmodifiableList(entries);
58+
return entries;
6059
}
6160

6261
@Override
@@ -65,4 +64,17 @@ public String toString() {
6564
.append("entries", entries)
6665
.toString();
6766
}
67+
68+
@Override
69+
public boolean equals(Object o) {
70+
if (this == o) return true;
71+
if (o == null || getClass() != o.getClass()) return false;
72+
KeyringTrace that = (KeyringTrace) o;
73+
return Objects.equals(entries, that.entries);
74+
}
75+
76+
@Override
77+
public int hashCode() {
78+
return Objects.hash(entries);
79+
}
6880
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/KmsKeyring.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,16 @@ class KmsKeyring implements Keyring {
7070
}
7171

7272
@Override
73-
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
73+
public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) {
7474
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
7575

7676
// If this keyring is a discovery keyring, OnEncrypt MUST return the input encryption materials unmodified.
7777
if (isDiscovery) {
78-
return;
78+
return encryptionMaterials;
7979
}
8080

81+
EncryptionMaterials resultMaterials = encryptionMaterials;
82+
8183
// If the input encryption materials do not contain a plaintext data key and this keyring does not
8284
// have a generator defined, OnEncrypt MUST not modify the encryption materials and MUST fail.
8385
if (!encryptionMaterials.hasCleartextDataKey() && generatorKeyId == null) {
@@ -89,7 +91,7 @@ public void onEncrypt(EncryptionMaterials encryptionMaterials) {
8991
// If the input encryption materials do not contain a plaintext data key and a generator is defined onEncrypt
9092
// MUST attempt to generate a new plaintext data key and encrypt that data key by calling KMS GenerateDataKey.
9193
if (!encryptionMaterials.hasCleartextDataKey()) {
92-
generateDataKey(encryptionMaterials);
94+
resultMaterials = generateDataKey(encryptionMaterials);
9395
} else if (generatorKeyId != null) {
9496
// If this keyring's generator is defined and was not used to generate a data key, OnEncrypt
9597
// MUST also attempt to encrypt the plaintext data key using the CMK specified by the generator.
@@ -99,35 +101,38 @@ public void onEncrypt(EncryptionMaterials encryptionMaterials) {
99101
// Given a plaintext data key in the encryption materials, OnEncrypt MUST attempt
100102
// to encrypt the plaintext data key using each CMK specified in it's key IDs list.
101103
for (String keyId : keyIdsToEncrypt) {
102-
encryptDataKey(keyId, encryptionMaterials);
104+
resultMaterials = encryptDataKey(keyId, resultMaterials);
103105
}
106+
107+
return resultMaterials;
104108
}
105109

106-
private void generateDataKey(final EncryptionMaterials encryptionMaterials) {
110+
private EncryptionMaterials generateDataKey(final EncryptionMaterials encryptionMaterials) {
107111
final GenerateDataKeyResult result = dataKeyEncryptionDao.generateDataKey(generatorKeyId,
108112
encryptionMaterials.getAlgorithm(), encryptionMaterials.getEncryptionContext());
109113

110-
encryptionMaterials.setCleartextDataKey(result.getPlaintextDataKey(),
111-
new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.GENERATED_DATA_KEY));
112-
encryptionMaterials.addEncryptedDataKey(new KeyBlob(result.getEncryptedDataKey()),
113-
new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
114+
return encryptionMaterials
115+
.withCleartextDataKey(result.getPlaintextDataKey(),
116+
new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.GENERATED_DATA_KEY))
117+
.withEncryptedDataKey(new KeyBlob(result.getEncryptedDataKey()),
118+
new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
114119
}
115120

116-
private void encryptDataKey(final String keyId, final EncryptionMaterials encryptionMaterials) {
121+
private EncryptionMaterials encryptDataKey(final String keyId, final EncryptionMaterials encryptionMaterials) {
117122
final EncryptedDataKey encryptedDataKey = dataKeyEncryptionDao.encryptDataKey(keyId,
118123
encryptionMaterials.getCleartextDataKey(), encryptionMaterials.getEncryptionContext());
119124

120-
encryptionMaterials.addEncryptedDataKey(new KeyBlob(encryptedDataKey),
125+
return encryptionMaterials.withEncryptedDataKey(new KeyBlob(encryptedDataKey),
121126
new KeyringTraceEntry(KMS_PROVIDER_ID, keyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
122127
}
123128

124129
@Override
125-
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
130+
public DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
126131
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
127132
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
128133

129134
if (decryptionMaterials.hasCleartextDataKey() || encryptedDataKeys.isEmpty()) {
130-
return;
135+
return decryptionMaterials;
131136
}
132137

133138
final Set<String> configuredKeyIds = new HashSet<>(keyIds);
@@ -142,15 +147,16 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends En
142147
final DecryptDataKeyResult result = dataKeyEncryptionDao.decryptDataKey(encryptedDataKey,
143148
decryptionMaterials.getAlgorithm(), decryptionMaterials.getEncryptionContext());
144149

145-
decryptionMaterials.setCleartextDataKey(result.getPlaintextDataKey(),
150+
return decryptionMaterials.withCleartextDataKey(result.getPlaintextDataKey(),
146151
new KeyringTraceEntry(KMS_PROVIDER_ID, result.getKeyArn(),
147152
KeyringTraceFlag.DECRYPTED_DATA_KEY, KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT));
148-
return;
149153
} catch (CannotUnwrapDataKeyException e) {
150154
continue;
151155
}
152156
}
153157
}
158+
159+
return decryptionMaterials;
154160
}
155161

156162
private boolean okToDecrypt(EncryptedDataKey encryptedDataKey, Set<String> configuredKeyIds) {

src/main/java/com/amazonaws/encryptionsdk/keyrings/MultiKeyring.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,30 +45,34 @@ class MultiKeyring implements Keyring {
4545
}
4646

4747
@Override
48-
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
48+
public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) {
4949
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
5050

51+
EncryptionMaterials resultMaterials = encryptionMaterials;
52+
5153
if (generatorKeyring != null) {
52-
generatorKeyring.onEncrypt(encryptionMaterials);
54+
resultMaterials = generatorKeyring.onEncrypt(encryptionMaterials);
5355
}
5456

55-
if (!encryptionMaterials.hasCleartextDataKey()) {
57+
if (!resultMaterials.hasCleartextDataKey()) {
5658
throw new AwsCryptoException("Either a generator keyring must be supplied that produces a cleartext " +
5759
"data key or a cleartext data key must already be present in the encryption materials.");
5860
}
5961

6062
for (Keyring keyring : childrenKeyrings) {
61-
keyring.onEncrypt(encryptionMaterials);
63+
resultMaterials = keyring.onEncrypt(resultMaterials);
6264
}
65+
66+
return resultMaterials;
6367
}
6468

6569
@Override
66-
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
70+
public DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
6771
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
6872
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
6973

7074
if (decryptionMaterials.hasCleartextDataKey()) {
71-
return;
75+
return decryptionMaterials;
7276
}
7377

7478
final List<Keyring> keyringsToDecryptWith = new ArrayList<>();
@@ -83,11 +87,11 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends En
8387

8488
for (Keyring keyring : keyringsToDecryptWith) {
8589
try {
86-
keyring.onDecrypt(decryptionMaterials, encryptedDataKeys);
90+
final DecryptionMaterials resultMaterials = keyring.onDecrypt(decryptionMaterials, encryptedDataKeys);
8791

88-
if (decryptionMaterials.hasCleartextDataKey()) {
92+
if (resultMaterials.hasCleartextDataKey()) {
8993
// Decryption succeeded, return immediately
90-
return;
94+
return resultMaterials;
9195
}
9296
} catch (Exception e) {
9397
exceptions.add(e);
@@ -100,5 +104,7 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends En
100104
exceptions.forEach(exception::addSuppressed);
101105
throw exception;
102106
}
107+
108+
return decryptionMaterials;
103109
}
104110
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,53 +65,54 @@ abstract class RawKeyring implements Keyring {
6565
abstract boolean validToDecrypt(EncryptedDataKey encryptedDataKey);
6666

6767
@Override
68-
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
68+
public EncryptionMaterials onEncrypt(EncryptionMaterials encryptionMaterials) {
6969
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
7070

71+
EncryptionMaterials resultMaterials = encryptionMaterials;
72+
7173
if (!encryptionMaterials.hasCleartextDataKey()) {
72-
generateDataKey(encryptionMaterials);
74+
resultMaterials = generateDataKey(encryptionMaterials);
7375
}
7476

7577
final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey(
76-
encryptionMaterials.getCleartextDataKey().getEncoded(),
77-
keyName, keyNamespace, encryptionMaterials.getEncryptionContext());
78-
encryptionMaterials.addEncryptedDataKey(new KeyBlob(encryptedDataKey),
78+
resultMaterials.getCleartextDataKey().getEncoded(),
79+
keyName, keyNamespace, resultMaterials.getEncryptionContext());
80+
return resultMaterials.withEncryptedDataKey(new KeyBlob(encryptedDataKey),
7981
new KeyringTraceEntry(keyNamespace, keyName, encryptTraceFlags()));
8082
}
8183

8284
@Override
83-
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
85+
public DecryptionMaterials onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
8486
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
8587
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
8688

8789
if (decryptionMaterials.hasCleartextDataKey() || encryptedDataKeys.isEmpty()) {
88-
return;
90+
return decryptionMaterials;
8991
}
9092

9193
for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) {
9294
if (validToDecrypt(encryptedDataKey)) {
9395
try {
9496
final byte[] decryptedKey = jceKeyCipher.decryptKey(
9597
encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext());
96-
decryptionMaterials.setCleartextDataKey(
98+
return decryptionMaterials.withCleartextDataKey(
9799
new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithm().getDataKeyAlgo()),
98100
new KeyringTraceEntry(keyNamespace, keyName, decryptTraceFlags()));
99-
return;
100101
} catch (Exception e) {
101102
LOGGER.info("Could not decrypt key due to: " + e.getMessage());
102103
}
103104
}
104105
}
105106

106-
LOGGER.warning("Could not decrypt any data keys");
107+
return decryptionMaterials;
107108
}
108109

109-
private void generateDataKey(EncryptionMaterials encryptionMaterials) {
110+
private EncryptionMaterials generateDataKey(EncryptionMaterials encryptionMaterials) {
110111
final byte[] rawKey = new byte[encryptionMaterials.getAlgorithm().getDataKeyLength()];
111112
Utils.getSecureRandom().nextBytes(rawKey);
112113
final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithm().getDataKeyAlgo());
113114

114-
encryptionMaterials.setCleartextDataKey(key, new KeyringTraceEntry(keyNamespace, keyName, GENERATED_DATA_KEY));
115+
return encryptionMaterials.withCleartextDataKey(key, new KeyringTraceEntry(keyNamespace, keyName, GENERATED_DATA_KEY));
115116
}
116117

117118
private KeyringTraceFlag[] encryptTraceFlags() {

src/main/java/com/amazonaws/encryptionsdk/keyrings/StandardKeyrings.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,14 @@ public static Keyring rawRsa(String keyNamespace, String keyName, PublicKey publ
6666
* @param clientSupplier A function that returns a KMS client that can make GenerateDataKey,
6767
* Encrypt, and Decrypt calls in a particular AWS region.
6868
* @param grantTokens A list of string grant tokens to be included in all KMS calls.
69-
* @param keyIds A list of strings identifying KMS CMKs, in ARN, CMK Alias, or ARN Alias format.
70-
* @param generator A string that identifies a KMS CMK responsible for generating a data key,
69+
* @param keyIds A list of strings identifying KMS CMKs used for encrypting and decrypting data keys in
70+
* ARN, CMK Alias, or ARN Alias format.
71+
* @param generatorKeyId A string that identifies a KMS CMK responsible for generating a data key,
7172
* as well as encrypting and decrypting data keys in ARN, CMK Alias, or ARN Alias format.
7273
* @return The {@code Keyring}
7374
*/
74-
public static Keyring kms(KmsClientSupplier clientSupplier, List<String> grantTokens, List<String> keyIds, String generator) {
75-
return new KmsKeyring(DataKeyEncryptionDao.kms(clientSupplier, grantTokens), keyIds, generator);
75+
public static Keyring kms(KmsClientSupplier clientSupplier, List<String> grantTokens, List<String> keyIds, String generatorKeyId) {
76+
return new KmsKeyring(DataKeyEncryptionDao.kms(clientSupplier, grantTokens), keyIds, generatorKeyId);
7677
}
7778

7879
/**

0 commit comments

Comments
 (0)