Merge "Prevent windows from freezing screen while timeout"
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index a59927d..635b2fa 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -49,14 +49,25 @@
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$AES");
+ put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA1");
+ put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA224");
put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA256");
+ put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA384");
+ put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA512");
// java.security.SecretKeyFactory
- put("SecretKeyFactory.AES", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
- put("SecretKeyFactory.HmacSHA256", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
+ putSecretKeyFactoryImpl("AES");
+ putSecretKeyFactoryImpl("HmacSHA1");
+ putSecretKeyFactoryImpl("HmacSHA224");
+ putSecretKeyFactoryImpl("HmacSHA256");
+ putSecretKeyFactoryImpl("HmacSHA384");
+ putSecretKeyFactoryImpl("HmacSHA512");
// javax.crypto.Mac
+ putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224");
putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256");
+ putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384");
+ putMacImpl("HmacSHA512", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA512");
// javax.crypto.Cipher
putSymmetricCipherImpl("AES/ECB/NoPadding",
@@ -73,6 +84,10 @@
PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding");
}
+ private void putSecretKeyFactoryImpl(String algorithm) {
+ put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
+ }
+
private void putMacImpl(String algorithm, String implClass) {
put("Mac." + algorithm, implClass);
put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index ec358d6..487eac0 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -111,7 +111,9 @@
private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode;
private final @KeyStoreKeyConstraints.PaddingEnum int mPadding;
private final int mBlockSizeBytes;
- private final boolean mIvUsed;
+
+ /** Whether this transformation requires an IV. */
+ private final boolean mIvRequired;
// Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
// doFinal finishes.
@@ -119,10 +121,13 @@
private KeyStoreSecretKey mKey;
private SecureRandom mRng;
private boolean mFirstOperationInitiated;
- byte[] mIv;
+ private byte[] mIv;
+ /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
+ private boolean mIvHasBeenUsed;
- // Fields below must be reset
+ // Fields below must be reset after doFinal
private byte[] mAdditionalEntropyForBegin;
+
/**
* Token referencing this operation inside keystore service. It is initialized by
* {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some
@@ -143,7 +148,7 @@
mBlockMode = blockMode;
mPadding = padding;
mBlockSizeBytes = blockSizeBytes;
- mIvUsed = ivUsed;
+ mIvRequired = ivUsed;
}
@Override
@@ -170,7 +175,7 @@
}
private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- reset();
+ resetAll();
if (!(key instanceof KeyStoreSecretKey)) {
throw new InvalidKeyException(
"Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
@@ -187,7 +192,25 @@
mEncrypting = opmode == Cipher.ENCRYPT_MODE;
}
- private void reset() {
+ private void resetAll() {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ mEncrypting = false;
+ mKey = null;
+ mRng = null;
+ mFirstOperationInitiated = false;
+ mIv = null;
+ mIvHasBeenUsed = false;
+ mAdditionalEntropyForBegin = null;
+ mOperationToken = null;
+ mOperationHandle = null;
+ mMainDataStreamer = null;
+ }
+
+ private void resetWhilePreservingInitState() {
IBinder operationToken = mOperationToken;
if (operationToken != null) {
mOperationToken = null;
@@ -205,6 +228,12 @@
if (mKey == null) {
throw new IllegalStateException("Not initialized");
}
+ if ((mEncrypting) && (mIvRequired) && (mIvHasBeenUsed)) {
+ // IV is being reused for encryption: this violates security best practices.
+ throw new IllegalStateException(
+ "IV has already been used. Reusing IV in encryption mode violates security best"
+ + " practices.");
+ }
KeymasterArguments keymasterInputArgs = new KeymasterArguments();
keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm);
@@ -234,6 +263,7 @@
mOperationHandle = opResult.operationHandle;
loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
mFirstOperationInitiated = true;
+ mIvHasBeenUsed = true;
mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
mKeyStore, opResult.token));
@@ -298,7 +328,7 @@
}
}
- reset();
+ resetWhilePreservingInitState();
return output;
}
@@ -376,7 +406,7 @@
*/
@Override
protected AlgorithmParameters engineGetParameters() {
- if (!mIvUsed) {
+ if (!mIvRequired) {
return null;
}
if ((mIv != null) && (mIv.length > 0)) {
@@ -408,7 +438,7 @@
*/
protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
- if (!mIvUsed) {
+ if (!mIvRequired) {
if (params != null) {
throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
}
@@ -447,7 +477,7 @@
*/
protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
throws InvalidAlgorithmParameterException {
- if (!mIvUsed) {
+ if (!mIvRequired) {
if (params != null) {
throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
}
@@ -492,7 +522,7 @@
* and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters.
*/
protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
- if (!mIvUsed) {
+ if (!mIvRequired) {
return;
}
@@ -515,7 +545,7 @@
if (!mFirstOperationInitiated) {
// First begin operation -- see if we need to provide additional entropy for IV
// generation.
- if (mIvUsed) {
+ if (mIvRequired) {
// IV is needed
if ((mIv == null) && (mEncrypting)) {
// TODO: Switch to keymaster-generated IV code below once keymaster supports
@@ -534,7 +564,7 @@
}
}
- if ((mIvUsed) && (mIv != null)) {
+ if ((mIvRequired) && (mIv != null)) {
keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
}
}
@@ -557,7 +587,7 @@
returnedIv = null;
}
- if (mIvUsed) {
+ if (mIvRequired) {
if (mIv == null) {
mIv = returnedIv;
} else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index a5864a4..f69e7d1 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -35,9 +35,33 @@
*/
public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
+ public static class HmacSHA1 extends KeyStoreHmacSpi {
+ public HmacSHA1() {
+ super(KeyStoreKeyConstraints.Digest.SHA1);
+ }
+ }
+
+ public static class HmacSHA224 extends KeyStoreHmacSpi {
+ public HmacSHA224() {
+ super(KeyStoreKeyConstraints.Digest.SHA224);
+ }
+ }
+
public static class HmacSHA256 extends KeyStoreHmacSpi {
public HmacSHA256() {
- super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8);
+ super(KeyStoreKeyConstraints.Digest.SHA256);
+ }
+ }
+
+ public static class HmacSHA384 extends KeyStoreHmacSpi {
+ public HmacSHA384() {
+ super(KeyStoreKeyConstraints.Digest.SHA384);
+ }
+ }
+
+ public static class HmacSHA512 extends KeyStoreHmacSpi {
+ public HmacSHA512() {
+ super(KeyStoreKeyConstraints.Digest.SHA512);
}
}
@@ -52,9 +76,9 @@
private IBinder mOperationToken;
private Long mOperationHandle;
- protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) {
+ protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest) {
mDigest = digest;
- mMacSizeBytes = macSizeBytes;
+ mMacSizeBytes = KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
}
@Override
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index bd7aacf..9321302 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -327,7 +327,15 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
- value = {Digest.NONE, Digest.SHA256})
+ value = {
+ Digest.NONE,
+ Digest.MD5,
+ Digest.SHA1,
+ Digest.SHA224,
+ Digest.SHA256,
+ Digest.SHA384,
+ Digest.SHA512,
+ })
public @interface DigestEnum {}
/**
@@ -343,9 +351,34 @@
public static final int NONE = 1 << 0;
/**
- * SHA-256 digest.
+ * MD5 digest.
*/
- public static final int SHA256 = 1 << 1;
+ public static final int MD5 = 1 << 1;
+
+ /**
+ * SHA-1 digest.
+ */
+ public static final int SHA1 = 1 << 2;
+
+ /**
+ * SHA-2 224 (aka SHA-224) digest.
+ */
+ public static final int SHA224 = 1 << 3;
+
+ /**
+ * SHA-2 256 (aka SHA-256) digest.
+ */
+ public static final int SHA256 = 1 << 4;
+
+ /**
+ * SHA-2 384 (aka SHA-384) digest.
+ */
+ public static final int SHA384 = 1 << 5;
+
+ /**
+ * SHA-2 512 (aka SHA-512) digest.
+ */
+ public static final int SHA512 = 1 << 6;
/**
* @hide
@@ -354,8 +387,18 @@
switch (digest) {
case NONE:
return "NONE";
+ case MD5:
+ return "MD5";
+ case SHA1:
+ return "SHA-1";
+ case SHA224:
+ return "SHA-224";
case SHA256:
- return "SHA256";
+ return "SHA-256";
+ case SHA384:
+ return "SHA-384";
+ case SHA512:
+ return "SHA-512";
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
@@ -364,13 +407,19 @@
/**
* @hide
*/
- public static String[] allToString(@DigestEnum int digests) {
- int[] values = getSetFlags(digests);
- String[] result = new String[values.length];
- for (int i = 0; i < values.length; i++) {
- result[i] = toString(values[i]);
+ public static String allToString(@DigestEnum int digests) {
+ StringBuilder result = new StringBuilder("[");
+ boolean firstValue = true;
+ for (@DigestEnum int digest : getSetFlags(digests)) {
+ if (firstValue) {
+ firstValue = false;
+ } else {
+ result.append(", ");
+ }
+ result.append(toString(digest));
}
- return result;
+ result.append(']');
+ return result.toString();
}
/**
@@ -380,8 +429,18 @@
switch (digest) {
case NONE:
return KeymasterDefs.KM_DIGEST_NONE;
+ case MD5:
+ return KeymasterDefs.KM_DIGEST_MD5;
+ case SHA1:
+ return KeymasterDefs.KM_DIGEST_SHA1;
+ case SHA224:
+ return KeymasterDefs.KM_DIGEST_SHA_2_224;
case SHA256:
return KeymasterDefs.KM_DIGEST_SHA_2_256;
+ case SHA384:
+ return KeymasterDefs.KM_DIGEST_SHA_2_384;
+ case SHA512:
+ return KeymasterDefs.KM_DIGEST_SHA_2_512;
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
@@ -394,8 +453,18 @@
switch (digest) {
case KeymasterDefs.KM_DIGEST_NONE:
return NONE;
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return MD5;
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return SHA224;
case KeymasterDefs.KM_DIGEST_SHA_2_256:
return SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return SHA512;
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
@@ -429,11 +498,21 @@
public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
String algorithmLower = algorithm.toLowerCase(Locale.US);
if (algorithmLower.startsWith("hmac")) {
- if ("hmacsha256".equals(algorithmLower)) {
+ String digestLower = algorithmLower.substring("hmac".length());
+ if ("md5".equals(digestLower)) {
+ return MD5;
+ } else if ("sha1".equals(digestLower)) {
+ return SHA1;
+ } else if ("sha224".equals(digestLower)) {
+ return SHA224;
+ } else if ("sha256".equals(digestLower)) {
return SHA256;
+ } else if ("sha384".equals(digestLower)) {
+ return SHA384;
+ } else if ("sha512".equals(digestLower)) {
+ return SHA512;
} else {
- throw new IllegalArgumentException("Unsupported digest: "
- + algorithmLower.substring("hmac".length()));
+ throw new IllegalArgumentException("Unsupported digest: " + digestLower);
}
} else {
return null;
@@ -447,8 +526,18 @@
switch (digest) {
case NONE:
return "NONE";
+ case MD5:
+ return "MD5";
+ case SHA1:
+ return "SHA1";
+ case SHA224:
+ return "SHA224";
case SHA256:
return "SHA256";
+ case SHA384:
+ return "SHA384";
+ case SHA512:
+ return "SHA512";
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
@@ -461,8 +550,18 @@
switch (digest) {
case NONE:
return null;
+ case MD5:
+ return 128 / 8;
+ case SHA1:
+ return 160 / 8;
+ case SHA224:
+ return 224 / 8;
case SHA256:
return 256 / 8;
+ case SHA384:
+ return 384 / 8;
+ case SHA512:
+ return 512 / 8;
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
@@ -471,7 +570,7 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
- value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
+ value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR, BlockMode.GCM})
public @interface BlockModeEnum {}
/**
@@ -489,6 +588,9 @@
/** Counter (CTR) block mode. */
public static final int CTR = 1 << 2;
+ /** Galois/Counter Mode (GCM) block mode. */
+ public static final int GCM = 1 << 3;
+
/**
* @hide
*/
@@ -500,6 +602,8 @@
return KeymasterDefs.KM_MODE_CBC;
case CTR:
return KeymasterDefs.KM_MODE_CTR;
+ case GCM:
+ return KeymasterDefs.KM_MODE_GCM;
default:
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
@@ -516,6 +620,8 @@
return CBC;
case KeymasterDefs.KM_MODE_CTR:
return CTR;
+ case KeymasterDefs.KM_MODE_GCM:
+ return GCM;
default:
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
@@ -554,6 +660,8 @@
return "CBC";
case CTR:
return "CTR";
+ case GCM:
+ return "GCM";
default:
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
@@ -570,6 +678,8 @@
return CBC;
} else if ("ctr".equals(modeLower)) {
return CTR;
+ } else if ("gcm".equals(modeLower)) {
+ return GCM;
} else {
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 09499d0..5be8c39 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -41,12 +41,41 @@
}
}
- public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi {
- public HmacSHA256() {
+ protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi {
+ protected HmacBase(@KeyStoreKeyConstraints.DigestEnum int digest) {
super(KeyStoreKeyConstraints.Algorithm.HMAC,
- KeyStoreKeyConstraints.Digest.SHA256,
- KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
- KeyStoreKeyConstraints.Digest.SHA256) * 8);
+ digest,
+ KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest) * 8);
+ }
+ }
+
+ public static class HmacSHA1 extends HmacBase {
+ public HmacSHA1() {
+ super(KeyStoreKeyConstraints.Digest.SHA1);
+ }
+ }
+
+ public static class HmacSHA224 extends HmacBase {
+ public HmacSHA224() {
+ super(KeyStoreKeyConstraints.Digest.SHA224);
+ }
+ }
+
+ public static class HmacSHA256 extends HmacBase {
+ public HmacSHA256() {
+ super(KeyStoreKeyConstraints.Digest.SHA256);
+ }
+ }
+
+ public static class HmacSHA384 extends HmacBase {
+ public HmacSHA384() {
+ super(KeyStoreKeyConstraints.Digest.SHA384);
+ }
+ }
+
+ public static class HmacSHA512 extends HmacBase {
+ public HmacSHA512() {
+ super(KeyStoreKeyConstraints.Digest.SHA512);
}
}