1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.locksettings.recoverablekeystore; 18 19import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN; 20import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD; 21import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PATTERN; 22import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PIN; 23import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; 24import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; 25 26import static com.google.common.truth.Truth.assertThat; 27 28import static org.junit.Assert.assertArrayEquals; 29import static org.junit.Assert.assertEquals; 30import static org.junit.Assert.assertFalse; 31import static org.junit.Assert.assertNotNull; 32import static org.junit.Assert.assertNull; 33import static org.junit.Assert.assertTrue; 34 35import static org.mockito.ArgumentMatchers.any; 36import static org.mockito.ArgumentMatchers.anyInt; 37import static org.mockito.ArgumentMatchers.eq; 38import static org.mockito.Mockito.atLeast; 39import static org.mockito.Mockito.never; 40import static org.mockito.Mockito.verify; 41import static org.mockito.Mockito.when; 42 43import android.content.Context; 44import android.os.FileUtils; 45import android.security.keystore.AndroidKeyStoreSecretKey; 46import android.security.keystore.KeyGenParameterSpec; 47import android.security.keystore.KeyProperties; 48import android.security.keystore.recovery.KeyChainSnapshot; 49import android.security.keystore.recovery.KeyDerivationParams; 50import android.security.keystore.recovery.RecoveryController; 51import android.security.keystore.recovery.TrustedRootCertificates; 52import android.security.keystore.recovery.WrappedApplicationKey; 53import android.support.test.InstrumentationRegistry; 54import android.support.test.filters.SmallTest; 55import android.support.test.runner.AndroidJUnit4; 56 57import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; 58import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; 59 60import org.junit.After; 61import org.junit.Before; 62import org.junit.Test; 63import org.junit.runner.RunWith; 64import org.mockito.Mock; 65import org.mockito.MockitoAnnotations; 66import org.mockito.Spy; 67 68import java.io.File; 69import java.nio.charset.StandardCharsets; 70import java.util.Arrays; 71import java.util.List; 72import java.util.Random; 73 74import javax.crypto.KeyGenerator; 75import javax.crypto.SecretKey; 76 77@SmallTest 78@RunWith(AndroidJUnit4.class) 79public class KeySyncTaskTest { 80 81 private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore"; 82 83 private static final String KEY_ALGORITHM = "AES"; 84 private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; 85 private static final String TEST_ROOT_CERT_ALIAS = "trusted_root"; 86 private static final String WRAPPING_KEY_ALIAS = "KeySyncTaskTest/WrappingKey"; 87 private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; 88 private static final int TEST_USER_ID = 1000; 89 private static final int TEST_RECOVERY_AGENT_UID = 10009; 90 private static final int TEST_RECOVERY_AGENT_UID2 = 10010; 91 private static final byte[] TEST_VAULT_HANDLE = 92 new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; 93 private static final String TEST_APP_KEY_ALIAS = "rcleaver"; 94 private static final int TEST_GENERATION_ID = 2; 95 private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PATTERN; 96 private static final String TEST_CREDENTIAL = "pas123"; 97 private static final byte[] THM_ENCRYPTED_RECOVERY_KEY_HEADER = 98 "V1 THM_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8); 99 100 @Mock private PlatformKeyManager mPlatformKeyManager; 101 @Mock private RecoverySnapshotListenersStorage mSnapshotListenersStorage; 102 @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; 103 @Spy private MockScrypt mMockScrypt; 104 105 private RecoverySnapshotStorage mRecoverySnapshotStorage; 106 private RecoverableKeyStoreDb mRecoverableKeyStoreDb; 107 private File mDatabaseFile; 108 private AndroidKeyStoreSecretKey mWrappingKey; 109 private PlatformEncryptionKey mEncryptKey; 110 111 private KeySyncTask mKeySyncTask; 112 113 @Before 114 public void setUp() throws Exception { 115 MockitoAnnotations.initMocks(this); 116 117 Context context = InstrumentationRegistry.getTargetContext(); 118 mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME); 119 mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context); 120 121 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, 122 new int[] {TYPE_LOCKSCREEN}); 123 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, 124 new int[] {TYPE_LOCKSCREEN}); 125 126 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, 127 TEST_ROOT_CERT_ALIAS); 128 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, 129 TEST_ROOT_CERT_ALIAS); 130 mRecoverySnapshotStorage = new RecoverySnapshotStorage(context.getFilesDir()); 131 132 mKeySyncTask = new KeySyncTask( 133 mRecoverableKeyStoreDb, 134 mRecoverySnapshotStorage, 135 mSnapshotListenersStorage, 136 TEST_USER_ID, 137 TEST_CREDENTIAL_TYPE, 138 TEST_CREDENTIAL, 139 /*credentialUpdated=*/ false, 140 mPlatformKeyManager, 141 mTestOnlyInsecureCertificateHelper, 142 mMockScrypt); 143 144 mWrappingKey = generateAndroidKeyStoreKey(); 145 mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey); 146 when(mPlatformKeyManager.getDecryptKey(TEST_USER_ID)).thenReturn( 147 new PlatformDecryptionKey(TEST_GENERATION_ID, mWrappingKey)); 148 } 149 150 @After 151 public void tearDown() { 152 mRecoverableKeyStoreDb.close(); 153 mDatabaseFile.delete(); 154 155 File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(), 156 SNAPSHOT_TOP_LEVEL_DIRECTORY); 157 FileUtils.deleteContentsAndDir(file); 158 } 159 160 @Test 161 public void isPin_isTrueForNumericString() { 162 assertTrue(KeySyncTask.isPin("3298432574398654376547")); 163 } 164 165 @Test 166 public void isPin_isFalseForStringContainingLetters() { 167 assertFalse(KeySyncTask.isPin("398i54369548654")); 168 } 169 170 @Test 171 public void isPin_isFalseForStringContainingSymbols() { 172 assertFalse(KeySyncTask.isPin("-3987543643")); 173 } 174 175 @Test 176 public void hashCredentialsBySaltedSha256_returnsSameHashForSameCredentialsAndSalt() { 177 String credentials = "password1234"; 178 byte[] salt = randomBytes(16); 179 180 assertArrayEquals( 181 KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials), 182 KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials)); 183 } 184 185 @Test 186 public void hashCredentialsBySaltedSha256_returnsDifferentHashForDifferentCredentials() { 187 byte[] salt = randomBytes(16); 188 189 assertFalse( 190 Arrays.equals( 191 KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234"), 192 KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345"))); 193 } 194 195 @Test 196 public void hashCredentialsBySaltedSha256_returnsDifferentHashForDifferentSalt() { 197 String credentials = "wowmuch"; 198 199 assertFalse( 200 Arrays.equals( 201 KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials), 202 KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials))); 203 } 204 205 @Test 206 public void hashCredentialsBySaltedSha256_returnsDifferentHashEvenIfConcatIsSame() { 207 assertFalse( 208 Arrays.equals( 209 KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"), "4567"), 210 KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"), "567"))); 211 } 212 213 @Test 214 public void getUiFormat_returnsPinIfPin() { 215 assertEquals(UI_FORMAT_PIN, 216 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234")); 217 } 218 219 @Test 220 public void getUiFormat_returnsPasswordIfPassword() { 221 assertEquals(UI_FORMAT_PASSWORD, 222 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a")); 223 } 224 225 @Test 226 public void getUiFormat_returnsPatternIfPattern() { 227 assertEquals(UI_FORMAT_PATTERN, 228 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234")); 229 230 } 231 232 @Test 233 public void run_doesNotSendAnythingIfNoKeysToSync() throws Exception { 234 mKeySyncTask.run(); 235 236 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); 237 } 238 239 @Test 240 public void run_doesNotSendAnythingIfSnapshotIsUpToDate() throws Exception { 241 mKeySyncTask.run(); 242 243 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); 244 } 245 246 @Test 247 public void run_doesNotSendAnythingIfNoRecoveryAgentSet() throws Exception { 248 SecretKey applicationKey = generateKey(); 249 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 250 mRecoverableKeyStoreDb.insertKey( 251 TEST_USER_ID, 252 TEST_RECOVERY_AGENT_UID, 253 TEST_APP_KEY_ALIAS, 254 WrappedKey.fromSecretKey(mEncryptKey, applicationKey)); 255 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 256 257 mKeySyncTask.run(); 258 259 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); 260 } 261 262 @Test 263 public void run_doesNotSendAnythingIfNoDeviceIdIsSet() throws Exception { 264 SecretKey applicationKey = generateKey(); 265 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 266 mRecoverableKeyStoreDb.insertKey( 267 TEST_USER_ID, 268 TEST_RECOVERY_AGENT_UID, 269 TEST_APP_KEY_ALIAS, 270 WrappedKey.fromSecretKey(mEncryptKey, applicationKey)); 271 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 272 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 273 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 274 275 mKeySyncTask.run(); 276 277 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); 278 } 279 280 @Test 281 public void run_useScryptToHashPasswordInTestMode() throws Exception { 282 String password = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + ""; // The shortest 283 String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias"; 284 mKeySyncTask = new KeySyncTask( 285 mRecoverableKeyStoreDb, 286 mRecoverySnapshotStorage, 287 mSnapshotListenersStorage, 288 TEST_USER_ID, 289 CREDENTIAL_TYPE_PASSWORD, 290 /*credential=*/ password, 291 /*credentialUpdated=*/ false, 292 mPlatformKeyManager, 293 mTestOnlyInsecureCertificateHelper, 294 mMockScrypt); 295 mRecoverableKeyStoreDb.setServerParams( 296 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 297 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 298 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, 299 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS); 300 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 301 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, 302 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS, 303 TestData.getInsecureCertPathForEndpoint1()); 304 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias); 305 306 mKeySyncTask.run(); 307 308 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 309 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); 310 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). 311 isEqualTo(UI_FORMAT_PASSWORD); 312 verify(mMockScrypt).scrypt(eq(password.getBytes()), any(), 313 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R), 314 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES)); 315 KeyDerivationParams keyDerivationParams = 316 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); 317 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( 318 KeyDerivationParams.ALGORITHM_SCRYPT); 319 assertThat(keyDerivationParams.getMemoryDifficulty()).isEqualTo(KeySyncTask.SCRYPT_PARAM_N); 320 } 321 322 @Test 323 public void run_useSha256ToHashPatternInProdMode() throws Exception { 324 String pattern = "123456"; 325 mKeySyncTask = new KeySyncTask( 326 mRecoverableKeyStoreDb, 327 mRecoverySnapshotStorage, 328 mSnapshotListenersStorage, 329 TEST_USER_ID, 330 CREDENTIAL_TYPE_PATTERN, 331 /*credential=*/ pattern, 332 /*credentialUpdated=*/ false, 333 mPlatformKeyManager, 334 mTestOnlyInsecureCertificateHelper, 335 mMockScrypt); 336 mRecoverableKeyStoreDb.setServerParams( 337 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 338 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 339 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 340 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 341 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 342 343 mKeySyncTask.run(); 344 345 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 346 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); 347 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). 348 isEqualTo(UI_FORMAT_PATTERN); 349 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); 350 KeyDerivationParams keyDerivationParams = 351 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); 352 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( 353 KeyDerivationParams.ALGORITHM_SHA256); 354 } 355 356 @Test 357 public void run_useScryptToHashPasswordInProdMode() throws Exception { 358 String shortPassword = "abc"; 359 mKeySyncTask = new KeySyncTask( 360 mRecoverableKeyStoreDb, 361 mRecoverySnapshotStorage, 362 mSnapshotListenersStorage, 363 TEST_USER_ID, 364 CREDENTIAL_TYPE_PASSWORD, 365 /*credential=*/ shortPassword, 366 /*credentialUpdated=*/ false, 367 mPlatformKeyManager, 368 mTestOnlyInsecureCertificateHelper, 369 mMockScrypt); 370 mRecoverableKeyStoreDb.setServerParams( 371 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 372 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 373 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 374 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 375 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 376 377 mKeySyncTask.run(); 378 379 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 380 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); 381 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). 382 isEqualTo(UI_FORMAT_PASSWORD); 383 verify(mMockScrypt).scrypt(eq(shortPassword.getBytes()), any(), 384 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R), 385 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES)); 386 KeyDerivationParams keyDerivationParams = 387 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); 388 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( 389 KeyDerivationParams.ALGORITHM_SCRYPT); 390 } 391 392 @Test 393 public void run_stillCreatesSnapshotIfNoRecoveryAgentPendingIntentRegistered() 394 throws Exception { 395 mRecoverableKeyStoreDb.setServerParams( 396 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 397 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 398 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 399 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 400 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 401 402 mKeySyncTask.run(); 403 404 assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); 405 } 406 407 @Test 408 public void run_InTestModeWithWhitelistedCredentials() throws Exception { 409 mRecoverableKeyStoreDb.setServerParams( 410 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 411 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 412 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 413 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 414 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 415 416 // Enter test mode with whitelisted credentials 417 when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true); 418 when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any())) 419 .thenReturn(true); 420 mKeySyncTask.run(); 421 422 verify(mTestOnlyInsecureCertificateHelper) 423 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS)); 424 425 // run whitelist checks 426 verify(mTestOnlyInsecureCertificateHelper) 427 .doesCredentialSupportInsecureMode(anyInt(), any()); 428 verify(mTestOnlyInsecureCertificateHelper) 429 .keepOnlyWhitelistedInsecureKeys(any()); 430 431 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 432 assertNotNull(keyChainSnapshot); // created snapshot 433 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); 434 assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included 435 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); 436 } 437 438 @Test 439 public void run_InTestModeWithNonWhitelistedCredentials() throws Exception { 440 mRecoverableKeyStoreDb.setServerParams( 441 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 442 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 443 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 444 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 445 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 446 447 // Enter test mode with non whitelisted credentials 448 when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true); 449 when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any())) 450 .thenReturn(false); 451 mKeySyncTask.run(); 452 453 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); // not created 454 verify(mTestOnlyInsecureCertificateHelper) 455 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS)); 456 verify(mTestOnlyInsecureCertificateHelper) 457 .doesCredentialSupportInsecureMode(anyInt(), any()); 458 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); 459 } 460 461 @Test 462 public void run_doesNotFilterCredentialsAndAliasesInProd() throws Exception { 463 mRecoverableKeyStoreDb.setServerParams( 464 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 465 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 466 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 467 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 468 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 469 470 mKeySyncTask.run(); 471 assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); 472 473 verify(mTestOnlyInsecureCertificateHelper) 474 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS)); 475 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 476 .isTestOnlyCertificateAlias(eq(TEST_ROOT_CERT_ALIAS)); 477 478 // no whitelists check 479 verify(mTestOnlyInsecureCertificateHelper, never()) 480 .doesCredentialSupportInsecureMode(anyInt(), any()); 481 verify(mTestOnlyInsecureCertificateHelper, never()) 482 .keepOnlyWhitelistedInsecureKeys(any()); 483 } 484 485 @Test 486 public void run_replacesNullActiveRootAliasWithDefaultValue() throws Exception { 487 mRecoverableKeyStoreDb.setServerParams( 488 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 489 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); 490 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 491 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 492 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 493 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, 494 /*alias=*/ null); 495 496 when(mTestOnlyInsecureCertificateHelper.getDefaultCertificateAliasIfEmpty(null)) 497 .thenReturn(TEST_ROOT_CERT_ALIAS); // override default. 498 mKeySyncTask.run(); 499 500 verify(mTestOnlyInsecureCertificateHelper).getDefaultCertificateAliasIfEmpty(null); 501 } 502 503 @Test 504 public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception { 505 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 506 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, 507 TestData.getInsecureCertPathForEndpoint1()); 508 509 mRecoverableKeyStoreDb.setServerParams( 510 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 511 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 512 SecretKey applicationKey = 513 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 514 515 mKeySyncTask.run(); 516 517 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 518 KeyDerivationParams keyDerivationParams = 519 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams(); 520 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo( 521 KeyDerivationParams.ALGORITHM_SHA256); 522 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); 523 byte[] lockScreenHash = KeySyncTask.hashCredentialsBySaltedSha256( 524 keyDerivationParams.getSalt(), 525 TEST_CREDENTIAL); 526 Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID); 527 assertThat(counterId).isNotNull(); 528 byte[] recoveryKey = decryptThmEncryptedKey( 529 lockScreenHash, 530 keyChainSnapshot.getEncryptedRecoveryKeyBlob(), 531 /*vaultParams=*/ KeySyncUtils.packVaultParams( 532 TestData.getInsecureCertPathForEndpoint1().getCertificates().get(0) 533 .getPublicKey(), 534 counterId, 535 /*maxAttempts=*/ 10, 536 TEST_VAULT_HANDLE)); 537 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); 538 assertThat(applicationKeys).hasSize(1); 539 assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId); 540 assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10); 541 assertThat(keyChainSnapshot.getTrustedHardwareCertPath()) 542 .isEqualTo(TestData.getInsecureCertPathForEndpoint1()); 543 assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE); 544 WrappedApplicationKey keyData = applicationKeys.get(0); 545 assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias()); 546 assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias()); 547 byte[] appKey = KeySyncUtils.decryptApplicationKey( 548 recoveryKey, keyData.getEncryptedKeyMaterial()); 549 assertThat(appKey).isEqualTo(applicationKey.getEncoded()); 550 } 551 552 @Test 553 public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception { 554 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 555 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 556 mRecoverableKeyStoreDb.setServerParams( 557 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); 558 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 559 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 560 561 mKeySyncTask.run(); 562 563 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 564 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); 565 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys(); 566 assertThat(applicationKeys).hasSize(1); 567 assertThat(keyChainSnapshot.getTrustedHardwareCertPath()) 568 .isEqualTo(TestData.CERT_PATH_1); 569 } 570 571 @Test 572 public void run_setsCorrectSnapshotVersion() throws Exception { 573 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 574 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 575 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 576 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 577 578 mKeySyncTask.run(); 579 580 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 581 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value; 582 mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true); 583 584 mKeySyncTask.run(); 585 586 keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 587 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated 588 } 589 590 @Test 591 public void run_recreatesMissingSnapshot() throws Exception { 592 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 593 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 594 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 595 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 596 597 mKeySyncTask.run(); 598 599 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 600 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value; 601 602 mRecoverySnapshotStorage.remove(TEST_RECOVERY_AGENT_UID); // corrupt snapshot. 603 604 mKeySyncTask.run(); 605 606 keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 607 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // Same version 608 } 609 610 @Test 611 public void run_setsCorrectTypeForPassword() throws Exception { 612 String password = "password"; 613 mKeySyncTask = new KeySyncTask( 614 mRecoverableKeyStoreDb, 615 mRecoverySnapshotStorage, 616 mSnapshotListenersStorage, 617 TEST_USER_ID, 618 CREDENTIAL_TYPE_PASSWORD, 619 password, 620 /*credentialUpdated=*/ false, 621 mPlatformKeyManager, 622 mTestOnlyInsecureCertificateHelper, 623 mMockScrypt); 624 625 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 626 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 627 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 628 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 629 630 mKeySyncTask.run(); 631 632 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 633 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); 634 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). 635 isEqualTo(UI_FORMAT_PASSWORD); 636 verify(mMockScrypt).scrypt(eq(password.getBytes()), any(), 637 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R), 638 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES)); 639 } 640 641 @Test 642 public void run_setsCorrectTypeForPin() throws Exception { 643 String pin = "1234"; 644 mKeySyncTask = new KeySyncTask( 645 mRecoverableKeyStoreDb, 646 mRecoverySnapshotStorage, 647 mSnapshotListenersStorage, 648 TEST_USER_ID, 649 CREDENTIAL_TYPE_PASSWORD, 650 /*credential=*/ pin, 651 /*credentialUpdated=*/ false, 652 mPlatformKeyManager, 653 mTestOnlyInsecureCertificateHelper, 654 mMockScrypt); 655 656 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 657 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 658 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 659 SecretKey applicationKey = 660 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 661 662 mKeySyncTask.run(); 663 664 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 665 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); 666 // Password with only digits is changed to pin. 667 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). 668 isEqualTo(UI_FORMAT_PIN); 669 verify(mMockScrypt).scrypt(eq(pin.getBytes()), any(), 670 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R), 671 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES)); 672 } 673 674 @Test 675 public void run_setsCorrectTypeForPattern() throws Exception { 676 mKeySyncTask = new KeySyncTask( 677 mRecoverableKeyStoreDb, 678 mRecoverySnapshotStorage, 679 mSnapshotListenersStorage, 680 TEST_USER_ID, 681 CREDENTIAL_TYPE_PATTERN, 682 "12345", 683 /*credentialUpdated=*/ false, 684 mPlatformKeyManager, 685 mTestOnlyInsecureCertificateHelper, 686 mMockScrypt); 687 688 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 689 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 690 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 691 SecretKey applicationKey = 692 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 693 694 mKeySyncTask.run(); 695 696 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); 697 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1); 698 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()). 699 isEqualTo(UI_FORMAT_PATTERN); 700 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); 701 } 702 703 @Test 704 public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception { 705 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 706 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 707 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 708 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 709 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 710 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true); 711 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 712 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS); 713 mKeySyncTask.run(); 714 715 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); 716 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2); 717 } 718 719 @Test 720 public void run_sendsEncryptedKeysOnlyForAgentWhichActiveUserSecretType() throws Exception { 721 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, 722 new int[] {TYPE_LOCKSCREEN, 1000}); 723 // Snapshot will not be created during unlock event. 724 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, 725 new int[] {1000}); 726 727 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 728 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 729 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 730 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 731 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 732 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true); 733 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 734 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS); 735 mKeySyncTask.run(); 736 737 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); 738 verify(mSnapshotListenersStorage, never()). 739 recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2); 740 } 741 742 @Test 743 public void run_notifiesNonregisteredAgent() throws Exception { 744 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 745 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 746 mRecoverableKeyStoreDb.setRecoveryServiceCertPath( 747 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1); 748 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); 749 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false); 750 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 751 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS); 752 mKeySyncTask.run(); 753 754 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); 755 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2); 756 } 757 758 @Test 759 public void run_customLockScreen_RecoveryStatusFailure() throws Exception { 760 mKeySyncTask = new KeySyncTask( 761 mRecoverableKeyStoreDb, 762 mRecoverySnapshotStorage, 763 mSnapshotListenersStorage, 764 TEST_USER_ID, 765 /*credentialType=*/ 3, 766 "12345", 767 /*credentialUpdated=*/ false, 768 mPlatformKeyManager, 769 mTestOnlyInsecureCertificateHelper, 770 mMockScrypt); 771 772 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); 773 774 int status = 775 mRecoverableKeyStoreDb 776 .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID) 777 .get(TEST_APP_KEY_ALIAS); 778 assertEquals(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS, status); 779 780 mKeySyncTask.run(); 781 782 status = mRecoverableKeyStoreDb 783 .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID) 784 .get(TEST_APP_KEY_ALIAS); 785 assertEquals(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE, status); 786 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt()); 787 } 788 789 private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias) 790 throws Exception{ 791 SecretKey applicationKey = generateKey(); 792 mRecoverableKeyStoreDb.setServerParams( 793 userId, recoveryAgentUid, TEST_VAULT_HANDLE); 794 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, TEST_GENERATION_ID); 795 796 // Newly added key is not synced. 797 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, recoveryAgentUid, true); 798 799 mRecoverableKeyStoreDb.insertKey( 800 userId, 801 recoveryAgentUid, 802 alias, 803 WrappedKey.fromSecretKey(mEncryptKey, applicationKey)); 804 return applicationKey; 805 } 806 807 private byte[] decryptThmEncryptedKey( 808 byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception { 809 byte[] locallyEncryptedKey = SecureBox.decrypt( 810 TestData.getInsecurePrivateKeyForEndpoint1(), 811 /*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash), 812 /*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams), 813 encryptedKey 814 ); 815 return KeySyncUtils.decryptRecoveryKey(lockScreenHash, locallyEncryptedKey); 816 } 817 818 private SecretKey generateKey() throws Exception { 819 KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); 820 keyGenerator.init(/*keySize=*/ 256); 821 return keyGenerator.generateKey(); 822 } 823 824 private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception { 825 KeyGenerator keyGenerator = KeyGenerator.getInstance( 826 KEY_ALGORITHM, 827 ANDROID_KEY_STORE_PROVIDER); 828 keyGenerator.init(new KeyGenParameterSpec.Builder( 829 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 830 .setBlockModes(KeyProperties.BLOCK_MODE_GCM) 831 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 832 .build()); 833 return (AndroidKeyStoreSecretKey) keyGenerator.generateKey(); 834 } 835 836 private static byte[] utf8Bytes(String s) { 837 return s.getBytes(StandardCharsets.UTF_8); 838 } 839 840 private static byte[] randomBytes(int n) { 841 byte[] bytes = new byte[n]; 842 new Random().nextBytes(bytes); 843 return bytes; 844 } 845} 846