am f7e7f744: am a482b046: Merge "Require IND-CPA by default for new AndroidKeyStore keys."

* commit 'f7e7f744e193e027afde5c071889bde1c5882cfd':
  Require IND-CPA by default for new AndroidKeyStore keys.
This commit is contained in:
Alex Klyubin
2015-04-09 16:43:32 +00:00
committed by Android Git Automerger
6 changed files with 247 additions and 12 deletions

View File

@@ -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);
}

View File

@@ -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;
}
@@ -168,6 +172,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 <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;
@@ -345,6 +363,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 <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);
}

View File

@@ -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);
}
/**
@@ -342,6 +364,21 @@ public final class KeyPairGeneratorSpec 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 <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;
@@ -669,6 +708,33 @@ public final class KeyPairGeneratorSpec 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 <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);
}

View File

@@ -591,6 +591,14 @@ public abstract class KeyStoreKeyConstraints {
/** Galois/Counter Mode (GCM) block mode. */
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
*/
@@ -667,6 +675,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
*/

View File

@@ -138,13 +138,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 :
@@ -173,8 +186,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);
}

View File

@@ -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;
}
@@ -184,6 +189,21 @@ public final class KeyStoreParameter implements ProtectionParameter {
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 <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;
@@ -380,6 +401,46 @@ public final class KeyStoreParameter implements ProtectionParameter {
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 <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);
}