Merge "Use Scrypt to hash long passwords in RecoverableKeyStore" into pi-dev
am: e73074e9a8
Change-Id: I50e3ee0083badc478dd9be5c647b351bfd2a840c
This commit is contained in:
@@ -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<String, SecretKey> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<WrappedApplicationKey> 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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user