OSDN Git Service

Add challenge to IGateKeeperService
authorAndres Morales <anmorales@google.com>
Fri, 10 Apr 2015 02:14:42 +0000 (19:14 -0700)
committerAndres Morales <anmorales@google.com>
Tue, 14 Apr 2015 01:38:45 +0000 (18:38 -0700)
required for enrolling secondary auth form-factors

Change-Id: Id5a1eb1ed22f01fbaabe8e4ebddfc42d58322625

core/java/android/service/gatekeeper/IGateKeeperService.aidl
core/java/com/android/internal/widget/ILockSettings.aidl
core/java/com/android/internal/widget/LockPatternUtils.java
services/core/java/com/android/server/LockSettingsService.java

index 675374d..2f3e296 100644 (file)
@@ -45,7 +45,21 @@ interface IGateKeeperService {
      * @param enrolledPasswordHandle The handle against which the provided password will be
      *                               verified.
      * @param The plaintext blob to verify against enrolledPassword.
-     * @return true if success, false if failure
+     * @return True if the authentication was successful
      */
-    boolean verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
+    boolean verify(int uid, in byte[] enrolledPasswordHandle,
+            in byte[] providedPassword);
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param challenge a challenge to authenticate agaisnt the device credential. If successful
+     *                  authentication occurs, this value will be written to the returned 
+     *                  authentication attestation.
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return an opaque attestation of authentication on success, or null.
+     */
+    byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle, 
+            in byte[] providedPassword);
 }
