Merge "Hook in user authenticators and their exceptions."
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 846d1f1..4650d27 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -536,10 +536,9 @@
         if (params.getUserAuthenticators().isEmpty()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         } else {
-        // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
-//            for (int userAuthenticatorId : params.getUserAuthenticators()) {
-//                args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
-//            }
+            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
+                            params.getUserAuthenticators()));
         }
         if (params.getUserAuthenticationValidityDurationSeconds() != null) {
             args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index bdaa812..5219086 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -224,8 +224,7 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw new CryptoOperationException("Failed to start keystore operation",
-                    KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+            throw KeymasterUtils.getCryptoOperationException(opResult.resultCode);
         }
 
         if (opResult.token == null) {
@@ -252,7 +251,7 @@
         try {
             output = mMainDataStreamer.update(input, inputOffset, inputLen);
         } catch (KeymasterException e) {
-            throw new CryptoOperationException("Keystore operation failed", e);
+            throw KeymasterUtils.getCryptoOperationException(e);
         }
 
         if (output.length == 0) {
@@ -297,7 +296,7 @@
                 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
                     throw new AEADBadTagException();
                 default:
-                    throw new CryptoOperationException("Keystore operation failed", e);
+                    throw KeymasterUtils.getCryptoOperationException(e);
             }
         }
 
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
index 2b279f6..993614b 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -120,7 +120,7 @@
             if (opResult == null) {
                 throw new KeyStoreConnectException();
             } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-                throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+                throw KeymasterUtils.getKeymasterException(opResult.resultCode);
             }
 
             if (opResult.inputConsumed == chunk.length) {
@@ -203,7 +203,7 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+            throw KeymasterUtils.getKeymasterException(opResult.resultCode);
         }
 
         return concat(output, opResult.output);
