From 24bfd61d3c8bac869eb4e525e382476d3b7f5eb9 Mon Sep 17 00:00:00 2001 From: Rubin Xu Date: Tue, 31 Oct 2017 15:40:32 +0000 Subject: [PATCH] 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 --- .../server/SyntheticPasswordCrypto.java | 21 +++++++++++++--- .../server/SyntheticPasswordManager.java | 25 ++++++++++++++----- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/SyntheticPasswordCrypto.java index 71ab2a5540715..b6a9b76046d6c 100644 --- a/services/core/java/com/android/server/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/SyntheticPasswordCrypto.java @@ -112,7 +112,7 @@ public class SyntheticPasswordCrypto { } } - 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 @@ public class SyntheticPasswordCrypto { 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 @@ public class SyntheticPasswordCrypto { 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/SyntheticPasswordManager.java b/services/core/java/com/android/server/SyntheticPasswordManager.java index 1d17ff72940da..2da2773284760 100644 --- a/services/core/java/com/android/server/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/SyntheticPasswordManager.java @@ -75,7 +75,8 @@ public class SyntheticPasswordManager { public static final long DEFAULT_HANDLE = 0; private static final String DEFAULT_PASSWORD = "default-password"; - 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; @@ -468,10 +469,11 @@ public class SyntheticPasswordManager { } + final long sid = sidFromPasswordHandle(pwd.passwordHandle); byte[] 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); @@ -490,7 +492,7 @@ public class SyntheticPasswordManager { byte[] applicationId = transformUnderSecdiscardable(token, loadSecdiscardable(handle, userId)); 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) { @@ -505,19 +507,26 @@ public class SyntheticPasswordManager { } 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; @@ -532,6 +541,10 @@ public class SyntheticPasswordManager { } 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; }