Skip to content

Commit 36958b8

Browse files
Defining Keyring interface, RawAesKeyring and RawRsaKeyring. (#142)
* Defining Keyring interface, RawAesKeyring and RawRsaKeyring. *Issue #, if available:* #102 *Description of changes:* This change defines the Keyring interface, RawAesKeyring and RawRsaKeyring. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. # Check any applicable: - [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files. * Moving factory methods to StandardKeyrings and correcting RawAes trace. * Adding additional tests for RawAesKeyring and RawRsaKeyring * Creating separate Encryption/Decryption materials for Keyring usage To maintain backward compatibility with MasterKey/MasterKeyProviders, new EncryptionMaterials and DecryptionMaterials classes are defined for use in Keyrings, so they can include names inline with the spec and additional validation. This change also adds test dependencies for JUnit5. * Minor formatting fixes * Fixing comments and migrating KeyringTraceTest to JUnit5 * Renaming algorithm to algorithmSuite * Making optional materials properties throw exceptions if not populated. * Using Objects.requireNonNull and renaming builder methods
1 parent 282cd6f commit 36958b8

20 files changed

+1700
-73
lines changed

pom.xml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,23 @@
5353
</dependency>
5454

5555
<dependency>
56-
<groupId>org.mockito</groupId>
57-
<artifactId>mockito-core</artifactId>
58-
<version>2.28.1</version>
56+
<groupId>org.junit.jupiter</groupId>
57+
<artifactId>junit-jupiter-engine</artifactId>
58+
<version>5.5.2</version>
59+
<scope>test</scope>
60+
</dependency>
61+
62+
<dependency>
63+
<groupId>org.junit.vintage</groupId>
64+
<artifactId>junit-vintage-engine</artifactId>
65+
<version>5.5.2</version>
5966
<scope>test</scope>
6067
</dependency>
6168

6269
<dependency>
63-
<groupId>junit</groupId>
64-
<artifactId>junit</artifactId>
65-
<version>4.12</version>
70+
<groupId>org.mockito</groupId>
71+
<artifactId>mockito-junit-jupiter</artifactId>
72+
<version>3.1.0</version>
6673
<scope>test</scope>
6774
</dependency>
6875

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
//@ model import java.util.Arrays;
1717
//@ model import java.nio.charset.StandardCharsets;
1818

19+
import java.nio.charset.Charset;
20+
import java.nio.charset.StandardCharsets;
1921

2022
//@ nullable_by_default
2123
public interface EncryptedDataKey {
24+
25+
Charset PROVIDER_ENCODING = StandardCharsets.UTF_8;
2226

2327
//@// An EncryptedDataKey object abstractly contains 3 pieces of data.
2428
//@// These are represented by 3 byte arrays:
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package com.amazonaws.encryptionsdk.keyrings;
15+
16+
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
17+
18+
import javax.crypto.SecretKey;
19+
import java.security.PublicKey;
20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
import java.util.Objects;
24+
25+
import static java.util.Objects.requireNonNull;
26+
import static org.apache.commons.lang3.Validate.isTrue;
27+
28+
/**
29+
* Contains the cryptographic materials needed for a decryption operation with Keyrings.
30+
*/
31+
public final class DecryptionMaterials {
32+
private final CryptoAlgorithm algorithmSuite;
33+
private SecretKey plaintextDataKey;
34+
private final PublicKey verificationKey;
35+
private final Map<String, String> encryptionContext;
36+
private final KeyringTrace keyringTrace;
37+
38+
private DecryptionMaterials(Builder b) {
39+
requireNonNull(b.algorithmSuite, "algorithmSuite is required");
40+
requireNonNull(b.keyringTrace, "keyringTrace is required");
41+
requireNonNull(b.encryptionContext, "encryptionContext is required");
42+
validatePlaintextDataKey(b.algorithmSuite, b.plaintextDataKey);
43+
validateVerificationKey(b.algorithmSuite, b.verificationKey);
44+
45+
algorithmSuite = b.algorithmSuite;
46+
plaintextDataKey = b.plaintextDataKey;
47+
verificationKey = b.verificationKey;
48+
encryptionContext = b.encryptionContext;
49+
keyringTrace = b.keyringTrace;
50+
}
51+
52+
/**
53+
* The algorithm suite to use for this decryption operation.
54+
*/
55+
public CryptoAlgorithm getAlgorithmSuite() {
56+
return algorithmSuite;
57+
}
58+
59+
/**
60+
* Returns true if a plaintext data key has been populated.
61+
*
62+
* @return True if plaintext data key is populated, false otherwise.
63+
*/
64+
public boolean hasPlaintextDataKey() {
65+
return this.plaintextDataKey != null;
66+
}
67+
68+
/**
69+
* A data key to be used as input for encryption.
70+
*
71+
* @return The plaintext data key.
72+
* @throws IllegalStateException if plaintext data key has not been populated.
73+
*/
74+
public SecretKey getPlaintextDataKey() throws IllegalStateException {
75+
if (!hasPlaintextDataKey()) {
76+
throw new IllegalStateException("plaintextDataKey has not been populated");
77+
}
78+
return plaintextDataKey;
79+
}
80+
81+
/**
82+
* Sets the plaintext data key. The plaintext data key must not already be populated.
83+
*
84+
* @param plaintextDataKey The plaintext data key.
85+
* @param keyringTraceEntry The keyring trace entry recording this action.
86+
*/
87+
public void setPlaintextDataKey(SecretKey plaintextDataKey, KeyringTraceEntry keyringTraceEntry) {
88+
if (hasPlaintextDataKey()) {
89+
throw new IllegalStateException("plaintextDataKey was already populated");
90+
}
91+
requireNonNull(plaintextDataKey, "plaintextDataKey is required");
92+
requireNonNull(keyringTraceEntry, "keyringTraceEntry is required");
93+
validatePlaintextDataKey(algorithmSuite, plaintextDataKey);
94+
this.plaintextDataKey = plaintextDataKey;
95+
keyringTrace.add(keyringTraceEntry);
96+
}
97+
98+
/**
99+
* Returns true if verification key has been populated.
100+
*
101+
* @return True if verification key is populated, false otherwise.
102+
*/
103+
public boolean hasVerificationKey() {
104+
return verificationKey != null;
105+
}
106+
107+
/**
108+
* The verification key used for signature verification.
109+
*
110+
* @return The verification key.
111+
* @throws IllegalStateException if a verification key has not been populated.
112+
*/
113+
public PublicKey getVerificationKey() throws IllegalStateException {
114+
if (!hasVerificationKey()) {
115+
throw new IllegalStateException(String.format(
116+
"Signature verification is not supported by AlgorithmSuite %s", algorithmSuite.name()));
117+
}
118+
return verificationKey;
119+
}
120+
121+
public Map<String, String> getEncryptionContext() {
122+
return encryptionContext;
123+
}
124+
125+
public KeyringTrace getKeyringTrace() {
126+
return keyringTrace;
127+
}
128+
129+
@Override
130+
public boolean equals(Object o) {
131+
if (this == o) return true;
132+
if (o == null || getClass() != o.getClass()) return false;
133+
DecryptionMaterials that = (DecryptionMaterials) o;
134+
return algorithmSuite == that.algorithmSuite &&
135+
Objects.equals(plaintextDataKey, that.plaintextDataKey) &&
136+
Objects.equals(verificationKey, that.verificationKey) &&
137+
Objects.equals(encryptionContext, that.encryptionContext) &&
138+
Objects.equals(keyringTrace, that.keyringTrace);
139+
}
140+
141+
@Override
142+
public int hashCode() {
143+
return Objects.hash(algorithmSuite, plaintextDataKey, verificationKey, encryptionContext, keyringTrace);
144+
}
145+
146+
public static Builder newBuilder(CryptoAlgorithm algorithm) {
147+
return new Builder(algorithm);
148+
}
149+
150+
public Builder toBuilder() {
151+
return new Builder(this);
152+
}
153+
154+
private void validatePlaintextDataKey(CryptoAlgorithm algorithmSuite, SecretKey plaintextDataKey) throws IllegalArgumentException {
155+
if (plaintextDataKey != null) {
156+
isTrue(algorithmSuite.getDataKeyLength() == plaintextDataKey.getEncoded().length,
157+
String.format("Incorrect key length. Expected %s but got %s",
158+
algorithmSuite.getDataKeyLength(), plaintextDataKey.getEncoded().length));
159+
isTrue(algorithmSuite.getDataKeyAlgo().equalsIgnoreCase(plaintextDataKey.getAlgorithm()),
160+
String.format("Incorrect key algorithm. Expected %s but got %s",
161+
algorithmSuite.getDataKeyAlgo(), plaintextDataKey.getAlgorithm()));
162+
}
163+
}
164+
165+
/**
166+
* Validates that a verification key is specified if and only if
167+
* the given algorithm suite supports signature verification.
168+
*/
169+
private void validateVerificationKey(CryptoAlgorithm algorithmSuite, PublicKey verificationKey) throws IllegalArgumentException {
170+
if (algorithmSuite.getTrailingSignatureAlgo() == null && verificationKey != null) {
171+
throw new IllegalArgumentException(
172+
String.format("Algorithm Suite %s does not support signature verification", algorithmSuite.name()));
173+
} else if (algorithmSuite.getTrailingSignatureAlgo() != null && verificationKey == null) {
174+
throw new IllegalArgumentException(
175+
String.format("Algorithm %s requires a verification key for signature verification", algorithmSuite.name()));
176+
}
177+
}
178+
179+
public static final class Builder {
180+
private CryptoAlgorithm algorithmSuite;
181+
private SecretKey plaintextDataKey;
182+
private PublicKey verificationKey;
183+
private Map<String, String> encryptionContext = Collections.emptyMap();
184+
private KeyringTrace keyringTrace = new KeyringTrace();
185+
186+
private Builder(CryptoAlgorithm algorithmSuite) {
187+
this.algorithmSuite = algorithmSuite;
188+
}
189+
190+
private Builder(DecryptionMaterials result) {
191+
this.algorithmSuite = result.algorithmSuite;
192+
this.plaintextDataKey = result.plaintextDataKey;
193+
this.verificationKey = result.verificationKey;
194+
this.encryptionContext = result.encryptionContext;
195+
this.keyringTrace = result.keyringTrace;
196+
}
197+
198+
public Builder algorithmSuite(CryptoAlgorithm algorithmSuite) {
199+
this.algorithmSuite = algorithmSuite;
200+
return this;
201+
}
202+
203+
public Builder plaintextDataKey(SecretKey plaintextDataKey) {
204+
this.plaintextDataKey = plaintextDataKey;
205+
return this;
206+
}
207+
208+
public Builder verificationKey(PublicKey verificationKey) {
209+
this.verificationKey = verificationKey;
210+
return this;
211+
}
212+
213+
public Builder encryptionContext(Map<String, String> encryptionContext) {
214+
this.encryptionContext = Collections.unmodifiableMap(new HashMap<>(encryptionContext));
215+
return this;
216+
}
217+
218+
public Builder keyringTrace(KeyringTrace keyringTrace) {
219+
this.keyringTrace = keyringTrace;
220+
return this;
221+
}
222+
223+
public DecryptionMaterials build() {
224+
return new DecryptionMaterials(this);
225+
}
226+
}
227+
}

0 commit comments

Comments
 (0)