From 76973437e9cb36eb60fb2a374366713307688136 Mon Sep 17 00:00:00 2001 From: Bo Zhu Date: Tue, 3 Apr 2018 00:37:51 -0700 Subject: [PATCH] Use Scrypt to hash long passwords in RecoverableKeyStore It's enabled for test mode for now, and will need to be updated after the other components of the system is updated. Bug: 77325751 Test: runtest frameworks-services -p com.android.server.locksettings.recoverablekeystore Change-Id: I4fc77aba37aeddbe6a82b633934b068cd4ac507f --- .../recoverablekeystore/KeySyncTask.java | 56 ++++- .../recoverablekeystore/KeySyncTaskTest.java | 200 ++++++++++++++++-- .../recoverablekeystore/MockScrypt.java | 53 +++++ 3 files changed, 283 insertions(+), 26 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/MockScrypt.java diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index 4c4176a4769cb..567eaaa5d1596 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -20,6 +20,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_L import android.annotation.Nullable; import android.content.Context; +import android.security.Scrypt; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; @@ -69,6 +70,17 @@ public class KeySyncTask implements Runnable { private static final String LOCK_SCREEN_HASH_ALGORITHM = "SHA-256"; private static final int TRUSTED_HARDWARE_MAX_ATTEMPTS = 10; + // TODO: Reduce the minimal length once all other components are updated + private static final int MIN_CREDENTIAL_LEN_TO_USE_SCRYPT = 24; + @VisibleForTesting + static final int SCRYPT_PARAM_N = 4096; + @VisibleForTesting + static final int SCRYPT_PARAM_R = 8; + @VisibleForTesting + static final int SCRYPT_PARAM_P = 1; + @VisibleForTesting + static final int SCRYPT_PARAM_OUTLEN_BYTES = 32; + private final RecoverableKeyStoreDb mRecoverableKeyStoreDb; private final int mUserId; private final int mCredentialType; @@ -78,6 +90,7 @@ public class KeySyncTask implements Runnable { private final RecoverySnapshotStorage mRecoverySnapshotStorage; private final RecoverySnapshotListenersStorage mSnapshotListenersStorage; private final TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; + private final Scrypt mScrypt; public static KeySyncTask newInstance( Context context, @@ -98,7 +111,8 @@ public class KeySyncTask implements Runnable { credential, credentialUpdated, PlatformKeyManager.getInstance(context, recoverableKeyStoreDb), - new TestOnlyInsecureCertificateHelper()); + new TestOnlyInsecureCertificateHelper(), + new Scrypt()); } /** @@ -110,7 +124,7 @@ public class KeySyncTask implements Runnable { * @param credential The credential, encoded as a {@link String}. * @param credentialUpdated signals weather credentials were updated. * @param platformKeyManager platform key manager - * @param TestOnlyInsecureCertificateHelper utility class used for end-to-end tests + * @param testOnlyInsecureCertificateHelper utility class used for end-to-end tests */ @VisibleForTesting KeySyncTask( @@ -122,7 +136,8 @@ public class KeySyncTask implements Runnable { String credential, boolean credentialUpdated, PlatformKeyManager platformKeyManager, - TestOnlyInsecureCertificateHelper TestOnlyInsecureCertificateHelper) { + TestOnlyInsecureCertificateHelper testOnlyInsecureCertificateHelper, + Scrypt scrypt) { mSnapshotListenersStorage = recoverySnapshotListenersStorage; mRecoverableKeyStoreDb = recoverableKeyStoreDb; mUserId = userId; @@ -131,7 +146,8 @@ public class KeySyncTask implements Runnable { mCredentialUpdated = credentialUpdated; mPlatformKeyManager = platformKeyManager; mRecoverySnapshotStorage = snapshotStorage; - mTestOnlyInsecureCertificateHelper = TestOnlyInsecureCertificateHelper; + mTestOnlyInsecureCertificateHelper = testOnlyInsecureCertificateHelper; + mScrypt = scrypt; } @Override @@ -230,8 +246,14 @@ public class KeySyncTask implements Runnable { } } + boolean useScryptToHashCredential = shouldUseScryptToHashCredential(rootCertAlias); byte[] salt = generateSalt(); - byte[] localLskfHash = hashCredentials(salt, mCredential); + byte[] localLskfHash; + if (useScryptToHashCredential) { + localLskfHash = hashCredentialsByScrypt(salt, mCredential); + } else { + localLskfHash = hashCredentialsBySaltedSha256(salt, mCredential); + } Map rawKeys; try { @@ -303,10 +325,17 @@ public class KeySyncTask implements Runnable { Log.e(TAG,"Could not encrypt with recovery key", e); return; } + KeyDerivationParams keyDerivationParams; + if (useScryptToHashCredential) { + keyDerivationParams = KeyDerivationParams.createScryptParams( + salt, /*memoryDifficulty=*/ SCRYPT_PARAM_N); + } else { + keyDerivationParams = KeyDerivationParams.createSha256Params(salt); + } KeyChainProtectionParams metadata = new KeyChainProtectionParams.Builder() .setUserSecretType(TYPE_LOCKSCREEN) .setLockScreenUiFormat(getUiFormat(mCredentialType, mCredential)) - .setKeyDerivationParams(KeyDerivationParams.createSha256Params(salt)) + .setKeyDerivationParams(keyDerivationParams) .setSecret(new byte[0]) .build(); @@ -443,7 +472,7 @@ public class KeySyncTask implements Runnable { * @return The SHA-256 hash. */ @VisibleForTesting - static byte[] hashCredentials(byte[] salt, String credentials) { + static byte[] hashCredentialsBySaltedSha256(byte[] salt, String credentials) { byte[] credentialsBytes = credentials.getBytes(StandardCharsets.UTF_8); ByteBuffer byteBuffer = ByteBuffer.allocate( salt.length + credentialsBytes.length + LENGTH_PREFIX_BYTES * 2); @@ -462,6 +491,12 @@ public class KeySyncTask implements Runnable { } } + private byte[] hashCredentialsByScrypt(byte[] salt, String credentials) { + return mScrypt.scrypt( + credentials.getBytes(StandardCharsets.UTF_8), salt, + SCRYPT_PARAM_N, SCRYPT_PARAM_R, SCRYPT_PARAM_P, SCRYPT_PARAM_OUTLEN_BYTES); + } + private static SecretKey generateRecoveryKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance(RECOVERY_KEY_ALGORITHM); keyGenerator.init(RECOVERY_KEY_SIZE_BITS); @@ -479,4 +514,11 @@ public class KeySyncTask implements Runnable { } return keyEntries; } + + private boolean shouldUseScryptToHashCredential(String rootCertAlias) { + return mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD + && mCredential.length() >= MIN_CREDENTIAL_LEN_TO_USE_SCRYPT + // TODO: Remove the test cert check once all other components are updated + && mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(rootCertAlias); + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index 7ce5904d74d6d..5a7dfa2c3bc22 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -50,6 +50,7 @@ import android.security.keystore.KeyProperties; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.RecoveryController; +import android.security.keystore.recovery.TrustedRootCertificates; import android.security.keystore.recovery.WrappedApplicationKey; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -101,6 +102,7 @@ public class KeySyncTaskTest { @Mock private PlatformKeyManager mPlatformKeyManager; @Mock private RecoverySnapshotListenersStorage mSnapshotListenersStorage; @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; + @Spy private MockScrypt mMockScrypt; private RecoverySnapshotStorage mRecoverySnapshotStorage; private RecoverableKeyStoreDb mRecoverableKeyStoreDb; @@ -138,7 +140,8 @@ public class KeySyncTaskTest { TEST_CREDENTIAL, /*credentialUpdated=*/ false, mPlatformKeyManager, - mTestOnlyInsecureCertificateHelper); + mTestOnlyInsecureCertificateHelper, + mMockScrypt); mWrappingKey = generateAndroidKeyStoreKey(); mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey); @@ -172,41 +175,41 @@ public class KeySyncTaskTest { } @Test - public void hashCredentials_returnsSameHashForSameCredentialsAndSalt() { + public void hashCredentialsBySaltedSha256_returnsSameHashForSameCredentialsAndSalt() { String credentials = "password1234"; byte[] salt = randomBytes(16); assertArrayEquals( - KeySyncTask.hashCredentials(salt, credentials), - KeySyncTask.hashCredentials(salt, credentials)); + KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials), + KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials)); } @Test - public void hashCredentials_returnsDifferentHashForDifferentCredentials() { + public void hashCredentialsBySaltedSha256_returnsDifferentHashForDifferentCredentials() { byte[] salt = randomBytes(16); assertFalse( Arrays.equals( - KeySyncTask.hashCredentials(salt, "password1234"), - KeySyncTask.hashCredentials(salt, "password12345"))); + KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234"), + KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345"))); } @Test - public void hashCredentials_returnsDifferentHashForDifferentSalt() { + public void hashCredentialsBySaltedSha256_returnsDifferentHashForDifferentSalt() { String credentials = "wowmuch"; assertFalse( Arrays.equals( - KeySyncTask.hashCredentials(randomBytes(64), credentials), - KeySyncTask.hashCredentials(randomBytes(64), credentials))); + KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials), + KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials))); } @Test - public void hashCredentials_returnsDifferentHashEvenIfConcatIsSame() { + public void hashCredentialsBySaltedSha256_returnsDifferentHashEvenIfConcatIsSame() { assertFalse( Arrays.equals( - KeySyncTask.hashCredentials(utf8Bytes("123"), "4567"), - KeySyncTask.hashCredentials(utf8Bytes("1234"), "567"))); + KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"), "4567"), + KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"), "567"))); } @Test @@ -276,6 +279,155 @@ public class KeySyncTaskTest { assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); } + @Test + public void run_useScryptToHashLongPasswordInTestMode() throws Exception { + String longPassword = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "0123456789"; + String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias"; + mKeySyncTask = new KeySyncTask( + mRecoverableKeyStoreDb, + mRecoverySnapshotStorage, + mSnapshotListenersStorage, + TEST_USER_ID, + CREDENTIAL_TYPE_PASSWORD, + /*credential=*/ longPassword, + /*credentialUpdated=*/ false, + mPlatformKeyManager, + mTestOnlyInsecureCertificateHelper, + mMockScrypt); + mRecoverableKeyStoreDb.setServerParams( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); + mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); + mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, + TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS); + mRecoverableKeyStoreDb.setRecoveryServiceCertPath( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, + TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS, + TestData.getInsecureCertPathForEndpoint1()); + addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias); + + mKeySyncTask.run(); + + KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); + assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); + assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). + isEqualTo(UI_FORMAT_PASSWORD); + verify(mMockScrypt).scrypt(eq(longPassword.getBytes()), any(), + eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R), + eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES)); + KeyDerivationParams keyDerivationParams = + keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); + assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( + KeyDerivationParams.ALGORITHM_SCRYPT); + assertThat(keyDerivationParams.getMemoryDifficulty()).isEqualTo(KeySyncTask.SCRYPT_PARAM_N); + } + + @Test + public void run_useSha256ToHashShortPasswordInTestMode() throws Exception { + String shortPassword = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "012345678"; + String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias"; + mKeySyncTask = new KeySyncTask( + mRecoverableKeyStoreDb, + mRecoverySnapshotStorage, + mSnapshotListenersStorage, + TEST_USER_ID, + CREDENTIAL_TYPE_PASSWORD, + /*credential=*/ shortPassword, + /*credentialUpdated=*/ false, + mPlatformKeyManager, + mTestOnlyInsecureCertificateHelper, + mMockScrypt); + mRecoverableKeyStoreDb.setServerParams( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); + mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); + mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, + TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS); + mRecoverableKeyStoreDb.setRecoveryServiceCertPath( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, + TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS, + TestData.getInsecureCertPathForEndpoint1()); + addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias); + + mKeySyncTask.run(); + + KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); + assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); + assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). + isEqualTo(UI_FORMAT_PASSWORD); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); + KeyDerivationParams keyDerivationParams = + keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); + assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( + KeyDerivationParams.ALGORITHM_SHA256); + } + + @Test + public void run_useSha256ToHashShortPasswordInProdMode() throws Exception { + String shortPassword = "01234567890123456789abc"; // 23 chars + mKeySyncTask = new KeySyncTask( + mRecoverableKeyStoreDb, + mRecoverySnapshotStorage, + mSnapshotListenersStorage, + TEST_USER_ID, + CREDENTIAL_TYPE_PASSWORD, + /*credential=*/ shortPassword, + /*credentialUpdated=*/ false, + mPlatformKeyManager, + mTestOnlyInsecureCertificateHelper, + mMockScrypt); + mRecoverableKeyStoreDb.setServerParams( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); + mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); + addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); + mRecoverableKeyStoreDb.setRecoveryServiceCertPath( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); + + mKeySyncTask.run(); + + KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); + assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); + assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). + isEqualTo(UI_FORMAT_PASSWORD); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); + KeyDerivationParams keyDerivationParams = + keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); + assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( + KeyDerivationParams.ALGORITHM_SHA256); + } + + @Test + public void run_useSha256ToHashLongPasswordInProdMode() throws Exception { + String longPassword = "01234567890123456789abcd"; // 24 chars + mKeySyncTask = new KeySyncTask( + mRecoverableKeyStoreDb, + mRecoverySnapshotStorage, + mSnapshotListenersStorage, + TEST_USER_ID, + CREDENTIAL_TYPE_PASSWORD, + /*credential=*/ longPassword, + /*credentialUpdated=*/ false, + mPlatformKeyManager, + mTestOnlyInsecureCertificateHelper, + mMockScrypt); + mRecoverableKeyStoreDb.setServerParams( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); + mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); + addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); + mRecoverableKeyStoreDb.setRecoveryServiceCertPath( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); + + mKeySyncTask.run(); + + KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); + assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); + assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). + isEqualTo(UI_FORMAT_PASSWORD); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); + KeyDerivationParams keyDerivationParams = + keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); + assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( + KeyDerivationParams.ALGORITHM_SHA256); + } + @Test public void run_stillCreatesSnapshotIfNoRecoveryAgentPendingIntentRegistered() throws Exception { @@ -319,6 +471,7 @@ public class KeySyncTaskTest { assertNotNull(keyChainSnapshot); // created snapshot List applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } @Test @@ -341,6 +494,7 @@ public class KeySyncTaskTest { .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS)); verify(mTestOnlyInsecureCertificateHelper) .doesCredentialSupportInsecureMode(anyInt(), any()); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } @Test @@ -404,7 +558,7 @@ public class KeySyncTaskTest { assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( KeyDerivationParams.ALGORITHM_SHA256); verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); - byte[] lockScreenHash = KeySyncTask.hashCredentials( + byte[] lockScreenHash = KeySyncTask.hashCredentialsBySaltedSha256( keyDerivationParams.getSalt(), TEST_CREDENTIAL); Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID); @@ -501,7 +655,8 @@ public class KeySyncTaskTest { "password", /*credentialUpdated=*/ false, mPlatformKeyManager, - mTestOnlyInsecureCertificateHelper); + mTestOnlyInsecureCertificateHelper, + mMockScrypt); mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); @@ -515,9 +670,10 @@ public class KeySyncTaskTest { assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). isEqualTo(UI_FORMAT_PASSWORD); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } - @Test + @Test public void run_setsCorrectTypeForPin() throws Exception { mKeySyncTask = new KeySyncTask( mRecoverableKeyStoreDb, @@ -528,7 +684,8 @@ public class KeySyncTaskTest { /*credential=*/ "1234", /*credentialUpdated=*/ false, mPlatformKeyManager, - mTestOnlyInsecureCertificateHelper); + mTestOnlyInsecureCertificateHelper, + mMockScrypt); mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); @@ -543,6 +700,7 @@ public class KeySyncTaskTest { // Password with only digits is changed to pin. assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). isEqualTo(UI_FORMAT_PIN); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } @Test @@ -556,7 +714,8 @@ public class KeySyncTaskTest { "12345", /*credentialUpdated=*/ false, mPlatformKeyManager, - mTestOnlyInsecureCertificateHelper); + mTestOnlyInsecureCertificateHelper, + mMockScrypt); mRecoverableKeyStoreDb.setRecoveryServiceCertPath( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); @@ -570,6 +729,7 @@ public class KeySyncTaskTest { assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). isEqualTo(UI_FORMAT_PATTERN); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } @Test @@ -638,7 +798,8 @@ public class KeySyncTaskTest { "12345", /*credentialUpdated=*/ false, mPlatformKeyManager, - mTestOnlyInsecureCertificateHelper); + mTestOnlyInsecureCertificateHelper, + mMockScrypt); addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); @@ -654,6 +815,7 @@ public class KeySyncTaskTest { .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID) .get(TEST_APP_KEY_ALIAS); assertEquals(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE, status); + verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); } private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias) diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/MockScrypt.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/MockScrypt.java new file mode 100644 index 0000000000000..e736e25a46c9b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/MockScrypt.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 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 com.android.server.locksettings.recoverablekeystore; + +import static org.junit.Assert.assertEquals; + +import android.security.Scrypt; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class MockScrypt extends Scrypt { + + @Override + public byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) { + assertEquals(32, outLen); + + ByteBuffer byteBuffer = ByteBuffer.allocate( + password.length + salt.length + Integer.BYTES * 6); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.putInt(password.length); + byteBuffer.put(password); + byteBuffer.putInt(salt.length); + byteBuffer.put(salt); + byteBuffer.putInt(n); + byteBuffer.putInt(r); + byteBuffer.putInt(p); + byteBuffer.putInt(outLen); + + try { + return MessageDigest.getInstance("SHA-256").digest(byteBuffer.array()); + } catch (NoSuchAlgorithmException e) { + // Should never happen + throw new RuntimeException(e); + } + } +}