diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index ed690de12ec42..c5b6a68d41dcb 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -512,12 +512,23 @@ public class AndroidKeyStore extends KeyStoreSpi { } } - int purposes = params.getPurposes(); + @KeyStoreKeyConstraints.PurposeEnum int purposes = params.getPurposes(); + @KeyStoreKeyConstraints.BlockModeEnum int blockModes = params.getBlockModes(); + if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + && (params.isRandomizedEncryptionRequired())) { + @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes = + blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES; + if (incompatibleBlockModes != 0) { + throw new KeyStoreException("Randomized encryption (IND-CPA) required but may be" + + " violated by block mode(s): " + + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes) + + ". See KeyStoreParameter documentation."); + } + } for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - for (int keymasterBlockMode : - KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) { + for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) { args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); } for (int keymasterPadding : @@ -553,8 +564,8 @@ public class AndroidKeyStore extends KeyStoreSpi { args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8); if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) - || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { - // Permit caller-specified IV. This is needed for the Cipher abstraction. + && (!params.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 0e490cd7cadb0..29b1209b08054 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -22,6 +22,7 @@ import android.text.TextUtils; import java.security.spec.AlgorithmParameterSpec; import java.util.Date; +import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -51,6 +52,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; private final boolean mInvalidatedOnNewFingerprintEnrolled; @@ -66,6 +68,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { @KeyStoreKeyConstraints.PurposeEnum int purposes, @KeyStoreKeyConstraints.PaddingEnum int paddings, @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + boolean randomizedEncryptionRequired, @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds, boolean invalidatedOnNewFingerprintEnrolled) { @@ -89,6 +92,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mPurposes = purposes; mPaddings = paddings; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; @@ -171,6 +175,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { return mBlockModes; } + /** + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is indistinguishability under chosen-plaintext attack ({@code + * IND-CPA}). This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. + */ + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; + } + /** * Gets the set of user authenticators which protect access to this key. The key can only be * used iff the user has authenticated to at least one of these user authenticators. @@ -223,6 +240,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; private boolean mInvalidatedOnNewFingerprintEnrolled; @@ -362,6 +380,43 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { return this; } + /** + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is indistinguishability under chosen-plaintext attack + * ({@code IND-CPA}). This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. + * + *

By default, {@code IND-CPA} is required. + * + *

When {@code IND-CPA} is required: + *