index 53a860d..bfafff6 100644 (file)
@@ -26,8 +26,10 @@ interface ILockSettings {
     String getString(in String key, in String defaultValue, in int userId);
     void setLockPattern(in String pattern, in String savedPattern, int userId);
     boolean checkPattern(in String pattern, int userId);
+    byte[] verifyPattern(in String pattern, long challenge, int userId);
     void setLockPassword(in String password, in String savedPassword, int userId);
     boolean checkPassword(in String password, int userId);
+    byte[] verifyPassword(in String password, long challenge, int userId);
     boolean checkVoldPassword(int userId);
     boolean havePattern(int userId);
     boolean havePassword(int userId);
index fce57bd..123d1ac 100644 (file)
@@ -280,6 +280,24 @@ public class LockPatternUtils {
     }
 
     /**
+     * Check to see if a pattern matches the saved pattern.
+     * If pattern matches, return an opaque attestation that the challenge
+     * was verified.
+     *
+     * @param pattern The pattern to check.
+     * @param challenge The challenge to verify against the pattern
+     * @return the attestation that the challenge was verified, or null.
+     */
+    public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge) {
+        final int userId = getCurrentOrCallingUserId();
+        try {
+            return getLockSettings().verifyPattern(patternToString(pattern), challenge, userId);
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
+    /**
      * Check to see if a pattern matches the saved pattern.  If no pattern exists,
      * always returns true.
      * @param pattern The pattern to check.
@@ -295,6 +313,24 @@ public class LockPatternUtils {
     }
 
     /**
+     * Check to see if a password matches the saved password.
+     * If password matches, return an opaque attestation that the challenge
+     * was verified.
+     *
+     * @param password The password to check.
+     * @param challenge The challenge to verify against the password
+     * @return the attestation that the challenge was verified, or null.
+     */
+    public byte[] verifyPassword(String password, long challenge) {
+        final int userId = getCurrentOrCallingUserId();
+        try {
+            return getLockSettings().verifyPassword(password, challenge, userId);
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
+    /**
      * Check to see if a password matches the saved password.  If no password exists,
      * always returns true.
      * @param password The password to check.
index 48f4120..ee73b1a 100644 (file)
@@ -387,6 +387,11 @@ public class LockSettingsService extends ILockSettings.Stub {
             throws RemoteException {
         byte[] currentHandle = getCurrentHandle(userId);
 
+        if (pattern == null) {
+            mStorage.writePatternHash(null, userId);
+            return;
+        }
+
         if (currentHandle == null) {
             if (savedCredential != null) {
                 Slog.w(TAG, "Saved credential provided, but none stored");
@@ -406,9 +411,13 @@ public class LockSettingsService extends ILockSettings.Stub {
     @Override
     public void setLockPassword(String password, String savedCredential, int userId)
             throws RemoteException {
-
         byte[] currentHandle = getCurrentHandle(userId);
 
+        if (password == null) {
+            mStorage.writePasswordHash(null, userId);
+            return;
+        }
+
         if (currentHandle == null) {
             if (savedCredential != null) {
                 Slog.w(TAG, "Saved credential provided, but none stored");
@@ -446,70 +455,144 @@ public class LockSettingsService extends ILockSettings.Stub {
 
     @Override
     public boolean checkPattern(String pattern, int userId) throws RemoteException {
-        checkPasswordReadPermission(userId);
+        try {
+            doVerifyPattern(pattern, false, 0, userId);
+        } catch (VerificationFailedException ex) {
+            return false;
+        }
+
+        return true;
+    }
 
-        CredentialHash storedHash = mStorage.readPatternHash(userId);
+    @Override
+    public byte[] verifyPattern(String pattern, long challenge, int userId)
+            throws RemoteException {
+        try {
+            return doVerifyPattern(pattern, true, challenge, userId);
+        } catch (VerificationFailedException ex) {
+            return null;
+        }
+    }
+
+    private byte[] doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
+            int userId) throws VerificationFailedException, RemoteException {
+       checkPasswordReadPermission(userId);
 
-        if (storedHash == null) {
-            return true;
+       CredentialHash storedHash = mStorage.readPatternHash(userId);
+
+        if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(pattern)) {
+            // don't need to pass empty passwords to GateKeeper
+            return null;
+        }
+
+        if (TextUtils.isEmpty(pattern)) {
+            throw new VerificationFailedException();
         }
 
         if (storedHash.version == CredentialHash.VERSION_LEGACY) {
-            // Try the old backend and upgrade if a match is found
-            byte[] hash = LockPatternUtils.patternToHash(
-                    LockPatternUtils.stringToPattern(pattern));
-            boolean matched = Arrays.equals(hash, storedHash.hash);
-            if (matched && !TextUtils.isEmpty(pattern)) {
+            byte[] hash = mLockPatternUtils.patternToHash(
+                    mLockPatternUtils.stringToPattern(pattern));
+            if (Arrays.equals(hash, storedHash.hash)) {
                 maybeUpdateKeystore(pattern, userId);
-                // migrate pattern to GateKeeper
+                // migrate password to GateKeeper
                 setLockPattern(pattern, null, userId);
+                if (!hasChallenge) {
+                    return null;
+                }
+                // Fall through to get the auth token. Technically this should never happen,
+                // as a user that had a legacy pattern would have to unlock their device
+                // before getting to a flow with a challenge, but supporting for consistency.
+            } else {
+                throw new VerificationFailedException();
             }
-
-            return matched;
         }
 
-        boolean matched = getGateKeeperService()
-                .verify(userId, storedHash.hash, pattern.getBytes());
-        if (matched && !TextUtils.isEmpty(pattern)) {
-            maybeUpdateKeystore(pattern, userId);
-            return true;
+        byte[] token = null;
+        if (hasChallenge) {
+            token = getGateKeeperService()
+                    .verifyChallenge(userId, challenge, storedHash.hash, pattern.getBytes());
+            if (token == null) {
+                throw new VerificationFailedException();
+            }
+        } else if (!getGateKeeperService().verify(userId, storedHash.hash, pattern.getBytes())) {
+            throw new VerificationFailedException();
         }
 
-        return matched;
+        // pattern has matched
+        maybeUpdateKeystore(pattern, userId);
+        return token;
+
     }
 
     @Override
     public boolean checkPassword(String password, int userId) throws RemoteException {
-        checkPasswordReadPermission(userId);
+        try {
+            doVerifyPassword(password, false, 0, userId);
+        } catch (VerificationFailedException ex) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public byte[] verifyPassword(String password, long challenge, int userId)
+            throws RemoteException {
+        try {
+            return doVerifyPassword(password, true, challenge, userId);
+        } catch (VerificationFailedException ex) {
+            return null;
+        }
+    }
+
+    private byte[] doVerifyPassword(String password, boolean hasChallenge, long challenge,
+            int userId) throws VerificationFailedException, RemoteException {
+       checkPasswordReadPermission(userId);
 
        CredentialHash storedHash = mStorage.readPasswordHash(userId);
 
-        if (storedHash == null) {
-            return true;
+        if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(password)) {
+            // don't need to pass empty passwords to GateKeeper
+            return null;
+        }
+
+        if (TextUtils.isEmpty(password)) {
+            throw new VerificationFailedException();
         }
 
         if (storedHash.version == CredentialHash.VERSION_LEGACY) {
             byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
-            boolean matched = Arrays.equals(hash, storedHash.hash);
-            if (matched && !TextUtils.isEmpty(password)) {
+            if (Arrays.equals(hash, storedHash.hash)) {
                 maybeUpdateKeystore(password, userId);
                 // migrate password to GateKeeper
                 setLockPassword(password, null, userId);
+                if (!hasChallenge) {
+                    return null;
+                }
+                // Fall through to get the auth token. Technically this should never happen,
+                // as a user that had a legacy password would have to unlock their device
+                // before getting to a flow with a challenge, but supporting for consistency.
+            } else {
+                throw new VerificationFailedException();
             }
-            return matched;
         }
 
-
-
-        boolean matched = getGateKeeperService()
-                .verify(userId, storedHash.hash, password.getBytes());
-        if (!TextUtils.isEmpty(password) && matched) {
-            maybeUpdateKeystore(password, userId);
+        byte[] token = null;
+        if (hasChallenge) {
+            token = getGateKeeperService()
+                    .verifyChallenge(userId, challenge, storedHash.hash, password.getBytes());
+            if (token == null) {
+                throw new VerificationFailedException();
+            }
+        } else if (!getGateKeeperService().verify(userId, storedHash.hash, password.getBytes())) {
+            throw new VerificationFailedException();
         }
 
-        return matched;
+        // password has matched
+        maybeUpdateKeystore(password, userId);
+        return token;
     }
 
+
     @Override
     public boolean checkVoldPassword(int userId) throws RemoteException {
         if (!mFirstCallToVold) {
@@ -624,4 +707,6 @@ public class LockSettingsService extends ILockSettings.Stub {
         return null;
     }
 
+    private class VerificationFailedException extends Exception {}
+
 }