Merge "Require IND-CPA by default for new AndroidKeyStore keys."
This commit is contained in:
@@ -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)) {
|
for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
|
||||||
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
|
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
|
||||||
}
|
}
|
||||||
for (int keymasterBlockMode :
|
for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
|
||||||
KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) {
|
|
||||||
args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
|
args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
|
||||||
}
|
}
|
||||||
for (int keymasterPadding :
|
for (int keymasterPadding :
|
||||||
@@ -549,8 +560,8 @@ public class AndroidKeyStore extends KeyStoreSpi {
|
|||||||
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
|
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
|
||||||
|
|
||||||
if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
|
if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
|
||||||
|| ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
|
&& (!params.isRandomizedEncryptionRequired())) {
|
||||||
// Permit caller-specified IV. This is needed for the Cipher abstraction.
|
// Permit caller-provided IV when encrypting with this key
|
||||||
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
|
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.text.TextUtils;
|
|||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.KeyGenerator;
|
import javax.crypto.KeyGenerator;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
|
private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
|
||||||
private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
|
private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
|
||||||
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
||||||
|
private final boolean mRandomizedEncryptionRequired;
|
||||||
private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
||||||
private final int mUserAuthenticationValidityDurationSeconds;
|
private final int mUserAuthenticationValidityDurationSeconds;
|
||||||
|
|
||||||
@@ -65,6 +67,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
@KeyStoreKeyConstraints.PurposeEnum int purposes,
|
@KeyStoreKeyConstraints.PurposeEnum int purposes,
|
||||||
@KeyStoreKeyConstraints.PaddingEnum int paddings,
|
@KeyStoreKeyConstraints.PaddingEnum int paddings,
|
||||||
@KeyStoreKeyConstraints.BlockModeEnum int blockModes,
|
@KeyStoreKeyConstraints.BlockModeEnum int blockModes,
|
||||||
|
boolean randomizedEncryptionRequired,
|
||||||
@KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
|
@KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
|
||||||
int userAuthenticationValidityDurationSeconds) {
|
int userAuthenticationValidityDurationSeconds) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
@@ -87,6 +90,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
mPurposes = purposes;
|
mPurposes = purposes;
|
||||||
mPaddings = paddings;
|
mPaddings = paddings;
|
||||||
mBlockModes = blockModes;
|
mBlockModes = blockModes;
|
||||||
|
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
|
||||||
mUserAuthenticators = userAuthenticators;
|
mUserAuthenticators = userAuthenticators;
|
||||||
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
||||||
}
|
}
|
||||||
@@ -168,6 +172,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
return mBlockModes;
|
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
|
* 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.
|
* 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.PurposeEnum int mPurposes;
|
||||||
private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
|
private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
|
||||||
private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
||||||
|
private boolean mRandomizedEncryptionRequired = true;
|
||||||
private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
||||||
private int mUserAuthenticationValidityDurationSeconds = -1;
|
private int mUserAuthenticationValidityDurationSeconds = -1;
|
||||||
|
|
||||||
@@ -345,6 +363,43 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
return this;
|
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
|
* 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.
|
* iff the user has authenticated to at least one of these user authenticators.
|
||||||
@@ -394,6 +449,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
mPurposes,
|
mPurposes,
|
||||||
mPaddings,
|
mPaddings,
|
||||||
mBlockModes,
|
mBlockModes,
|
||||||
|
mRandomizedEncryptionRequired,
|
||||||
mUserAuthenticators,
|
mUserAuthenticators,
|
||||||
mUserAuthenticationValidityDurationSeconds);
|
mUserAuthenticationValidityDurationSeconds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
|
|
||||||
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
||||||
|
|
||||||
|
private final boolean mRandomizedEncryptionRequired;
|
||||||
|
|
||||||
private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
||||||
|
|
||||||
private final int mUserAuthenticationValidityDurationSeconds;
|
private final int mUserAuthenticationValidityDurationSeconds;
|
||||||
@@ -132,6 +134,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
@KeyStoreKeyConstraints.DigestEnum int digests,
|
@KeyStoreKeyConstraints.DigestEnum int digests,
|
||||||
@KeyStoreKeyConstraints.PaddingEnum int paddings,
|
@KeyStoreKeyConstraints.PaddingEnum int paddings,
|
||||||
@KeyStoreKeyConstraints.BlockModeEnum int blockModes,
|
@KeyStoreKeyConstraints.BlockModeEnum int blockModes,
|
||||||
|
boolean randomizedEncryptionRequired,
|
||||||
@KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
|
@KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
|
||||||
int userAuthenticationValidityDurationSeconds) {
|
int userAuthenticationValidityDurationSeconds) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
@@ -171,6 +174,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
mDigests = digests;
|
mDigests = digests;
|
||||||
mPaddings = paddings;
|
mPaddings = paddings;
|
||||||
mBlockModes = blockModes;
|
mBlockModes = blockModes;
|
||||||
|
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
|
||||||
mUserAuthenticators = userAuthenticators;
|
mUserAuthenticators = userAuthenticators;
|
||||||
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
||||||
}
|
}
|
||||||
@@ -182,8 +186,26 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
|
public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
|
||||||
AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
|
AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
|
||||||
Date startDate, Date endDate, int flags) {
|
Date startDate, Date endDate, int flags) {
|
||||||
this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
|
this(context,
|
||||||
endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1);
|
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;
|
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
|
* 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.
|
* 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 @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
||||||
|
|
||||||
|
private boolean mRandomizedEncryptionRequired = true;
|
||||||
|
|
||||||
private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
||||||
|
|
||||||
private int mUserAuthenticationValidityDurationSeconds = -1;
|
private int mUserAuthenticationValidityDurationSeconds = -1;
|
||||||
@@ -669,6 +708,33 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
return this;
|
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
|
* 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.
|
* iff the user has authenticated to at least one of these user authenticators.
|
||||||
@@ -736,6 +802,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
|
|||||||
mDigests,
|
mDigests,
|
||||||
mPaddings,
|
mPaddings,
|
||||||
mBlockModes,
|
mBlockModes,
|
||||||
|
mRandomizedEncryptionRequired,
|
||||||
mUserAuthenticators,
|
mUserAuthenticators,
|
||||||
mUserAuthenticationValidityDurationSeconds);
|
mUserAuthenticationValidityDurationSeconds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,6 +591,14 @@ public abstract class KeyStoreKeyConstraints {
|
|||||||
/** Galois/Counter Mode (GCM) block mode. */
|
/** Galois/Counter Mode (GCM) block mode. */
|
||||||
public static final int GCM = 1 << 3;
|
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
|
* @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
|
* @hide
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -138,13 +138,26 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
|
|||||||
}
|
}
|
||||||
int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
|
int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
|
||||||
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
|
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 :
|
for (int keymasterPurpose :
|
||||||
KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
|
KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
|
||||||
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
|
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
|
||||||
}
|
}
|
||||||
for (int keymasterBlockMode :
|
for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
|
||||||
KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) {
|
|
||||||
args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
|
args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
|
||||||
}
|
}
|
||||||
for (int keymasterPadding :
|
for (int keymasterPadding :
|
||||||
@@ -173,8 +186,8 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
|
|||||||
? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
|
? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
|
||||||
|
|
||||||
if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
|
if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
|
||||||
|| ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
|
&& (!spec.isRandomizedEncryptionRequired())) {
|
||||||
// Permit caller-specified IV. This is needed due to the Cipher abstraction.
|
// Permit caller-provided IV when encrypting with this key
|
||||||
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
|
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import java.security.KeyPairGenerator;
|
|||||||
import java.security.KeyStore.ProtectionParameter;
|
import java.security.KeyStore.ProtectionParameter;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides the optional parameters that can be specified for
|
* This provides the optional parameters that can be specified for
|
||||||
* {@code KeyStore} entries that work with
|
* {@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.PaddingEnum int mPaddings;
|
||||||
private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
|
private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
|
||||||
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
||||||
|
private final boolean mRandomizedEncryptionRequired;
|
||||||
private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
||||||
private final int mUserAuthenticationValidityDurationSeconds;
|
private final int mUserAuthenticationValidityDurationSeconds;
|
||||||
|
|
||||||
@@ -63,6 +66,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
|
|||||||
@KeyStoreKeyConstraints.PaddingEnum int paddings,
|
@KeyStoreKeyConstraints.PaddingEnum int paddings,
|
||||||
@KeyStoreKeyConstraints.DigestEnum Integer digests,
|
@KeyStoreKeyConstraints.DigestEnum Integer digests,
|
||||||
@KeyStoreKeyConstraints.BlockModeEnum int blockModes,
|
@KeyStoreKeyConstraints.BlockModeEnum int blockModes,
|
||||||
|
boolean randomizedEncryptionRequired,
|
||||||
@KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
|
@KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
|
||||||
int userAuthenticationValidityDurationSeconds) {
|
int userAuthenticationValidityDurationSeconds) {
|
||||||
if ((userAuthenticationValidityDurationSeconds < 0)
|
if ((userAuthenticationValidityDurationSeconds < 0)
|
||||||
@@ -79,6 +83,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
|
|||||||
mPaddings = paddings;
|
mPaddings = paddings;
|
||||||
mDigests = digests;
|
mDigests = digests;
|
||||||
mBlockModes = blockModes;
|
mBlockModes = blockModes;
|
||||||
|
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
|
||||||
mUserAuthenticators = userAuthenticators;
|
mUserAuthenticators = userAuthenticators;
|
||||||
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
|
||||||
}
|
}
|
||||||
@@ -184,6 +189,21 @@ public final class KeyStoreParameter implements ProtectionParameter {
|
|||||||
return mBlockModes;
|
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
|
* 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.
|
* 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.PaddingEnum int mPaddings;
|
||||||
private @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
|
private @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
|
||||||
private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
|
||||||
|
private boolean mRandomizedEncryptionRequired = true;
|
||||||
private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
|
||||||
private int mUserAuthenticationValidityDurationSeconds = -1;
|
private int mUserAuthenticationValidityDurationSeconds = -1;
|
||||||
|
|
||||||
@@ -380,6 +401,46 @@ public final class KeyStoreParameter implements ProtectionParameter {
|
|||||||
return this;
|
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
|
* 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.
|
* iff the user has authenticated to at least one of these user authenticators.
|
||||||
@@ -432,6 +493,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
|
|||||||
mPaddings,
|
mPaddings,
|
||||||
mDigests,
|
mDigests,
|
||||||
mBlockModes,
|
mBlockModes,
|
||||||
|
mRandomizedEncryptionRequired,
|
||||||
mUserAuthenticators,
|
mUserAuthenticators,
|
||||||
mUserAuthenticationValidityDurationSeconds);
|
mUserAuthenticationValidityDurationSeconds);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user