Two phases to set the password for disk encryption

Revert "Revert "Two phases to set the password for disk encryption""

This reverts commit a1eb750e75ff7c7ef7698deed4442449c33334c8.

Bug: 28154455
Bug: 28694324
Change-Id: I8106bfba28da401b9fd38349c6a9fa9a24f54712
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 5b6117d..cec3229 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -790,10 +790,11 @@
                 if (isSecure) {
                     tieManagedProfileLockIfNecessary(managedUserId, null);
                 } else {
+                    clearUserKeyProtection(managedUserId);
                     getGateKeeperService().clearSecureUserId(managedUserId);
                     mStorage.writePatternHash(null, managedUserId);
                     setKeystorePassword(null, managedUserId);
-                    clearUserKeyProtection(managedUserId);
+                    fixateNewestUserKeyAuth(managedUserId);
                     mStorage.removeChildProfileLock(managedUserId);
                     removeKeystoreProfileKey(managedUserId);
                 }
@@ -828,10 +829,11 @@
         byte[] currentHandle = getCurrentHandle(userId);
 
         if (pattern == null) {
+            clearUserKeyProtection(userId);
             getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePatternHash(null, userId);
             setKeystorePassword(null, userId);
-            clearUserKeyProtection(userId);
+            fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
             return;
         }
@@ -861,8 +863,12 @@
 
         byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
         if (enrolledHandle != null) {
+            CredentialHash willStore
+                = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
+            setUserKeyProtection(userId, pattern,
+                doVerifyPattern(pattern, willStore, true, 0, userId));
             mStorage.writePatternHash(enrolledHandle, userId);
-            setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
+            fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
         } else {
             throw new RemoteException("Failed to enroll pattern");
@@ -885,10 +891,11 @@
             throws RemoteException {
         byte[] currentHandle = getCurrentHandle(userId);
         if (password == null) {
+            clearUserKeyProtection(userId);
             getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePasswordHash(null, userId);
             setKeystorePassword(null, userId);
-            clearUserKeyProtection(userId);
+            fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
             return;
         }
@@ -916,8 +923,12 @@
 
         byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
         if (enrolledHandle != null) {
+            CredentialHash willStore
+                = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
+            setUserKeyProtection(userId, password,
+                doVerifyPassword(password, willStore, true, 0, userId));
             mStorage.writePasswordHash(enrolledHandle, userId);
-            setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
+            fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
         } else {
             throw new RemoteException("Failed to enroll password");
@@ -1022,11 +1033,11 @@
         if (token == null) {
             throw new RemoteException("Empty payload verifying a credential we just set");
         }
-        changeUserKey(userId, token, secretFromCredential(credential));
+        addUserKeyAuth(userId, token, secretFromCredential(credential));
     }
 
     private void clearUserKeyProtection(int userId) throws RemoteException {
-        changeUserKey(userId, null, null);
+        addUserKeyAuth(userId, null, null);
     }
 
     private static byte[] secretFromCredential(String credential) throws RemoteException {
@@ -1045,18 +1056,23 @@
         }
     }
 
-    private void changeUserKey(int userId, byte[] token, byte[] secret)
+    private void addUserKeyAuth(int userId, byte[] token, byte[] secret)
             throws RemoteException {
         final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
         final IMountService mountService = getMountService();
         final long callingId = Binder.clearCallingIdentity();
         try {
-            mountService.changeUserKey(userId, userInfo.serialNumber, token, null, secret);
+            mountService.addUserKeyAuth(userId, userInfo.serialNumber, token, secret);
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
     }
 
+    private void fixateNewestUserKeyAuth(int userId)
+            throws RemoteException {
+        getMountService().fixateNewestUserKeyAuth(userId);
+    }
+
     @Override
     public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
         return doVerifyPattern(pattern, false, 0, userId);
@@ -1072,6 +1088,11 @@
             long challenge, int userId) throws RemoteException {
        checkPasswordReadPermission(userId);
        CredentialHash storedHash = mStorage.readPatternHash(userId);
+       return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId);
+    }
+
+    private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash,
+            boolean hasChallenge, long challenge, int userId) throws RemoteException {
        boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
 
        String patternToVerify;
@@ -1109,7 +1130,6 @@
        }
 
        return response;
-
     }
 
     @Override
@@ -1159,6 +1179,11 @@
             long challenge, int userId) throws RemoteException {
        checkPasswordReadPermission(userId);
        CredentialHash storedHash = mStorage.readPasswordHash(userId);
+       return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId);
+    }
+
+    private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash,
+            boolean hasChallenge, long challenge, int userId) throws RemoteException {
        return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
                new CredentialUtil() {
                    @Override
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index 9ab6300..ab91a73 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -74,7 +74,7 @@
 
     private SparseArray<Integer> mStoredCredentialType;
 
-    class CredentialHash {
+    static class CredentialHash {
         static final int TYPE_NONE = -1;
         static final int TYPE_PATTERN = 1;
         static final int TYPE_PASSWORD = 2;
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 25ce485..c89b6ea 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2816,15 +2816,36 @@
         }
     }
 
+    /*
+     * Add this token/secret pair to the set of ways we can recover a disk encryption key.
+     * Changing the token/secret for a disk encryption key is done in two phases: first, adding
+     * a new token/secret pair with this call, then delting all other pairs with
+     * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as
+     * Gatekeeper, to be updated between the two calls.
+     */
     @Override
-    public void changeUserKey(int userId, int serialNumber,
-            byte[] token, byte[] oldSecret, byte[] newSecret) {
+    public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) {
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
         try {
-            mCryptConnector.execute("cryptfs", "change_user_key", userId, serialNumber,
-                encodeBytes(token), encodeBytes(oldSecret), encodeBytes(newSecret));
+            mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber,
+                encodeBytes(token), encodeBytes(secret));
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    /*
+     * Delete all disk encryption token/secret pairs except the most recently added one
+     */
+    @Override
+    public void fixateNewestUserKeyAuth(int userId) {
+        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+        waitForReady();
+
+        try {
+            mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }