Swap the order of synthetic password wrapping

Synthetic password is double encrypted by both a random auth-bound keymaster
key and a secret derived from user password. In order to avoid a password
verification oracle without rate limiting, synthetic password needs to be
encrypted by the derived secret first, and then the auth-bound key. This
change corrects the order of encryptions, as well as adds an upgrade path to
refresh existing credentials.

Test: Running an old build with existing password, flash to new build,
      verify the device unlocks successfully.
Bug: 68694819

Change-Id: Ifdaa01f3f4ddd5bb3f3d808d38f440ced729034f
Merged-In: Ifdaa01f3f4ddd5bb3f3d808d38f440ced729034f
(cherry picked from commit 78acfe71d5d527ec727ffa3ad33f0de6255d60d7)
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index b7bca1f..ef94000 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -112,7 +112,7 @@
         }
     }
 
-    public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
+    public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
         try {
             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
             keyStore.load(null);
@@ -120,6 +120,20 @@
             SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
             byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
             return decrypt(decryptionKey, intermediate);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("Failed to decrypt blob", e);
+        }
+    }
+
+    public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+
+            SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
+            byte[] intermediate = decrypt(decryptionKey, blob);
+            return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
         } catch (CertificateException | IOException | BadPaddingException
                 | IllegalBlockSizeException
                 | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
@@ -150,9 +164,8 @@
             keyStore.setEntry(keyAlias,
                     new KeyStore.SecretKeyEntry(secretKey),
                     builder.build());
-            byte[] intermediate = encrypt(secretKey, data);
-            return encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
-
+            byte[] intermediate = encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, data);
+            return encrypt(secretKey, intermediate);
         } catch (CertificateException | IOException | BadPaddingException
                 | IllegalBlockSizeException
                 | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 9440f17..ca6c9e7 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -101,7 +101,8 @@
     private static final byte WEAVER_VERSION = 1;
     private static final int INVALID_WEAVER_SLOT = -1;
 
-    private static final byte SYNTHETIC_PASSWORD_VERSION = 1;
+    private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
+    private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
     private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
 
@@ -792,6 +793,7 @@
         byte[] pwdToken = computePasswordToken(credential, pwd);
 
         final byte[] applicationId;
+        final long sid;
         int weaverSlot = loadWeaverSlot(handle, userId);
         if (weaverSlot != INVALID_WEAVER_SLOT) {
             // Weaver based user password
@@ -804,6 +806,7 @@
             if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
                 return result;
             }
+            sid = GateKeeper.INVALID_SECURE_USER_ID;
             applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
         } else {
             byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
@@ -836,12 +839,13 @@
                 result.gkResponse = VerifyCredentialResponse.ERROR;
                 return result;
             }
+            sid = sidFromPasswordHandle(pwd.passwordHandle);
             applicationId = transformUnderSecdiscardable(pwdToken,
                     loadSecdiscardable(handle, userId));
         }
 
         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
-                applicationId, userId);
+                applicationId, sid, userId);
 
         // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
         result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
@@ -877,7 +881,7 @@
         }
         byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
-                applicationId, userId);
+                applicationId, 0L, userId);
         if (result.authToken != null) {
             result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
             if (result.gkResponse == null) {
@@ -892,19 +896,26 @@
     }
 
     private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
-            byte[] applicationId, int userId) {
+            byte[] applicationId, long sid, int userId) {
         byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
         if (blob == null) {
             return null;
         }
-        if (blob[0] != SYNTHETIC_PASSWORD_VERSION) {
+        final byte version = blob[0];
+        if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
             throw new RuntimeException("Unknown blob version");
         }
         if (blob[1] != type) {
             throw new RuntimeException("Invalid blob type");
         }
-        byte[] secret = decryptSPBlob(getHandleName(handle),
+        final byte[] secret;
+        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
+            secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
+                    Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+        } else {
+            secret = decryptSPBlob(getHandleName(handle),
                 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+        }
         if (secret == null) {
             Log.e(TAG, "Fail to decrypt SP for user " + userId);
             return null;
@@ -919,6 +930,10 @@
         } else {
             result.syntheticPassword = new String(secret);
         }
+        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
+            Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
+            createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
+        }
         return result;
     }