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); + } + } +}