diff options
author | Alex Klyubin <klyubin@google.com> | 2015-04-08 13:36:22 -0700 |
---|---|---|
committer | Alex Klyubin <klyubin@google.com> | 2015-04-08 16:28:27 -0700 |
commit | f853f649981ed3cf2f1fbf1363a0932e9736daf6 (patch) | |
tree | 28bcbf8b3cfccddb3051803ed814329c96a063a0 /keystore/java | |
parent | c0d6b7cb752b50ed37197e15cc6f305a0038e61b (diff) | |
download | frameworks_base-f853f649981ed3cf2f1fbf1363a0932e9736daf6.zip frameworks_base-f853f649981ed3cf2f1fbf1363a0932e9736daf6.tar.gz frameworks_base-f853f649981ed3cf2f1fbf1363a0932e9736daf6.tar.bz2 |
Require IND-CPA by default for new AndroidKeyStore keys.
Bug: 18088752
Change-Id: I01e44b7155df4326b5c9d83dda57f889c1f23ec7
Diffstat (limited to 'keystore/java')
6 files changed, 247 insertions, 12 deletions
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index f72c7ac..55a8b4f 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 : @@ -549,8 +560,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 3ada7f6..5bed2e1 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; @@ -65,6 +67,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) { if (context == null) { @@ -87,6 +90,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mPurposes = purposes; mPaddings = paddings; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -169,6 +173,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. 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. * @@ -207,6 +224,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; @@ -346,6 +364,43 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. 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. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required: + * <ul> + * <li>block modes which do not offer {@code IND-CPA}, such as {@code ECB}, are prohibited; + * </li> + * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM}, + * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are + * used.</li> + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are generating a random IV for encryption and then initializing a {@code} + * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV + * instead. This will occur if the {@code Cipher} is initialized for encryption without an + * IV. The IV can then be queried via {@link Cipher#getIV()}.</li> + * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully + * random, such as the name of the file being encrypted, or transaction ID, or password, + * or a device identifier), consider changing your design to use a random IV which will then + * be provided in addition to the ciphertext to the entities which need to decrypt the + * ciphertext.</li> + * </ul> + */ + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; + return this; + } + + /** * Sets the 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. * @@ -394,6 +449,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mPurposes, mPaddings, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index edaa9a5..8945701 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -86,6 +86,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; @@ -132,6 +134,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { @KeyStoreKeyConstraints.DigestEnum int digests, @KeyStoreKeyConstraints.PaddingEnum int paddings, @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + boolean randomizedEncryptionRequired, @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds) { if (context == null) { @@ -171,6 +174,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mDigests = digests; mPaddings = paddings; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -182,8 +186,26 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize, AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate, int flags) { - this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate, - endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1); + this(context, + keyStoreAlias, + keyType, + keySize, + spec, + subjectDN, + serialNumber, + startDate, + endDate, + flags, + startDate, + endDate, + endDate, + 0, + 0, + 0, + 0, + true, + 0, + -1); } /** @@ -343,6 +365,21 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. 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. + * + * @hide + */ + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; + } + + /** * Gets the set of user authenticators which protect access to the private key. The key can only * be used iff the user has authenticated to at least one of these user authenticators. * @@ -429,6 +466,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -670,6 +709,33 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. 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. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required, encryption/decryption transformations which do not + * offer {@code IND-CPA}, such as RSA without padding, are prohibited. + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are using RSA encryption without padding, consider switching to padding + * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li> + * </ul> + * + * @hide + */ + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; + return this; + } + + /** * Sets the 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. * @@ -736,6 +802,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mDigests, mPaddings, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java index 202e38d..ca93cfb 100644 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ b/keystore/java/android/security/KeyStoreKeyConstraints.java @@ -493,6 +493,14 @@ public abstract class KeyStoreKeyConstraints { public static final int GCM = 1 << 3; /** + * Set of block modes compatible with IND-CPA if used correctly. + * + * @hide + */ + public static final @BlockModeEnum int IND_CPA_COMPATIBLE_MODES = + CBC | CTR | GCM; + + /** * @hide */ public static int toKeymaster(@BlockModeEnum int mode) { @@ -571,6 +579,24 @@ public abstract class KeyStoreKeyConstraints { /** * @hide */ + public static String allToString(@BlockModeEnum int modes) { + StringBuilder result = new StringBuilder("["); + boolean firstValue = true; + for (@BlockModeEnum int mode : getSetFlags(modes)) { + if (firstValue) { + firstValue = false; + } else { + result.append(", "); + } + result.append(toString(mode)); + } + result.append(']'); + return result.toString(); + } + + /** + * @hide + */ public static @BlockModeEnum int fromJCAMode(String mode) { String modeLower = mode.toLowerCase(Locale.US); if ("ecb".equals(modeLower)) { diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index 09499d0..b7d6079 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -109,13 +109,26 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits; args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits); - int purposes = spec.getPurposes(); + @KeyStoreKeyConstraints.PurposeEnum int purposes = spec.getPurposes(); + @KeyStoreKeyConstraints.BlockModeEnum int blockModes = spec.getBlockModes(); + if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + && (spec.isRandomizedEncryptionRequired())) { + @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes = + blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES; + if (incompatibleBlockModes != 0) { + throw new IllegalStateException( + "Randomized encryption (IND-CPA) required but may be violated by block" + + " mode(s): " + + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes) + + ". See KeyGeneratorSpec documentation."); + } + } + for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - for (int keymasterBlockMode : - KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) { + for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) { args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); } for (int keymasterPadding : @@ -144,8 +157,8 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) - || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { - // Permit caller-specified IV. This is needed due to the Cipher abstraction. + && (!spec.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 4909467..035142e 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -23,6 +23,8 @@ import java.security.KeyPairGenerator; import java.security.KeyStore.ProtectionParameter; import java.util.Date; +import javax.crypto.Cipher; + /** * This provides the optional parameters that can be specified for * {@code KeyStore} entries that work with @@ -52,6 +54,7 @@ public final class KeyStoreParameter implements ProtectionParameter { private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests; private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; @@ -63,6 +66,7 @@ public final class KeyStoreParameter implements ProtectionParameter { @KeyStoreKeyConstraints.PaddingEnum int paddings, @KeyStoreKeyConstraints.DigestEnum Integer digests, @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + boolean randomizedEncryptionRequired, @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds) { if ((userAuthenticationValidityDurationSeconds < 0) @@ -79,6 +83,7 @@ public final class KeyStoreParameter implements ProtectionParameter { mPaddings = paddings; mDigests = digests; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -185,6 +190,21 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * 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 <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. 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. + * + * @hide + */ + 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. * @@ -235,6 +255,7 @@ public final class KeyStoreParameter implements ProtectionParameter { private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; private @KeyStoreKeyConstraints.DigestEnum Integer mDigests; private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -381,6 +402,46 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * 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 <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. 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. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required: + * <ul> + * <li>transformation which do not offer {@code IND-CPA}, such as symmetric ciphers using + * {@code ECB} mode or RSA encryption without padding, are prohibited;</li> + * <li>in transformations which use an IV, such as symmetric ciphers in {@code CBC}, + * {@code CTR}, and {@code GCM} block modes, caller-provided IVs are rejected when + * encrypting, to ensure that only random IVs are used.</li> + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are generating a random IV for encryption and then initializing a {@code} + * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV + * instead. This will occur if the {@code Cipher} is initialized for encryption without an + * IV. The IV can then be queried via {@link Cipher#getIV()}.</li> + * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully + * random, such as the name of the file being encrypted, or transaction ID, or password, + * or a device identifier), consider changing your design to use a random IV which will then + * be provided in addition to the ciphertext to the entities which need to decrypt the + * ciphertext.</li> + * <li>If you are using RSA encryption without padding, consider switching to padding + * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li> + * + * </ul> + */ + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; + return this; + } + + /** * Sets the 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. * @@ -432,6 +493,7 @@ public final class KeyStoreParameter implements ProtectionParameter { mPaddings, mDigests, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } |