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