@@ -227,7 +227,7 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+            throw KeymasterUtils.getKeymasterException(opResult.resultCode);
         }
 
         if (opResult.inputConsumed < chunk.length) {
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index 841b90d..1297cc2 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -103,8 +103,7 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw new CryptoOperationException("Failed to start keystore operation",
-                    KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+            throw KeymasterUtils.getCryptoOperationException(opResult.resultCode);
         }
         mOperationToken = opResult.token;
         if (mOperationToken == null) {
@@ -131,7 +130,7 @@
         try {
             output = mChunkedStreamer.update(input, offset, len);
         } catch (KeymasterException e) {
-            throw new CryptoOperationException("Keystore operation failed", e);
+            throw KeymasterUtils.getCryptoOperationException(e);
         }
         if ((output != null) && (output.length != 0)) {
             throw new CryptoOperationException("Update operation unexpectedly produced output");
@@ -148,7 +147,7 @@
         try {
             result = mChunkedStreamer.doFinal(null, 0, 0);
         } catch (KeymasterException e) {
-            throw new CryptoOperationException("Keystore operation failed", e);
+            throw KeymasterUtils.getCryptoOperationException(e);
         }
 
         engineReset();
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index c011083..c27ccb1 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -23,7 +23,10 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Locale;
+import java.util.Set;
 
 /**
  * Constraints for {@code AndroidKeyStore} keys.
@@ -520,4 +523,87 @@
             }
         }
     }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({UserAuthenticator.LOCK_SCREEN})
+    public @interface UserAuthenticatorEnum {}
+
+    /**
+     * User authenticators which can be used to restrict/protect access to keys.
+     */
+    public static abstract class UserAuthenticator {
+        private UserAuthenticator() {}
+
+        /** Lock screen. */
+        public static final int LOCK_SCREEN = 1;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return LOCK_SCREEN;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return LOCK_SCREEN;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int allToKeymaster(Set<Integer> userAuthenticators) {
+            int result = 0;
+            for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) {
+                result |= toKeymaster(userAuthenticator);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        public static Set<Integer> allFromKeymaster(int userAuthenticators) {
+            int userAuthenticator = 1;
+            Set<Integer> result = null;
+            while (userAuthenticators != 0) {
+                if ((userAuthenticators & 1) != 0) {
+                    if (result == null) {
+                        result = new HashSet<Integer>();
+                    }
+                    result.add(fromKeymaster(userAuthenticator));
+                }
+                userAuthenticators >>>= 1;
+                userAuthenticator <<= 1;
+            }
+            return (result != null) ? result : Collections.<Integer>emptySet();
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@UserAuthenticatorEnum int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return "LOCK_SCREEN";
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 7796de8..09fee1b 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -136,10 +136,9 @@
         if (spec.getUserAuthenticators().isEmpty()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         } else {
-        // TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
-//            for (int userAuthenticatorId : spec.getUserAuthenticators()) {
-//                args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
-//            }
+            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
+                            spec.getUserAuthenticators()));
         }
         if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
             args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
@@ -175,8 +174,7 @@
         int errorCode = mKeyStore.generateKey(
                 keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
         if (errorCode != KeyStore.NO_ERROR) {
-            throw new CryptoOperationException("Failed to generate key",
-                    KeymasterUtils.getExceptionForKeymasterError(errorCode));
+            throw KeymasterUtils.getCryptoOperationException(errorCode);
         }
         String keyAlgorithmJCA =
                 KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest);
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index 8921ba1..88255a8 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -22,7 +22,6 @@
 import java.security.InvalidKeyException;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
-import java.util.Collections;
 import java.util.Set;
 
 import javax.crypto.SecretKey;
@@ -113,13 +112,16 @@
             throw new InvalidKeySpecException("Unsupported key characteristic", e);
         }
 
-        // TODO: Read user authentication IDs once the Keymaster API has stabilized
-        Set<Integer> userAuthenticators = Collections.emptySet();
-        Set<Integer> teeBackedUserAuthenticators = Collections.emptySet();
-//        Set<Integer> userAuthenticators = new HashSet<Integer>(
-//                getInts(keyCharacteristics, KeymasterDefs.KM_TAG_USER_AUTH_ID));
-//        Set<Integer> teeBackedUserAuthenticators = new HashSet<Integer>(
-//                keyCharacteristics.hwEnforced.getInts(KeymasterDefs.KM_TAG_USER_AUTH_ID));
+        int swEnforcedUserAuthenticatorIds =
+                keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+        int hwEnforcedUserAuthenticatorIds =
+                keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+        int userAuthenticatorIds = swEnforcedUserAuthenticatorIds | hwEnforcedUserAuthenticatorIds;
+        Set<Integer> userAuthenticators =
+                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(userAuthenticatorIds);
+        Set<Integer> teeBackedUserAuthenticators =
+                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+                        hwEnforcedUserAuthenticatorIds);
 
         return new KeyStoreKeySpec(entryAlias,
                 origin,
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index 6bb9636..2645cf4 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -29,7 +29,7 @@
 public abstract class KeymasterUtils {
     private KeymasterUtils() {}
 
-    public static KeymasterException getExceptionForKeymasterError(int keymasterErrorCode) {
+    public static KeymasterException getKeymasterException(int keymasterErrorCode) {
         switch (keymasterErrorCode) {
             case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
                 // The name of this parameter significantly differs between Keymaster and framework
@@ -42,6 +42,19 @@
         }
     }
 
+    public static CryptoOperationException getCryptoOperationException(KeymasterException e) {
+        switch (e.getErrorCode()) {
+            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
+                return new UserNotAuthenticatedException();
+            default:
+                return new CryptoOperationException("Crypto operation failed", e);
+        }
+    }
+
+    public static CryptoOperationException getCryptoOperationException(int keymasterErrorCode) {
+        return getCryptoOperationException(getKeymasterException(keymasterErrorCode));
+    }
+
     public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) {
         if (keyCharacteristics.hwEnforced.containsTag(tag)) {
             return keyCharacteristics.hwEnforced.getInt(tag, -1);
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
new file mode 100644
index 0000000..b45817c
--- /dev/null
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * Indicates that a cryptographic operation could not be performed because the user has not been
+ * authenticated recently enough.
+ *
+ * @hide
+ */
+public class UserNotAuthenticatedException extends CryptoOperationException {
+    public UserNotAuthenticatedException() {
+        super("User not authenticated");
+    }
+
+    public UserNotAuthenticatedException(String message) {
+        super(message);
+    }
+}