diff --git a/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java b/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java index d31c615b5..25b4d9c14 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java +++ b/src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java @@ -131,9 +131,11 @@ public DefaultCryptoMaterialsManager(MasterKeyProvider mkp) { } return DecryptionMaterials.newBuilder() - .setDataKey(dataKey) - .setTrailingSignatureKey(pubKey) - .build(); + .setAlgorithm(request.getAlgorithm()) + .setCleartextDataKey(dataKey.getKey()) + .setMasterKey(dataKey.getMasterKey()) + .setTrailingSignatureKey(pubKey) + .build(); } private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey) { diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java b/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java index 386a4d867..5f2729155 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java +++ b/src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java @@ -28,7 +28,6 @@ import com.amazonaws.encryptionsdk.CryptoAlgorithm; import com.amazonaws.encryptionsdk.CryptoMaterialsManager; -import com.amazonaws.encryptionsdk.DataKey; import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager; import com.amazonaws.encryptionsdk.MasterKey; import com.amazonaws.encryptionsdk.MasterKeyProvider; @@ -61,7 +60,8 @@ public class DecryptionHandler> implements MessageCryptoH private CryptoHandler contentCryptoHandler_; - private DataKey dataKey_; + private SecretKey dataKey_; + private K masterKey_; private SecretKey decryptionKey_; private CryptoAlgorithm cryptoAlgo_; private Signature trailingSig_; @@ -454,12 +454,13 @@ private void readHeaderFields(final CiphertextHeaders ciphertextHeaders) { DecryptionMaterials result = materialsManager_.decryptMaterials(request); + dataKey_ = result.getCleartextDataKey(); //noinspection unchecked - dataKey_ = (DataKey)result.getDataKey(); + masterKey_ = (K)result.getMasterKey(); PublicKey trailingPublicKey = result.getTrailingSignatureKey(); try { - decryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(dataKey_.getKey(), ciphertextHeaders); + decryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(dataKey_, ciphertextHeaders); } catch (final InvalidKeyException ex) { throw new AwsCryptoException(ex); } @@ -536,7 +537,7 @@ public CiphertextHeaders getHeaders() { @Override public List getMasterKeys() { - return Collections.singletonList(dataKey_.getMasterKey()); + return Collections.singletonList(masterKey_); } @Override diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java new file mode 100644 index 000000000..cfb331ae6 --- /dev/null +++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.model.DecryptionMaterials; +import com.amazonaws.encryptionsdk.model.EncryptionMaterials; + +import javax.crypto.SecretKey; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.List; + +/** + * Keyrings are responsible for the generation, encryption, and decryption of data keys. + */ +public interface Keyring { + + /** + * Generate a data key if not present and encrypt it using any available wrapping key + * + * @param encryptionMaterials Materials needed for encryption that the keyring may modify. + */ + void onEncrypt(EncryptionMaterials encryptionMaterials); + + /** + * Attempt to decrypt the encrypted data keys + * + * @param decryptionMaterials Materials needed for decryption that the keyring may modify. + * @param encryptedDataKeys List of encrypted data keys. + */ + void onDecrypt(DecryptionMaterials decryptionMaterials, List encryptedDataKeys); + + /** + * Constructs a {@code Keyring} which does local AES-GCM encryption + * decryption of data keys using the provided wrapping key. + * + * @param keyNamespace A UTF-8 encoded value that, together with the key name, identifies the wrapping key. + * @param keyName A UTF-8 encoded value that, together with the key namespace, identifies the wrapping key. + * @param wrappingKey The AES key input to AES-GCM to encrypt plaintext data keys. + * @return The {@link Keyring} + */ + static Keyring rawAes(String keyNamespace, String keyName, SecretKey wrappingKey) { + return new RawAesKeyring(keyNamespace, keyName, wrappingKey); + } + + /** + * Constructs a {@code Keyring} which does local RSA encryption and decryption of data keys using the + * provided public and private keys. If {@code privateKey} is {@code null} then the returned {@code Keyring} + * can only be used for encryption. + * + * @param keyNamespace A UTF-8 encoded value that, together with the key name, identifies the wrapping key. + * @param keyName A UTF-8 encoded value that, together with the key namespace, identifies the wrapping key. + * @param publicKey The RSA public key used by this keyring to encrypt data keys. + * @param privateKey The RSA private key used by this keyring to decrypt data keys. + * @param wrappingAlgorithm The RSA algorithm to use with this keyring. + * @return The {@link Keyring} + */ + static Keyring rawRsa(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, String wrappingAlgorithm) { + return new RawRsaKeyring(keyNamespace, keyName, publicKey, privateKey, wrappingAlgorithm); + } +} diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java new file mode 100644 index 000000000..6785e67d9 --- /dev/null +++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.internal.JceKeyCipher; +import com.amazonaws.encryptionsdk.internal.Utils; + +import javax.crypto.SecretKey; + +/** + * A {@code Keyring} which does local AES-GCM encryption + * decryption of data keys using the provided wrapping key. + * + * Instantiate by using the {@code Keyring.rawAes(...)} factory method. + */ +class RawAesKeyring extends RawKeyring { + + RawAesKeyring(String keyNamespace, String keyName, SecretKey wrappingKey) { + super(keyNamespace, keyName, JceKeyCipher.aesGcm(wrappingKey)); + } + + @Override + boolean validToDecrypt(EncryptedDataKey encryptedDataKey) { + + // the key provider ID of the encrypted data key must + // have a value equal to this keyring's key namespace. + if (!keyNamespace.equals(encryptedDataKey.getProviderId())) { + return false; + } + + // the key name obtained from the encrypted data key's key provider + // information must have a value equal to this keyring's key name. + if (!Utils.arrayPrefixEquals(encryptedDataKey.getProviderInformation(), keyNameBytes, keyNameBytes.length)) { + return false; + } + + return true; + } + + @Override + void traceOnEncrypt(KeyringTrace keyringTrace) { + keyringTrace.add(keyNamespace, keyName, + KeyringTraceFlag.ENCRYPTED_DATA_KEY, + KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT, + KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT); + } + + @Override + void traceOnDecrypt(KeyringTrace keyringTrace) { + keyringTrace.add(keyNamespace, keyName, + KeyringTraceFlag.DECRYPTED_DATA_KEY, + KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT, + KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT); + } +} diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java new file mode 100644 index 000000000..fd090e104 --- /dev/null +++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java @@ -0,0 +1,136 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.internal.JceKeyCipher; +import com.amazonaws.encryptionsdk.internal.Utils; +import com.amazonaws.encryptionsdk.model.DecryptionMaterials; +import com.amazonaws.encryptionsdk.model.EncryptionMaterials; +import com.amazonaws.encryptionsdk.model.KeyBlob; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.logging.Logger; + +import static org.apache.commons.lang3.Validate.notBlank; +import static org.apache.commons.lang3.Validate.notNull; + +/** + * A keyring supporting local encryption and decryption using either RSA or AES-GCM. + */ +abstract class RawKeyring implements Keyring { + + final String keyNamespace; + final String keyName; + final byte[] keyNameBytes; + private final JceKeyCipher jceKeyCipher; + private static final Charset KEY_NAME_ENCODING = StandardCharsets.UTF_8; + private static final Logger LOGGER = Logger.getLogger(RawKeyring.class.getName()); + + RawKeyring(final String keyNamespace, final String keyName, JceKeyCipher jceKeyCipher) { + notBlank(keyNamespace, "keyNamespace is required"); + notBlank(keyName, "keyName is required"); + notNull(jceKeyCipher, "jceKeyCipher is required"); + + this.keyNamespace = keyNamespace; + this.keyName = keyName; + this.keyNameBytes = keyName.getBytes(KEY_NAME_ENCODING); + this.jceKeyCipher = jceKeyCipher; + } + + /** + * Returns true if the given encrypted data key may be decrypted with this keyring. + * + * @param encryptedDataKey The encrypted data key. + * @return True if the key may be decrypted, false otherwise. + */ + abstract boolean validToDecrypt(EncryptedDataKey encryptedDataKey); + + /** + * Records trace entries for the given keyring upon successful encryption. + * + * @param keyringTrace The keyring trace to record to. + */ + abstract void traceOnEncrypt(KeyringTrace keyringTrace); + + /** + * Records trace entries for the given keyring upon successful decryption. + * + * @param keyringTrace The keyring trace to record to. + */ + abstract void traceOnDecrypt(KeyringTrace keyringTrace); + + @Override + public void onEncrypt(EncryptionMaterials encryptionMaterials) { + notNull(encryptionMaterials, "encryptionMaterials are required"); + + if (encryptionMaterials.getCleartextDataKey() == null) { + generateDataKey(encryptionMaterials); + } + + final SecretKey cleartextDataKey = encryptionMaterials.getCleartextDataKey(); + + if (!cleartextDataKey.getAlgorithm().equalsIgnoreCase(encryptionMaterials.getAlgorithm().getDataKeyAlgo())) { + throw new IllegalArgumentException("Incorrect key algorithm. Expected " + cleartextDataKey.getAlgorithm() + + " but got " + encryptionMaterials.getAlgorithm().getDataKeyAlgo()); + } + + final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey( + cleartextDataKey.getEncoded(), keyName, keyNamespace, encryptionMaterials.getEncryptionContext()); + encryptionMaterials.getEncryptedDataKeys().add(new KeyBlob(encryptedDataKey)); + + traceOnEncrypt(encryptionMaterials.getKeyringTrace()); + } + + @Override + public void onDecrypt(DecryptionMaterials decryptionMaterials, List encryptedDataKeys) { + notNull(decryptionMaterials, "decryptionMaterials are required"); + notNull(encryptedDataKeys, "encryptedDataKeys are required"); + + if (decryptionMaterials.getCleartextDataKey() != null) { + return; + } + + for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) { + if (validToDecrypt(encryptedDataKey)) { + try { + final byte[] decryptedKey = jceKeyCipher.decryptKey( + encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext()); + decryptionMaterials.setCleartextDataKey( + new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithm().getDataKeyAlgo())); + traceOnDecrypt(decryptionMaterials.getKeyringTrace()); + return; + } catch (Exception e) { + LOGGER.info("Could not decrypt key due to: " + e.getMessage()); + } + } + } + + LOGGER.warning("Could not decrypt any data keys"); + } + + private void generateDataKey(EncryptionMaterials encryptionMaterials) { + final byte[] rawKey = new byte[encryptionMaterials.getAlgorithm().getDataKeyLength()]; + Utils.getSecureRandom().nextBytes(rawKey); + final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithm().getDataKeyAlgo()); + + encryptionMaterials.setCleartextDataKey(key); + encryptionMaterials.getKeyringTrace().add(keyNamespace, keyName, KeyringTraceFlag.GENERATED_DATA_KEY); + } +} diff --git a/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java new file mode 100644 index 000000000..0ba858efd --- /dev/null +++ b/src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.internal.JceKeyCipher; + +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Arrays; + +/** + * A {@link Keyring} which does local RSA encryption and decryption of data keys using the + * provided public and private keys. + * + * Instantiate by using the {@code Keyring.rawRsa(...)} factory method. + */ +class RawRsaKeyring extends RawKeyring { + + RawRsaKeyring(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, String transformation) { + super(keyNamespace, keyName, JceKeyCipher.rsa(publicKey, privateKey, transformation)); + } + + @Override + boolean validToDecrypt(EncryptedDataKey encryptedDataKey) { + + // the key provider ID of the encrypted data key must + // have a value equal to this keyring's key namespace. + if (!keyNamespace.equals(encryptedDataKey.getProviderId())) { + return false; + } + + // the encrypted data key's key provider information + // must have a value equal to this keyring's key name. + if(!Arrays.equals(encryptedDataKey.getProviderInformation(), keyNameBytes)) + { + return false; + } + + return true; + } + + @Override + void traceOnEncrypt(KeyringTrace keyringTrace) { + keyringTrace.add(keyNamespace, keyName, KeyringTraceFlag.ENCRYPTED_DATA_KEY); + } + + @Override + void traceOnDecrypt(KeyringTrace keyringTrace) { + keyringTrace.add(keyNamespace, keyName, KeyringTraceFlag.DECRYPTED_DATA_KEY); + } +} diff --git a/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java b/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java index 0c0ba52c7..c72ce83c0 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java +++ b/src/main/java/com/amazonaws/encryptionsdk/model/DecryptionMaterials.java @@ -1,29 +1,70 @@ package com.amazonaws.encryptionsdk.model; import java.security.PublicKey; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; -import com.amazonaws.encryptionsdk.DataKey; +import com.amazonaws.encryptionsdk.CryptoAlgorithm; +import com.amazonaws.encryptionsdk.MasterKey; import com.amazonaws.encryptionsdk.keyrings.KeyringTrace; +import javax.crypto.SecretKey; + public final class DecryptionMaterials { - private final DataKey dataKey; + private final CryptoAlgorithm algorithm; + private SecretKey cleartextDataKey; + private final MasterKey masterKey; private final PublicKey trailingSignatureKey; + private final Map encryptionContext; private final KeyringTrace keyringTrace; private DecryptionMaterials(Builder b) { - dataKey = b.getDataKey(); + algorithm = b.getAlgorithm(); + cleartextDataKey = b.getCleartextDataKey(); + masterKey = b.getMasterKey(); trailingSignatureKey = b.getTrailingSignatureKey(); + encryptionContext = b.getEncryptionContext(); keyringTrace = b.getKeyringTrace(); } - public DataKey getDataKey() { - return dataKey; + /** + * The algorithm to use for this decryption operation. Must match the algorithm in DecryptionMaterialsRequest, if that + * algorithm was non-null. + */ + public CryptoAlgorithm getAlgorithm() { + return algorithm; + } + + public SecretKey getCleartextDataKey() { + return cleartextDataKey; + } + + public void setCleartextDataKey(SecretKey cleartextDataKey) { + if(this.cleartextDataKey != null) { + throw new IllegalStateException("cleartextDataKey was already populated"); + } + + this.cleartextDataKey = cleartextDataKey; + } + + /** + * Gets the MasterKey (if any) used for decrypting the data key. Will be null + * if a KeyRing was used instead of a MasterKeyProvider. + * @return The MasterKey + */ + public MasterKey getMasterKey() { + return masterKey; } public PublicKey getTrailingSignatureKey() { return trailingSignatureKey; } + public Map getEncryptionContext() { + return encryptionContext; + } + public KeyringTrace getKeyringTrace() { return keyringTrace; } @@ -37,24 +78,47 @@ public Builder toBuilder() { } public static final class Builder { - private DataKey dataKey; + private CryptoAlgorithm algorithm; + private SecretKey cleartextDataKey; + private MasterKey masterKey; private PublicKey trailingSignatureKey; + private Map encryptionContext; private KeyringTrace keyringTrace; private Builder(DecryptionMaterials result) { - this.dataKey = result.getDataKey(); + this.algorithm = result.getAlgorithm(); + this.cleartextDataKey = result.getCleartextDataKey(); + this.masterKey = result.getMasterKey(); this.trailingSignatureKey = result.getTrailingSignatureKey(); this.keyringTrace = result.getKeyringTrace(); } private Builder() {} - public DataKey getDataKey() { - return dataKey; + public CryptoAlgorithm getAlgorithm() { + return algorithm; + } + + public Builder setAlgorithm(CryptoAlgorithm algorithm) { + this.algorithm = algorithm; + return this; + } + + public SecretKey getCleartextDataKey() { + return cleartextDataKey; } - public Builder setDataKey(DataKey dataKey) { - this.dataKey = dataKey; + public Builder setCleartextDataKey(SecretKey cleartextDataKey) { + this.cleartextDataKey = cleartextDataKey; + return this; + } + + public MasterKey getMasterKey() { + return masterKey; + } + + public Builder setMasterKey(MasterKey masterKey) { + this.masterKey = masterKey; return this; } @@ -67,6 +131,15 @@ public Builder setTrailingSignatureKey(PublicKey trailingSignatureKey) { return this; } + public Map getEncryptionContext() { + return encryptionContext; + } + + public Builder setEncryptionContext(Map encryptionContext) { + this.encryptionContext = Collections.unmodifiableMap(new HashMap<>(encryptionContext)); + return this; + } + public KeyringTrace getKeyringTrace() { return keyringTrace; } diff --git a/src/main/java/com/amazonaws/encryptionsdk/model/EncryptionMaterials.java b/src/main/java/com/amazonaws/encryptionsdk/model/EncryptionMaterials.java index f9b05a153..8c912adc1 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/model/EncryptionMaterials.java +++ b/src/main/java/com/amazonaws/encryptionsdk/model/EncryptionMaterials.java @@ -22,7 +22,7 @@ public final class EncryptionMaterials { private final CryptoAlgorithm algorithm; private final Map encryptionContext; private final List encryptedDataKeys; - private final SecretKey cleartextDataKey; + private SecretKey cleartextDataKey; private final PrivateKey trailingSignatureKey; private final List masterKeys; private final KeyringTrace keyringTrace; @@ -75,6 +75,14 @@ public SecretKey getCleartextDataKey() { return cleartextDataKey; } + public void setCleartextDataKey(SecretKey cleartextDataKey) { + if(this.cleartextDataKey != null) { + throw new IllegalStateException("cleartextDataKey was already populated"); + } + + this.cleartextDataKey = cleartextDataKey; + } + /** * The private key to be used to sign the message trailer. Must be present if any only if required by the * crypto algorithm, and the key type must likewise match the algorithm in use. @@ -87,6 +95,10 @@ public PrivateKey getTrailingSignatureKey() { return trailingSignatureKey; } + public KeyringTrace getKeyringTrace() { + return keyringTrace; + } + /** * Contains a list of all MasterKeys that could decrypt this message. */ @@ -115,7 +127,7 @@ public List getMasterKeys() { public static class Builder { private CryptoAlgorithm algorithm; private Map encryptionContext = Collections.emptyMap(); - private List encryptedDataKeys = null; + private List encryptedDataKeys = new ArrayList<>(); private SecretKey cleartextDataKey; private PrivateKey trailingSignatureKey; private List masterKeys = Collections.emptyList(); @@ -160,7 +172,7 @@ public List getEncryptedDataKeys() { } public Builder setEncryptedDataKeys(List encryptedDataKeys) { - this.encryptedDataKeys = Collections.unmodifiableList(new ArrayList<>(encryptedDataKeys)); + this.encryptedDataKeys = new ArrayList<>(encryptedDataKeys); return this; } diff --git a/src/test/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManagerTest.java b/src/test/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManagerTest.java index 9f693fe28..d831e3c8b 100644 --- a/src/test/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManagerTest.java +++ b/src/test/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManagerTest.java @@ -2,7 +2,6 @@ import static com.amazonaws.encryptionsdk.multi.MultipleProviderFactory.buildMultiProvider; import static java.util.Collections.singletonMap; -import static org.hamcrest.Matchers.hasEntry; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -250,7 +249,7 @@ public void decrypt_testSimpleRoundTrip() throws Exception { DecryptionMaterials decryptMaterials = new DefaultCryptoMaterialsManager(mk1).decryptMaterials(decryptReqFromMaterials(encryptMaterials)); - assertArrayEquals(decryptMaterials.getDataKey().getKey().getEncoded(), + assertArrayEquals(decryptMaterials.getCleartextDataKey().getEncoded(), encryptMaterials.getCleartextDataKey().getEncoded()); if (encryptMaterials.getTrailingSignatureKey() == null) { diff --git a/src/test/java/com/amazonaws/encryptionsdk/caching/CacheTestFixtures.java b/src/test/java/com/amazonaws/encryptionsdk/caching/CacheTestFixtures.java index bda4343d3..4d385ca5f 100644 --- a/src/test/java/com/amazonaws/encryptionsdk/caching/CacheTestFixtures.java +++ b/src/test/java/com/amazonaws/encryptionsdk/caching/CacheTestFixtures.java @@ -1,12 +1,9 @@ package com.amazonaws.encryptionsdk.caching; -import static org.mockito.ArgumentMatchers.eq; - import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Collections; -import com.amazonaws.encryptionsdk.DataKey; import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager; import com.amazonaws.encryptionsdk.MasterKey; import com.amazonaws.encryptionsdk.TestUtils; @@ -56,10 +53,8 @@ public static DecryptionMaterials createDecryptResult(DecryptionMaterialsRequest DecryptionMaterials realResult = new DefaultCryptoMaterialsManager(FIXED_KEY).decryptMaterials(request); return realResult .toBuilder() - .setDataKey(new DataKey(new SentinelKey(), - realResult.getDataKey().getEncryptedDataKey(), - realResult.getDataKey().getProviderInformation(), - realResult.getDataKey().getMasterKey())) + .setCleartextDataKey(realResult.getCleartextDataKey()) + .setMasterKey(realResult.getMasterKey()) .build(); } diff --git a/src/test/java/com/amazonaws/encryptionsdk/caching/LocalCryptoMaterialsCacheThreadStormTest.java b/src/test/java/com/amazonaws/encryptionsdk/caching/LocalCryptoMaterialsCacheThreadStormTest.java index 804b148ac..e2ac53637 100644 --- a/src/test/java/com/amazonaws/encryptionsdk/caching/LocalCryptoMaterialsCacheThreadStormTest.java +++ b/src/test/java/com/amazonaws/encryptionsdk/caching/LocalCryptoMaterialsCacheThreadStormTest.java @@ -1,9 +1,6 @@ package com.amazonaws.encryptionsdk.caching; -import static com.amazonaws.encryptionsdk.caching.CacheTestFixtures.createMaterialsResult; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.reflect.Field; @@ -28,7 +25,6 @@ import org.junit.Test; -import com.amazonaws.encryptionsdk.DataKey; import com.amazonaws.encryptionsdk.caching.CryptoMaterialsCache.UsageStats; import com.amazonaws.encryptionsdk.model.DecryptionMaterials; import com.amazonaws.encryptionsdk.model.EncryptionMaterials; @@ -137,9 +133,9 @@ public void decryptAddThread() { ref[0] = 0; CacheTestFixtures.SentinelKey key = new CacheTestFixtures.SentinelKey(); - DecryptionMaterials result = BASE_DECRYPT.toBuilder().setDataKey( - new DataKey(key, new byte[0], new byte[0], BASE_DECRYPT.getDataKey().getMasterKey()) - ).build(); + DecryptionMaterials result = BASE_DECRYPT.toBuilder() + .setCleartextDataKey(key) + .setMasterKey(BASE_DECRYPT.getMasterKey()).build(); ConcurrentHashMap expectedDecryptMap = possibleDecrypts.computeIfAbsent(ByteBuffer.wrap(ref), @@ -183,7 +179,7 @@ public void decryptCheckThread() { CacheTestFixtures.SentinelKey cachedKey = null; if (result != null) { inc("decrypt: hit"); - cachedKey = (CacheTestFixtures.SentinelKey) result.getResult().getDataKey().getKey(); + cachedKey = (CacheTestFixtures.SentinelKey) result.getResult().getCleartextDataKey(); if (expectedDecryptMap.containsKey(cachedKey)) { inc("decrypt: found key in expected"); } else { diff --git a/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyringTest.java b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyringTest.java new file mode 100644 index 000000000..eb8148f55 --- /dev/null +++ b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyringTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.model.KeyBlob; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; + +import static com.amazonaws.encryptionsdk.keyrings.RawKeyringTest.KEYNAME; +import static com.amazonaws.encryptionsdk.keyrings.RawKeyringTest.KEYNAMESPACE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class RawAesKeyringTest { + + @Mock + private SecretKey wrappingKey; + + @Test + public void testValidToDecrypt() { + RawAesKeyring rawAesKeyring = new RawAesKeyring(KEYNAMESPACE, KEYNAME, wrappingKey); + + assertTrue(rawAesKeyring.validToDecrypt(new KeyBlob( + KEYNAMESPACE, KEYNAME.getBytes(StandardCharsets.UTF_8), new byte[]{}))); + assertTrue(rawAesKeyring.validToDecrypt(new KeyBlob( + KEYNAMESPACE, ArrayUtils.add(KEYNAME.getBytes(StandardCharsets.UTF_8), (byte)5), new byte[]{}))); + //Bad namespace + assertFalse(rawAesKeyring.validToDecrypt(new KeyBlob( + "WrongNamespace", KEYNAME.getBytes(StandardCharsets.UTF_8), new byte[]{}))); + //Bad provider info + assertFalse(rawAesKeyring.validToDecrypt(new KeyBlob( + KEYNAMESPACE, new byte[]{1,2,3}, new byte[]{}))); + } + + @Test + public void testTraceOnEncrypt() { + RawAesKeyring rawAesKeyring = new RawAesKeyring(KEYNAMESPACE, KEYNAME, wrappingKey); + + KeyringTrace trace = new KeyringTrace(); + + rawAesKeyring.traceOnEncrypt(trace); + assertEquals(1, trace.getEntries().size()); + assertEquals(KEYNAME, trace.getEntries().get(0).getKeyName()); + assertEquals(KEYNAMESPACE, trace.getEntries().get(0).getKeyNamespace()); + assertEquals(3, trace.getEntries().get(0).getFlags().size()); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.ENCRYPTED_DATA_KEY)); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT)); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT)); + } + + @Test + public void testTraceOnDecrypt() { + RawAesKeyring rawAesKeyring = new RawAesKeyring(KEYNAMESPACE, KEYNAME, wrappingKey); + + KeyringTrace trace = new KeyringTrace(); + + rawAesKeyring.traceOnDecrypt(trace); + assertEquals(1, trace.getEntries().size()); + assertEquals(KEYNAME, trace.getEntries().get(0).getKeyName()); + assertEquals(KEYNAMESPACE, trace.getEntries().get(0).getKeyNamespace()); + assertEquals(3, trace.getEntries().get(0).getFlags().size()); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.DECRYPTED_DATA_KEY)); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT)); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT)); + } + +} diff --git a/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawKeyringTest.java b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawKeyringTest.java new file mode 100644 index 000000000..842210f80 --- /dev/null +++ b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawKeyringTest.java @@ -0,0 +1,245 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.CryptoAlgorithm; +import com.amazonaws.encryptionsdk.EncryptedDataKey; +import com.amazonaws.encryptionsdk.internal.JceKeyCipher; +import com.amazonaws.encryptionsdk.model.DecryptionMaterials; +import com.amazonaws.encryptionsdk.model.EncryptionMaterials; +import com.amazonaws.encryptionsdk.model.KeyBlob; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RawKeyringTest { + + static final String KEYNAME = "testKeyname"; + static final String KEYNAMESPACE = "testKeynamespace"; + private static final CryptoAlgorithm ALGORITHM = CryptoAlgorithm.ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384; + static final SecretKey DATA_KEY = new SecretKeySpec(new byte[]{10, 11, 12}, ALGORITHM.getDataKeyAlgo()); + private static final EncryptedDataKey ENCRYPTED_DATA_KEY = new KeyBlob("keyProviderId", new byte[]{1, 2, 3}, new byte[]{4, 5, 6}); + private static final Map ENCRYPTION_CONTEXT = Collections.singletonMap("myKey", "myValue"); + private static final KeyringTraceEntry ENCRYPTED_DATA_KEY_TRACE = new KeyringTraceEntry(KEYNAMESPACE, KEYNAME, Collections.singleton(KeyringTraceFlag.ENCRYPTED_DATA_KEY)); + private static final KeyringTraceEntry DECRYPTED_DATA_KEY_TRACE = new KeyringTraceEntry(KEYNAMESPACE, KEYNAME, Collections.singleton(KeyringTraceFlag.DECRYPTED_DATA_KEY)); + private static final KeyringTraceEntry GENERATED_DATA_KEY_TRACE = new KeyringTraceEntry(KEYNAMESPACE, KEYNAME, Collections.singleton(KeyringTraceFlag.GENERATED_DATA_KEY)); + @Mock + private JceKeyCipher jceKeyCipher; + @Captor + private ArgumentCaptor dataKeyCaptor; + + @Test + public void testEncryptDecryptExistingDataKey() throws GeneralSecurityException { + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> true, ENCRYPTED_DATA_KEY_TRACE, DECRYPTED_DATA_KEY_TRACE); + + EncryptionMaterials encryptionMaterials = EncryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setCleartextDataKey(DATA_KEY) + .setKeyringTrace(new KeyringTrace()) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .build(); + + when(jceKeyCipher.encryptKey(DATA_KEY.getEncoded(), KEYNAME, KEYNAMESPACE, ENCRYPTION_CONTEXT)).thenReturn(ENCRYPTED_DATA_KEY); + keyring.onEncrypt(encryptionMaterials); + + assertEquals(1, encryptionMaterials.getEncryptedDataKeys().size()); + assertEncryptedDataKeyEquals(ENCRYPTED_DATA_KEY, encryptionMaterials.getEncryptedDataKeys().get(0)); + assertEquals(1, encryptionMaterials.getKeyringTrace().getEntries().size()); + assertEquals(ENCRYPTED_DATA_KEY_TRACE, encryptionMaterials.getKeyringTrace().getEntries().get(0)); + + DecryptionMaterials decryptionMaterials = DecryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .setKeyringTrace(new KeyringTrace()) + .build(); + + when(jceKeyCipher.decryptKey(encryptionMaterials.getEncryptedDataKeys().get(0), KEYNAME, ENCRYPTION_CONTEXT)).thenReturn(DATA_KEY.getEncoded()); + keyring.onDecrypt(decryptionMaterials, encryptionMaterials.getEncryptedDataKeys()); + + assertEquals(DATA_KEY, decryptionMaterials.getCleartextDataKey()); + assertEquals(DECRYPTED_DATA_KEY_TRACE, decryptionMaterials.getKeyringTrace().getEntries().get(0)); + } + + @Test + public void testEncryptNullDataKey() { + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> true, ENCRYPTED_DATA_KEY_TRACE, null); + + EncryptionMaterials encryptionMaterials = EncryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setKeyringTrace(new KeyringTrace()) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .build(); + + when(jceKeyCipher.encryptKey(dataKeyCaptor.capture(), eq(KEYNAME), eq(KEYNAMESPACE), eq(ENCRYPTION_CONTEXT))).thenReturn(ENCRYPTED_DATA_KEY); + keyring.onEncrypt(encryptionMaterials); + + assertEquals(encryptionMaterials.getCleartextDataKey().getAlgorithm(), ALGORITHM.getDataKeyAlgo()); + assertArrayEquals(encryptionMaterials.getCleartextDataKey().getEncoded(), dataKeyCaptor.getValue()); + assertEquals(1, encryptionMaterials.getEncryptedDataKeys().size()); + assertNotNull(encryptionMaterials.getCleartextDataKey()); + assertEncryptedDataKeyEquals(ENCRYPTED_DATA_KEY, encryptionMaterials.getEncryptedDataKeys().get(0)); + assertEquals(2, encryptionMaterials.getKeyringTrace().getEntries().size()); + assertEquals(GENERATED_DATA_KEY_TRACE, encryptionMaterials.getKeyringTrace().getEntries().get(0)); + assertEquals(ENCRYPTED_DATA_KEY_TRACE, encryptionMaterials.getKeyringTrace().getEntries().get(1)); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncryptBadDataKeyAlgorithm() { + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> true, null, null); + + EncryptionMaterials encryptionMaterials = EncryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setCleartextDataKey(new SecretKeySpec(DATA_KEY.getEncoded(), "OtherAlgorithm")) + .setKeyringTrace(new KeyringTrace()) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .build(); + + keyring.onEncrypt(encryptionMaterials); + } + + @Test + public void testDecryptAlreadyDecryptedDataKey() { + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> true, null, null); + + DecryptionMaterials decryptionMaterials = DecryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setCleartextDataKey(DATA_KEY) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .setKeyringTrace(new KeyringTrace()) + .build(); + + keyring.onDecrypt(decryptionMaterials, Collections.singletonList(ENCRYPTED_DATA_KEY)); + + assertEquals(DATA_KEY, decryptionMaterials.getCleartextDataKey()); + assertEquals(0, decryptionMaterials.getKeyringTrace().getEntries().size()); + } + + @Test + public void testDecryptNoValidDataKey() { + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> false, null, null); + + DecryptionMaterials decryptionMaterials = DecryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .setKeyringTrace(new KeyringTrace()) + .build(); + + keyring.onDecrypt(decryptionMaterials, Collections.singletonList(ENCRYPTED_DATA_KEY)); + + assertNull(decryptionMaterials.getCleartextDataKey()); + assertEquals(0, decryptionMaterials.getKeyringTrace().getEntries().size()); + } + + @Test + public void testDecryptMultipleKeysOneInvalid() throws GeneralSecurityException { + final EncryptedDataKey BAD_DATA_KEY = new KeyBlob("badProviderId", new byte[]{1, 2, 3}, new byte[]{4, 5, 6}); + + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> !edk.getProviderId().equals(BAD_DATA_KEY.getProviderId()), null, DECRYPTED_DATA_KEY_TRACE); + + DecryptionMaterials decryptionMaterials = DecryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .setKeyringTrace(new KeyringTrace()) + .build(); + + when(jceKeyCipher.decryptKey(ENCRYPTED_DATA_KEY, KEYNAME, ENCRYPTION_CONTEXT)).thenReturn(DATA_KEY.getEncoded()); + + final List edks = new ArrayList<>(); + edks.add(BAD_DATA_KEY); + edks.add(ENCRYPTED_DATA_KEY); + + keyring.onDecrypt(decryptionMaterials, edks); + + assertEquals(DATA_KEY, decryptionMaterials.getCleartextDataKey()); + assertEquals(DECRYPTED_DATA_KEY_TRACE, decryptionMaterials.getKeyringTrace().getEntries().get(0)); + } + + @Test + public void testDecryptMultipleKeysOneException() throws GeneralSecurityException { + final EncryptedDataKey BAD_DATA_KEY = new KeyBlob("badProviderId", new byte[]{1, 2, 3}, new byte[]{4, 5, 6}); + + Keyring keyring = newRawKeyring(jceKeyCipher, (edk) -> true, null, DECRYPTED_DATA_KEY_TRACE); + + DecryptionMaterials decryptionMaterials = DecryptionMaterials.newBuilder() + .setAlgorithm(ALGORITHM) + .setEncryptionContext(ENCRYPTION_CONTEXT) + .setKeyringTrace(new KeyringTrace()) + .build(); + + when(jceKeyCipher.decryptKey(BAD_DATA_KEY, KEYNAME, ENCRYPTION_CONTEXT)) + .thenThrow(new GeneralSecurityException("could not decrypt key")); + when(jceKeyCipher.decryptKey(ENCRYPTED_DATA_KEY, KEYNAME, ENCRYPTION_CONTEXT)).thenReturn(DATA_KEY.getEncoded()); + + final List edks = new ArrayList<>(); + edks.add(BAD_DATA_KEY); + edks.add(ENCRYPTED_DATA_KEY); + + keyring.onDecrypt(decryptionMaterials, edks); + + assertEquals(DATA_KEY, decryptionMaterials.getCleartextDataKey()); + assertEquals(DECRYPTED_DATA_KEY_TRACE, decryptionMaterials.getKeyringTrace().getEntries().get(0)); + } + + private Keyring newRawKeyring(JceKeyCipher jceKeyCipher, Function validToDecrypt, + KeyringTraceEntry encryptTraceEntry, KeyringTraceEntry decryptTraceEntry) { + return new RawKeyring(KEYNAMESPACE, KEYNAME, jceKeyCipher) { + @Override + boolean validToDecrypt(EncryptedDataKey encryptedDataKey) { + return validToDecrypt.apply(encryptedDataKey); + } + + @Override + void traceOnEncrypt(KeyringTrace keyringTrace) { + if (encryptTraceEntry != null) { + keyringTrace.add(encryptTraceEntry.getKeyNamespace(), encryptTraceEntry.getKeyName(), + encryptTraceEntry.getFlags().toArray(new KeyringTraceFlag[]{})); + } + } + + @Override + void traceOnDecrypt(KeyringTrace keyringTrace) { + if (decryptTraceEntry != null) { + keyringTrace.add(decryptTraceEntry.getKeyNamespace(), decryptTraceEntry.getKeyName(), + decryptTraceEntry.getFlags().toArray(new KeyringTraceFlag[]{})); + } + } + }; + } + + private void assertEncryptedDataKeyEquals(EncryptedDataKey expected, EncryptedDataKey actual) { + assertEquals(expected.getProviderId(), actual.getProviderId()); + assertArrayEquals(expected.getProviderInformation(), actual.getProviderInformation()); + assertArrayEquals(expected.getEncryptedDataKey(), actual.getEncryptedDataKey()); + } +} diff --git a/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java new file mode 100644 index 000000000..a5c79a350 --- /dev/null +++ b/src/test/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyringTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except + * in compliance with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.amazonaws.encryptionsdk.keyrings; + +import com.amazonaws.encryptionsdk.model.KeyBlob; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.PublicKey; + +import static com.amazonaws.encryptionsdk.keyrings.RawKeyringTest.KEYNAME; +import static com.amazonaws.encryptionsdk.keyrings.RawKeyringTest.KEYNAMESPACE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class RawRsaKeyringTest { + + @Mock + PublicKey publicKey; + @Mock + PrivateKey privateKey; + private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding"; + + @Test + public void testValidToDecrypt() { + RawRsaKeyring rawRsaKeyring = new RawRsaKeyring(KEYNAMESPACE, KEYNAME, publicKey, privateKey, TRANSFORMATION); + + assertTrue(rawRsaKeyring.validToDecrypt(new KeyBlob( + KEYNAMESPACE, KEYNAME.getBytes(StandardCharsets.UTF_8), new byte[]{}))); + //Provider info has extra data + assertFalse(rawRsaKeyring.validToDecrypt(new KeyBlob( + KEYNAMESPACE, ArrayUtils.add(KEYNAME.getBytes(StandardCharsets.UTF_8), (byte)5), new byte[]{}))); + //Bad namespace + assertFalse(rawRsaKeyring.validToDecrypt(new KeyBlob( + "WrongNamespace", KEYNAME.getBytes(StandardCharsets.UTF_8), new byte[]{}))); + } + + @Test + public void testTraceOnEncrypt() { + RawRsaKeyring rawRsaKeyring = new RawRsaKeyring(KEYNAMESPACE, KEYNAME, publicKey, privateKey, TRANSFORMATION); + + KeyringTrace trace = new KeyringTrace(); + + rawRsaKeyring.traceOnEncrypt(trace); + assertEquals(1, trace.getEntries().size()); + assertEquals(KEYNAME, trace.getEntries().get(0).getKeyName()); + assertEquals(KEYNAMESPACE, trace.getEntries().get(0).getKeyNamespace()); + assertEquals(1, trace.getEntries().get(0).getFlags().size()); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.ENCRYPTED_DATA_KEY)); + } + + @Test + public void testTraceOnDecrypt() { + RawRsaKeyring rawRsaKeyring = new RawRsaKeyring(KEYNAMESPACE, KEYNAME, publicKey, privateKey, TRANSFORMATION); + + KeyringTrace trace = new KeyringTrace(); + + rawRsaKeyring.traceOnDecrypt(trace); + assertEquals(1, trace.getEntries().size()); + assertEquals(KEYNAME, trace.getEntries().get(0).getKeyName()); + assertEquals(KEYNAMESPACE, trace.getEntries().get(0).getKeyNamespace()); + assertEquals(1, trace.getEntries().get(0).getFlags().size()); + assertTrue(trace.getEntries().get(0).getFlags().contains(KeyringTraceFlag.DECRYPTED_DATA_KEY)); + } + +}