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 17 package com.android.server.locksettings.recoverablekeystore; 18 19 import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN; 20 import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD; 21 import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT; 22 import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE; 23 import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import static org.junit.Assert.assertArrayEquals; 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.fail; 30 import static org.mockito.ArgumentMatchers.any; 31 import static org.mockito.ArgumentMatchers.anyInt; 32 import static org.mockito.ArgumentMatchers.anyLong; 33 import static org.mockito.ArgumentMatchers.anyString; 34 import static org.mockito.ArgumentMatchers.eq; 35 import static org.mockito.Mockito.atLeast; 36 import static org.mockito.Mockito.times; 37 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.when; 39 40 import android.Manifest; 41 import android.app.KeyguardManager; 42 import android.app.PendingIntent; 43 import android.app.RemoteLockscreenValidationResult; 44 import android.app.RemoteLockscreenValidationSession; 45 import android.content.Context; 46 import android.content.Intent; 47 import android.os.Binder; 48 import android.os.ServiceSpecificException; 49 import android.os.UserHandle; 50 import android.security.keystore.KeyGenParameterSpec; 51 import android.security.keystore.KeyProperties; 52 import android.security.keystore.recovery.KeyChainProtectionParams; 53 import android.security.keystore.recovery.KeyDerivationParams; 54 import android.security.keystore.recovery.RecoveryCertPath; 55 import android.security.keystore.recovery.TrustedRootCertificates; 56 import android.security.keystore.recovery.WrappedApplicationKey; 57 import android.util.Pair; 58 59 import androidx.test.InstrumentationRegistry; 60 import androidx.test.filters.SmallTest; 61 import androidx.test.runner.AndroidJUnit4; 62 63 import com.android.internal.util.ArrayUtils; 64 import com.android.internal.widget.LockPatternUtils; 65 import com.android.internal.widget.LockscreenCredential; 66 import com.android.internal.widget.VerifyCredentialResponse; 67 import com.android.security.SecureBox; 68 import com.android.server.locksettings.LockSettingsService; 69 import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage; 70 import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager; 71 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; 72 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage; 73 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; 74 import com.android.server.locksettings.recoverablekeystore.storage.RemoteLockscreenValidationSessionStorage; 75 76 import com.google.common.collect.ImmutableList; 77 import com.google.common.collect.ImmutableMap; 78 79 import org.junit.After; 80 import org.junit.Before; 81 import org.junit.Ignore; 82 import org.junit.Test; 83 import org.junit.runner.RunWith; 84 import org.mockito.Mock; 85 import org.mockito.MockitoAnnotations; 86 import org.mockito.Spy; 87 88 import java.io.File; 89 import java.nio.charset.StandardCharsets; 90 import java.security.PublicKey; 91 import java.security.cert.CertPath; 92 import java.security.cert.CertificateFactory; 93 import java.security.cert.X509Certificate; 94 import java.util.ArrayList; 95 import java.util.Arrays; 96 import java.util.Map; 97 import java.util.Random; 98 import java.util.concurrent.ScheduledExecutorService; 99 100 import javax.crypto.KeyGenerator; 101 import javax.crypto.SecretKey; 102 import javax.crypto.spec.SecretKeySpec; 103 104 @SmallTest 105 @RunWith(AndroidJUnit4.class) 106 public class RecoverableKeyStoreManagerTest { 107 private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; 108 109 private static final String ROOT_CERTIFICATE_ALIAS = ""; 110 private static final String DEFAULT_ROOT_CERT_ALIAS = 111 TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS; 112 private static final String INSECURE_CERTIFICATE_ALIAS = 113 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS; 114 private static final String TEST_SESSION_ID = "karlin"; 115 private static final byte[] TEST_PUBLIC_KEY = TestData.CERT_1_PUBLIC_KEY.getEncoded(); 116 private static final byte[] TEST_SALT = getUtf8Bytes("salt"); 117 private static final byte[] TEST_SECRET = getUtf8Bytes("password1234"); 118 private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge"); 119 private static final byte[] TEST_VAULT_PARAMS = new byte[] { 120 // backend_key 121 (byte) 0x04, (byte) 0x8e, (byte) 0x0c, (byte) 0x11, (byte) 0x4a, (byte) 0x79, (byte) 0x20, 122 (byte) 0x7c, (byte) 0x00, (byte) 0x4c, (byte) 0xd7, (byte) 0xe9, (byte) 0x06, (byte) 0xe2, 123 (byte) 0x58, (byte) 0x21, (byte) 0x45, (byte) 0xfa, (byte) 0x24, (byte) 0xcb, (byte) 0x07, 124 (byte) 0x66, (byte) 0xde, (byte) 0xfd, (byte) 0xf1, (byte) 0x83, (byte) 0xb4, (byte) 0x26, 125 (byte) 0x55, (byte) 0x98, (byte) 0xcb, (byte) 0xa9, (byte) 0xd5, (byte) 0x55, (byte) 0xad, 126 (byte) 0x65, (byte) 0xc5, (byte) 0xff, (byte) 0x5c, (byte) 0xfb, (byte) 0x1c, (byte) 0x4e, 127 (byte) 0x34, (byte) 0x98, (byte) 0x7e, (byte) 0x4f, (byte) 0x96, (byte) 0xa2, (byte) 0xa3, 128 (byte) 0x7e, (byte) 0xf4, (byte) 0x46, (byte) 0x52, (byte) 0x04, (byte) 0xba, (byte) 0x2a, 129 (byte) 0xb9, (byte) 0x47, (byte) 0xbb, (byte) 0xc2, (byte) 0x1e, (byte) 0xdd, (byte) 0x15, 130 (byte) 0x1a, (byte) 0xc0, 131 // counter_id 132 (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x00, (byte) 0x00, (byte) 0x00, 133 (byte) 0x00, 134 // device_parameter 135 (byte) 0x78, (byte) 0x56, (byte) 0x34, (byte) 0x12, (byte) 0x00, (byte) 0x00, (byte) 0x00, 136 (byte) 0x0, 137 // max_attempts 138 (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00}; 139 private static final int TEST_GENERATION_ID = 2; 140 private static final int TEST_USER_ID = 10009; 141 private static final int KEY_CLAIMANT_LENGTH_BYTES = 16; 142 private static final byte[] RECOVERY_RESPONSE_HEADER = 143 "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8); 144 private static final String TEST_ALIAS = "nick"; 145 private static final String TEST_ALIAS2 = "bob"; 146 private static final int APPLICATION_KEY_SIZE_BYTES = 32; 147 private static final int GENERATION_ID = 1; 148 private static final byte[] NONCE = getUtf8Bytes("nonce"); 149 private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial"); 150 private static final byte[] KEY_METADATA_NULL = null; 151 private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata"); 152 private static final String KEY_ALGORITHM = "AES"; 153 private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; 154 private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey"; 155 private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = ""; 156 private static final KeyChainProtectionParams TEST_PROTECTION_PARAMS = 157 new KeyChainProtectionParams.Builder() 158 .setUserSecretType(TYPE_LOCKSCREEN) 159 .setLockScreenUiFormat(UI_FORMAT_PASSWORD) 160 .setKeyDerivationParams(KeyDerivationParams.createSha256Params(TEST_SALT)) 161 .setSecret(TEST_SECRET) 162 .build(); 163 private static final byte[] VALID_GUESS = getUtf8Bytes("password123"); 164 private static final byte[] INVALID_GUESS = getUtf8Bytes("not_password"); 165 private static final byte[] GUESS_LOCKOUT = getUtf8Bytes("need_to_wait"); 166 private static final int TIMEOUT_MILLIS = 30 * 1000; 167 168 @Mock private Context mMockContext; 169 @Mock private RecoverySnapshotListenersStorage mMockListenersStorage; 170 @Mock private KeyguardManager mKeyguardManager; 171 @Mock private PlatformKeyManager mPlatformKeyManager; 172 @Mock private ApplicationKeyStorage mApplicationKeyStorage; 173 @Mock private CleanupManager mCleanupManager; 174 @Mock private ScheduledExecutorService mExecutorService; 175 @Mock private LockSettingsService mLockSettingsService; 176 @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; 177 178 private int mUserId; 179 private RecoverableKeyStoreDb mRecoverableKeyStoreDb; 180 private File mDatabaseFile; 181 private RecoverableKeyStoreManager mRecoverableKeyStoreManager; 182 private RecoverySessionStorage mRecoverySessionStorage; 183 private RecoverySnapshotStorage mRecoverySnapshotStorage; 184 private PlatformEncryptionKey mPlatformEncryptionKey; 185 private RemoteLockscreenValidationSessionStorage mRemoteLockscreenValidationSessionStorage; 186 187 @Before setUp()188 public void setUp() throws Exception { 189 MockitoAnnotations.initMocks(this); 190 191 Context context = InstrumentationRegistry.getTargetContext(); 192 mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME); 193 mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context); 194 195 mUserId = UserHandle.getCallingUserId(); 196 mRecoverySessionStorage = new RecoverySessionStorage(); 197 mRemoteLockscreenValidationSessionStorage = new RemoteLockscreenValidationSessionStorage(); 198 199 when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager); 200 when(mMockContext.getSystemServiceName(any())).thenReturn("test"); 201 when(mMockContext.getApplicationContext()).thenReturn(mMockContext); 202 when(mKeyguardManager.isDeviceSecure(TEST_USER_ID)).thenReturn(true); 203 204 mPlatformEncryptionKey = 205 new PlatformEncryptionKey(TEST_GENERATION_ID, generateAndroidKeyStoreKey()); 206 when(mPlatformKeyManager.getEncryptKey(anyInt())).thenReturn(mPlatformEncryptionKey); 207 208 mRecoverableKeyStoreManager = new RecoverableKeyStoreManager( 209 mMockContext, 210 mRecoverableKeyStoreDb, 211 mRecoverySessionStorage, 212 mExecutorService, 213 mRecoverySnapshotStorage, 214 mMockListenersStorage, 215 mPlatformKeyManager, 216 mApplicationKeyStorage, 217 mTestOnlyInsecureCertificateHelper, 218 mCleanupManager, 219 mRemoteLockscreenValidationSessionStorage); 220 when(mLockSettingsService.verifyCredential( 221 any(LockscreenCredential.class), anyInt(), anyInt())).thenAnswer(args -> { 222 LockscreenCredential argument = (LockscreenCredential) args.getArguments()[0]; 223 if (Arrays.equals(argument.getCredential(), VALID_GUESS)) { 224 return VerifyCredentialResponse.OK; 225 } else if (Arrays.equals(argument.getCredential(), INVALID_GUESS)) { 226 return VerifyCredentialResponse.ERROR; 227 } else return VerifyCredentialResponse.fromTimeout(TIMEOUT_MILLIS); 228 }); 229 } 230 231 @After tearDown()232 public void tearDown() { 233 if (mRemoteLockscreenValidationSessionStorage != null) { 234 mRemoteLockscreenValidationSessionStorage.finishSession(mUserId); 235 } 236 if (mRecoverableKeyStoreDb != null) { 237 mRecoverableKeyStoreDb.close(); 238 } 239 if (mDatabaseFile != null) { 240 mDatabaseFile.delete(); 241 } 242 } 243 244 @Test importKey_storesTheKey()245 public void importKey_storesTheKey() throws Exception { 246 int uid = Binder.getCallingUid(); 247 int userId = UserHandle.getCallingUserId(); 248 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES); 249 250 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial); 251 252 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); 253 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 254 } 255 256 @Test importKey_throwsIfInvalidLength()257 public void importKey_throwsIfInvalidLength() throws Exception { 258 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1); 259 try { 260 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial); 261 fail("should have thrown"); 262 } catch (ServiceSpecificException e) { 263 assertThat(e.getMessage()).contains("not contain 256 bits"); 264 } 265 } 266 267 @Test importKey_throwsIfNullKey()268 public void importKey_throwsIfNullKey() throws Exception { 269 try { 270 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null); 271 fail("should have thrown"); 272 } catch (NullPointerException e) { 273 assertThat(e.getMessage()).contains("is null"); 274 } 275 } 276 277 @Test importKeyWithMetadata_nullMetadata_storesTheKey()278 public void importKeyWithMetadata_nullMetadata_storesTheKey() throws Exception { 279 int uid = Binder.getCallingUid(); 280 int userId = UserHandle.getCallingUserId(); 281 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES); 282 283 mRecoverableKeyStoreManager.importKeyWithMetadata( 284 TEST_ALIAS, keyMaterial, KEY_METADATA_NULL); 285 286 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); 287 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 288 } 289 290 @Test importKeyWithMetadata_nonNullMetadata_storesTheKey()291 public void importKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception { 292 int uid = Binder.getCallingUid(); 293 int userId = UserHandle.getCallingUserId(); 294 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES); 295 296 mRecoverableKeyStoreManager.importKeyWithMetadata( 297 TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL); 298 299 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); 300 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 301 } 302 303 @Test importKeyWithMetadata_throwsIfInvalidLength()304 public void importKeyWithMetadata_throwsIfInvalidLength() throws Exception { 305 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1); 306 try { 307 mRecoverableKeyStoreManager.importKeyWithMetadata( 308 TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL); 309 fail("should have thrown"); 310 } catch (ServiceSpecificException e) { 311 assertThat(e.getMessage()).contains("not contain 256 bits"); 312 } 313 } 314 315 @Test importKeyWithMetadata_throwsIfNullKey()316 public void importKeyWithMetadata_throwsIfNullKey() throws Exception { 317 try { 318 mRecoverableKeyStoreManager.importKeyWithMetadata( 319 TEST_ALIAS, /*keyBytes=*/ null, KEY_METADATA_NON_NULL); 320 fail("should have thrown"); 321 } catch (NullPointerException e) { 322 assertThat(e.getMessage()).contains("is null"); 323 } 324 } 325 326 @Test generateKeyWithMetadata_nullMetadata_storesTheKey()327 public void generateKeyWithMetadata_nullMetadata_storesTheKey() throws Exception { 328 int uid = Binder.getCallingUid(); 329 int userId = UserHandle.getCallingUserId(); 330 331 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL); 332 333 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); 334 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 335 } 336 337 @Test generateKeyWithMetadata_nonNullMetadata_storesTheKey()338 public void generateKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception { 339 int uid = Binder.getCallingUid(); 340 int userId = UserHandle.getCallingUserId(); 341 342 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NON_NULL); 343 344 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); 345 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 346 } 347 348 @Test removeKey_removesAKey()349 public void removeKey_removesAKey() throws Exception { 350 int uid = Binder.getCallingUid(); 351 mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); 352 353 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); 354 355 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNull(); 356 } 357 358 @Test removeKey_updatesShouldCreateSnapshot()359 public void removeKey_updatesShouldCreateSnapshot() throws Exception { 360 int uid = Binder.getCallingUid(); 361 int userId = UserHandle.getCallingUserId(); 362 mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); 363 // Pretend that key was synced 364 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 365 366 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); 367 368 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 369 } 370 371 @Test removeKey_failureDoesNotUpdateShouldCreateSnapshot()372 public void removeKey_failureDoesNotUpdateShouldCreateSnapshot() throws Exception { 373 int uid = Binder.getCallingUid(); 374 int userId = UserHandle.getCallingUserId(); 375 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 376 // Key did not exist 377 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); 378 379 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 380 } 381 382 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 383 @Test initRecoveryService_succeedsWithCertFile()384 public void initRecoveryService_succeedsWithCertFile() throws Exception { 385 int uid = Binder.getCallingUid(); 386 int userId = UserHandle.getCallingUserId(); 387 long certSerial = 1000L; 388 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 389 390 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 391 TestData.getCertXmlWithSerial(certSerial)); 392 393 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 394 .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS); 395 396 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 397 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 398 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1); 399 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 400 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial); 401 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); 402 } 403 404 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 405 @Test initRecoveryService_updatesShouldCreatesnapshotOnCertUpdate()406 public void initRecoveryService_updatesShouldCreatesnapshotOnCertUpdate() throws Exception { 407 int uid = Binder.getCallingUid(); 408 int userId = UserHandle.getCallingUserId(); 409 long certSerial = 1000L; 410 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 411 412 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 413 TestData.getCertXmlWithSerial(certSerial)); 414 415 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 416 417 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 418 TestData.getCertXmlWithSerial(certSerial + 1)); 419 420 // Since there were no recoverable keys, new snapshot will not be created. 421 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 422 423 generateKeyAndSimulateSync(userId, uid, 10); 424 425 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 426 TestData.getCertXmlWithSerial(certSerial + 2)); 427 428 // Since there were a recoverable key, new serial number triggers snapshot creation 429 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 430 } 431 432 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 433 @Test initRecoveryService_triesToFilterRootAlias()434 public void initRecoveryService_triesToFilterRootAlias() throws Exception { 435 int uid = Binder.getCallingUid(); 436 int userId = UserHandle.getCallingUserId(); 437 long certSerial = 1000L; 438 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 439 440 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 441 TestData.getCertXmlWithSerial(certSerial)); 442 443 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 444 .getDefaultCertificateAliasIfEmpty(eq(ROOT_CERTIFICATE_ALIAS)); 445 446 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 447 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS)); 448 449 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid); 450 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS); 451 452 } 453 454 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 455 @Test initRecoveryService_usesProdCertificateForEmptyRootAlias()456 public void initRecoveryService_usesProdCertificateForEmptyRootAlias() throws Exception { 457 int uid = Binder.getCallingUid(); 458 int userId = UserHandle.getCallingUserId(); 459 long certSerial = 1000L; 460 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 461 462 mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ "", 463 TestData.getCertXmlWithSerial(certSerial)); 464 465 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 466 .getDefaultCertificateAliasIfEmpty(eq("")); 467 468 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 469 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS)); 470 471 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid); 472 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS); 473 } 474 475 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 476 @Test initRecoveryService_usesProdCertificateForNullRootAlias()477 public void initRecoveryService_usesProdCertificateForNullRootAlias() throws Exception { 478 int uid = Binder.getCallingUid(); 479 int userId = UserHandle.getCallingUserId(); 480 long certSerial = 1000L; 481 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 482 483 mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ null, 484 TestData.getCertXmlWithSerial(certSerial)); 485 486 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 487 .getDefaultCertificateAliasIfEmpty(null); 488 489 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 490 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS)); 491 492 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid); 493 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS); 494 } 495 496 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 497 @Test initRecoveryService_regeneratesCounterId()498 public void initRecoveryService_regeneratesCounterId() throws Exception { 499 int uid = Binder.getCallingUid(); 500 int userId = UserHandle.getCallingUserId(); 501 long certSerial = 1000L; 502 503 Long counterId0 = mRecoverableKeyStoreDb.getCounterId(userId, uid); 504 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 505 TestData.getCertXmlWithSerial(certSerial)); 506 Long counterId1 = mRecoverableKeyStoreDb.getCounterId(userId, uid); 507 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 508 TestData.getCertXmlWithSerial(certSerial + 1)); 509 Long counterId2 = mRecoverableKeyStoreDb.getCounterId(userId, uid); 510 511 assertThat(!counterId1.equals(counterId0) || !counterId2.equals(counterId1)).isTrue(); 512 } 513 514 @Test initRecoveryService_throwsIfInvalidCert()515 public void initRecoveryService_throwsIfInvalidCert() throws Exception { 516 byte[] modifiedCertXml = TestData.getCertXml(); 517 modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate 518 try { 519 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 520 modifiedCertXml); 521 fail("should have thrown"); 522 } catch (ServiceSpecificException e) { 523 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); 524 } 525 } 526 527 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 528 @Test initRecoveryService_updatesWithLargerSerial()529 public void initRecoveryService_updatesWithLargerSerial() throws Exception { 530 int uid = Binder.getCallingUid(); 531 int userId = UserHandle.getCallingUserId(); 532 long certSerial = 1000L; 533 534 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 535 TestData.getCertXmlWithSerial(certSerial)); 536 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 537 TestData.getCertXmlWithSerial(certSerial + 1)); 538 539 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 540 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1); 541 // There were no keys. 542 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 543 } 544 545 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 546 @Test initRecoveryService_throwsExceptionOnSmallerSerial()547 public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception { 548 long certSerial = 1000L; 549 550 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 551 TestData.getCertXmlWithSerial(certSerial)); 552 try { 553 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 554 TestData.getCertXmlWithSerial(certSerial - 1)); 555 fail(); 556 } catch (ServiceSpecificException e) { 557 assertThat(e.errorCode).isEqualTo(ERROR_DOWNGRADE_CERTIFICATE); 558 } 559 } 560 561 @Ignore("Causing breakages so ignoring to resolve, b/231667368") 562 @Test initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed()563 public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception { 564 int uid = Binder.getCallingUid(); 565 int userId = UserHandle.getCallingUserId(); 566 int certSerial = 3333; 567 568 String testRootCertAlias = TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS; 569 570 mRecoverableKeyStoreManager.initRecoveryService(testRootCertAlias, 571 TestData.getInsecureCertXmlBytesWithEndpoint1(certSerial)); 572 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 573 testRootCertAlias)).isEqualTo(certSerial); 574 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 575 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint1()); 576 577 mRecoverableKeyStoreManager.initRecoveryService(testRootCertAlias, 578 TestData.getInsecureCertXmlBytesWithEndpoint2(certSerial - 1)); 579 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 580 testRootCertAlias)).isEqualTo(certSerial - 1); 581 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 582 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2()); 583 } 584 585 @Ignore("Causing breakages so ignoring to resolve, b/231667368") 586 @Test initRecoveryService_updatesCertsIndependentlyForDifferentRoots()587 public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception { 588 int uid = Binder.getCallingUid(); 589 int userId = UserHandle.getCallingUserId(); 590 591 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 592 TestData.getCertXmlWithSerial(1111L)); 593 mRecoverableKeyStoreManager.initRecoveryService( 594 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS, 595 TestData.getInsecureCertXmlBytesWithEndpoint1(2222)); 596 597 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 598 ROOT_CERTIFICATE_ALIAS)).isEqualTo(1111L); 599 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 600 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isEqualTo(2222L); 601 602 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 603 ROOT_CERTIFICATE_ALIAS)).isEqualTo(TestData.CERT_PATH_1); 604 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 605 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isEqualTo( 606 TestData.getInsecureCertPathForEndpoint1()); 607 } 608 609 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 610 @Test initRecoveryService_ignoresTheSameSerial()611 public void initRecoveryService_ignoresTheSameSerial() throws Exception { 612 int uid = Binder.getCallingUid(); 613 int userId = UserHandle.getCallingUserId(); 614 long certSerial = 1000L; 615 616 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 617 TestData.getCertXmlWithSerial(certSerial)); 618 619 generateKeyAndSimulateSync(userId, uid, 10); 620 621 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, 622 TestData.getCertXmlWithSerial(certSerial)); 623 624 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 625 } 626 627 @Test initRecoveryService_throwsIfRawPublicKey()628 public void initRecoveryService_throwsIfRawPublicKey() throws Exception { 629 int uid = Binder.getCallingUid(); 630 int userId = UserHandle.getCallingUserId(); 631 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 632 633 try { 634 mRecoverableKeyStoreManager 635 .initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY); 636 fail("should have thrown"); 637 } catch (ServiceSpecificException e) { 638 assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT); 639 } 640 641 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 642 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 643 DEFAULT_ROOT_CERT_ALIAS)).isNull(); 644 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, 645 DEFAULT_ROOT_CERT_ALIAS)).isNull(); 646 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); 647 } 648 649 @Test initRecoveryService_throwsIfUnknownRootCertAlias()650 public void initRecoveryService_throwsIfUnknownRootCertAlias() throws Exception { 651 try { 652 mRecoverableKeyStoreManager.initRecoveryService( 653 "unknown-root-cert-alias", TestData.getCertXml()); 654 fail("should have thrown"); 655 } catch (ServiceSpecificException e) { 656 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); 657 } 658 } 659 660 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 661 @Test initRecoveryServiceWithSigFile_succeeds()662 public void initRecoveryServiceWithSigFile_succeeds() throws Exception { 663 int uid = Binder.getCallingUid(); 664 int userId = UserHandle.getCallingUserId(); 665 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 666 667 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 668 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml()); 669 670 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 671 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, 672 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1); 673 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); 674 } 675 676 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 677 @Test initRecoveryServiceWithSigFile_usesProdCertificateForNullRootAlias()678 public void initRecoveryServiceWithSigFile_usesProdCertificateForNullRootAlias() 679 throws Exception { 680 int uid = Binder.getCallingUid(); 681 int userId = UserHandle.getCallingUserId(); 682 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 683 684 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 685 /*rootCertificateAlias=*/null, TestData.getCertXml(), TestData.getSigXml()); 686 687 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 688 .getDefaultCertificateAliasIfEmpty(null); 689 690 verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) 691 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS)); 692 } 693 694 @Test initRecoveryServiceWithSigFile_throwsIfNullCertFile()695 public void initRecoveryServiceWithSigFile_throwsIfNullCertFile() throws Exception { 696 try { 697 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 698 ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null, 699 TestData.getSigXml()); 700 fail("should have thrown"); 701 } catch (NullPointerException e) { 702 assertThat(e.getMessage()).contains("is null"); 703 } 704 } 705 706 @Test initRecoveryServiceWithSigFile_throwsIfNullSigFile()707 public void initRecoveryServiceWithSigFile_throwsIfNullSigFile() throws Exception { 708 try { 709 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 710 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), 711 /*recoveryServiceSigFile=*/ null); 712 fail("should have thrown"); 713 } catch (NullPointerException e) { 714 assertThat(e.getMessage()).contains("is null"); 715 } 716 } 717 718 @Test initRecoveryServiceWithSigFile_throwsIfWrongSigFileFormat()719 public void initRecoveryServiceWithSigFile_throwsIfWrongSigFileFormat() throws Exception { 720 try { 721 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 722 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), 723 getUtf8Bytes("wrong-sig-file-format")); 724 fail("should have thrown"); 725 } catch (ServiceSpecificException e) { 726 assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT); 727 } 728 } 729 730 @Test initRecoveryServiceWithSigFile_throwsIfTestAliasUsedWithProdCert()731 public void initRecoveryServiceWithSigFile_throwsIfTestAliasUsedWithProdCert() 732 throws Exception { 733 try { 734 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 735 INSECURE_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml()); 736 fail("should have thrown"); 737 } catch (ServiceSpecificException e) { 738 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); 739 } 740 } 741 742 @Test initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature()743 public void initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature() throws Exception { 744 byte[] modifiedCertXml = TestData.getCertXml(); 745 modifiedCertXml[modifiedCertXml.length - 1] = 0; // Change the last new line char to a zero 746 try { 747 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile( 748 ROOT_CERTIFICATE_ALIAS, modifiedCertXml, TestData.getSigXml()); 749 fail("should have thrown"); 750 } catch (ServiceSpecificException e) { 751 assertThat(e.getMessage()).contains("is invalid"); 752 } 753 } 754 755 @Test startRecoverySession_checksPermissionFirst()756 public void startRecoverySession_checksPermissionFirst() throws Exception { 757 mRecoverableKeyStoreManager.startRecoverySession( 758 TEST_SESSION_ID, 759 TEST_PUBLIC_KEY, 760 TEST_VAULT_PARAMS, 761 TEST_VAULT_CHALLENGE, 762 ImmutableList.of(TEST_PROTECTION_PARAMS)); 763 764 verify(mMockContext, times(1)) 765 .enforceCallingOrSelfPermission( 766 eq(Manifest.permission.RECOVER_KEYSTORE), any()); 767 } 768 769 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 770 @Test startRecoverySessionWithCertPath_storesTheSessionInfo()771 public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception { 772 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( 773 TEST_SESSION_ID, 774 TEST_DEFAULT_ROOT_CERT_ALIAS, 775 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), 776 TEST_VAULT_PARAMS, 777 TEST_VAULT_CHALLENGE, 778 ImmutableList.of(TEST_PROTECTION_PARAMS)); 779 780 assertEquals(1, mRecoverySessionStorage.size()); 781 RecoverySessionStorage.Entry entry = 782 mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID); 783 assertArrayEquals(TEST_SECRET, entry.getLskfHash()); 784 assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length); 785 } 786 787 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 788 @Test startRecoverySessionWithCertPath_checksPermissionFirst()789 public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception { 790 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( 791 TEST_SESSION_ID, 792 TEST_DEFAULT_ROOT_CERT_ALIAS, 793 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), 794 TEST_VAULT_PARAMS, 795 TEST_VAULT_CHALLENGE, 796 ImmutableList.of(TEST_PROTECTION_PARAMS)); 797 798 verify(mMockContext, times(2)) 799 .enforceCallingOrSelfPermission( 800 eq(Manifest.permission.RECOVER_KEYSTORE), any()); 801 } 802 803 @Test startRecoverySession_storesTheSessionInfo()804 public void startRecoverySession_storesTheSessionInfo() throws Exception { 805 mRecoverableKeyStoreManager.startRecoverySession( 806 TEST_SESSION_ID, 807 TEST_PUBLIC_KEY, 808 TEST_VAULT_PARAMS, 809 TEST_VAULT_CHALLENGE, 810 ImmutableList.of(TEST_PROTECTION_PARAMS)); 811 812 assertEquals(1, mRecoverySessionStorage.size()); 813 RecoverySessionStorage.Entry entry = 814 mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID); 815 assertArrayEquals(TEST_SECRET, entry.getLskfHash()); 816 assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length); 817 } 818 819 @Test closeSession_closesASession()820 public void closeSession_closesASession() throws Exception { 821 mRecoverableKeyStoreManager.startRecoverySession( 822 TEST_SESSION_ID, 823 TEST_PUBLIC_KEY, 824 TEST_VAULT_PARAMS, 825 TEST_VAULT_CHALLENGE, 826 ImmutableList.of(TEST_PROTECTION_PARAMS)); 827 828 mRecoverableKeyStoreManager.closeSession(TEST_SESSION_ID); 829 830 assertEquals(0, mRecoverySessionStorage.size()); 831 } 832 833 @Test closeSession_doesNotCloseUnrelatedSessions()834 public void closeSession_doesNotCloseUnrelatedSessions() throws Exception { 835 mRecoverableKeyStoreManager.startRecoverySession( 836 TEST_SESSION_ID, 837 TEST_PUBLIC_KEY, 838 TEST_VAULT_PARAMS, 839 TEST_VAULT_CHALLENGE, 840 ImmutableList.of(TEST_PROTECTION_PARAMS)); 841 842 mRecoverableKeyStoreManager.closeSession("some random session"); 843 844 assertEquals(1, mRecoverySessionStorage.size()); 845 } 846 847 @Test closeSession_throwsIfNullSession()848 public void closeSession_throwsIfNullSession() throws Exception { 849 try { 850 mRecoverableKeyStoreManager.closeSession(/*sessionId=*/ null); 851 fail("should have thrown"); 852 } catch (NullPointerException e) { 853 assertThat(e.getMessage()).contains("invalid"); 854 } 855 } 856 857 @Test startRecoverySession_throwsIfBadNumberOfSecrets()858 public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception { 859 try { 860 mRecoverableKeyStoreManager.startRecoverySession( 861 TEST_SESSION_ID, 862 TEST_PUBLIC_KEY, 863 TEST_VAULT_PARAMS, 864 TEST_VAULT_CHALLENGE, 865 ImmutableList.of()); 866 fail("should have thrown"); 867 } catch (UnsupportedOperationException e) { 868 assertThat(e.getMessage()).startsWith( 869 "Only a single KeyChainProtectionParams is supported"); 870 } 871 } 872 873 @Test startRecoverySession_throwsIfPublicKeysMismatch()874 public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception { 875 byte[] vaultParams = TEST_VAULT_PARAMS.clone(); 876 vaultParams[1] ^= (byte) 1; // Flip 1 bit 877 878 try { 879 mRecoverableKeyStoreManager.startRecoverySession( 880 TEST_SESSION_ID, 881 TEST_PUBLIC_KEY, 882 vaultParams, 883 TEST_VAULT_CHALLENGE, 884 ImmutableList.of(TEST_PROTECTION_PARAMS)); 885 fail("should have thrown"); 886 } catch (ServiceSpecificException e) { 887 assertThat(e.getMessage()).contains("do not match"); 888 } 889 } 890 891 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 892 @Test startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets()893 public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception { 894 try { 895 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( 896 TEST_SESSION_ID, 897 TEST_DEFAULT_ROOT_CERT_ALIAS, 898 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), 899 TEST_VAULT_PARAMS, 900 TEST_VAULT_CHALLENGE, 901 ImmutableList.of()); 902 fail("should have thrown"); 903 } catch (UnsupportedOperationException e) { 904 assertThat(e.getMessage()).startsWith( 905 "Only a single KeyChainProtectionParams is supported"); 906 } 907 } 908 909 @Ignore("Causing breakages so ignoring to resolve, b/281583079") 910 @Test startRecoverySessionWithCertPath_throwsIfPublicKeysMismatch()911 public void startRecoverySessionWithCertPath_throwsIfPublicKeysMismatch() throws Exception { 912 byte[] vaultParams = TEST_VAULT_PARAMS.clone(); 913 vaultParams[1] ^= (byte) 1; // Flip 1 bit 914 try { 915 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( 916 TEST_SESSION_ID, 917 TEST_DEFAULT_ROOT_CERT_ALIAS, 918 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), 919 vaultParams, 920 TEST_VAULT_CHALLENGE, 921 ImmutableList.of(TEST_PROTECTION_PARAMS)); 922 fail("should have thrown"); 923 } catch (ServiceSpecificException e) { 924 assertThat(e.getMessage()).contains("do not match"); 925 } 926 } 927 928 @Test startRecoverySessionWithCertPath_throwsIfEmptyCertPath()929 public void startRecoverySessionWithCertPath_throwsIfEmptyCertPath() throws Exception { 930 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 931 CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>()); 932 try { 933 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( 934 TEST_SESSION_ID, 935 TEST_DEFAULT_ROOT_CERT_ALIAS, 936 RecoveryCertPath.createRecoveryCertPath(emptyCertPath), 937 TEST_VAULT_PARAMS, 938 TEST_VAULT_CHALLENGE, 939 ImmutableList.of(TEST_PROTECTION_PARAMS)); 940 fail("should have thrown"); 941 } catch (ServiceSpecificException e) { 942 assertThat(e.getMessage()).contains("empty"); 943 } 944 } 945 946 @Test startRecoverySessionWithCertPath_throwsIfInvalidCertPath()947 public void startRecoverySessionWithCertPath_throwsIfInvalidCertPath() throws Exception { 948 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 949 CertPath shortCertPath = certFactory.generateCertPath( 950 TestData.CERT_PATH_1.getCertificates() 951 .subList(0, TestData.CERT_PATH_1.getCertificates().size() - 1)); 952 try { 953 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( 954 TEST_SESSION_ID, 955 TEST_DEFAULT_ROOT_CERT_ALIAS, 956 RecoveryCertPath.createRecoveryCertPath(shortCertPath), 957 TEST_VAULT_PARAMS, 958 TEST_VAULT_CHALLENGE, 959 ImmutableList.of(TEST_PROTECTION_PARAMS)); 960 fail("should have thrown"); 961 } catch (ServiceSpecificException e) { 962 // expected 963 } 964 } 965 966 @Test recoverKeyChainSnapshot_throwsIfNoSessionIsPresent()967 public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception { 968 try { 969 WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder() 970 .setAlias(TEST_ALIAS) 971 .setEncryptedKeyMaterial(randomBytes(32)) 972 .build(); 973 mRecoverableKeyStoreManager.recoverKeyChainSnapshot( 974 TEST_SESSION_ID, 975 /*recoveryKeyBlob=*/ randomBytes(32), 976 /*applicationKeys=*/ ImmutableList.of(applicationKey)); 977 fail("should have thrown"); 978 } catch (ServiceSpecificException e) { 979 // expected 980 } 981 } 982 983 @Test recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted()984 public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception { 985 mRecoverableKeyStoreManager.startRecoverySession( 986 TEST_SESSION_ID, 987 TEST_PUBLIC_KEY, 988 TEST_VAULT_PARAMS, 989 TEST_VAULT_CHALLENGE, 990 ImmutableList.of(TEST_PROTECTION_PARAMS)); 991 992 try { 993 mRecoverableKeyStoreManager.recoverKeyChainSnapshot( 994 TEST_SESSION_ID, 995 /*encryptedRecoveryKey=*/ randomBytes(60), 996 /*applicationKeys=*/ ImmutableList.of()); 997 fail("should have thrown"); 998 } catch (ServiceSpecificException e) { 999 assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key"); 1000 } 1001 } 1002 1003 @Test recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys()1004 public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() 1005 throws Exception { 1006 mRecoverableKeyStoreManager.startRecoverySession( 1007 TEST_SESSION_ID, 1008 TEST_PUBLIC_KEY, 1009 TEST_VAULT_PARAMS, 1010 TEST_VAULT_CHALLENGE, 1011 ImmutableList.of(TEST_PROTECTION_PARAMS)); 1012 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) 1013 .getKeyClaimant(); 1014 SecretKey recoveryKey = randomRecoveryKey(); 1015 byte[] encryptedClaimResponse = encryptClaimResponse( 1016 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); 1017 WrappedApplicationKey badApplicationKey = new WrappedApplicationKey.Builder() 1018 .setAlias(TEST_ALIAS) 1019 .setEncryptedKeyMaterial( 1020 encryptedApplicationKey(randomRecoveryKey(), randomBytes(32))) 1021 .build(); 1022 try { 1023 mRecoverableKeyStoreManager.recoverKeyChainSnapshot( 1024 TEST_SESSION_ID, 1025 /*encryptedRecoveryKey=*/ encryptedClaimResponse, 1026 /*applicationKeys=*/ ImmutableList.of(badApplicationKey)); 1027 fail("should have thrown"); 1028 } catch (ServiceSpecificException e) { 1029 assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys"); 1030 } 1031 } 1032 1033 @Test recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted()1034 public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted() 1035 throws Exception { 1036 mRecoverableKeyStoreManager.startRecoverySession( 1037 TEST_SESSION_ID, 1038 TEST_PUBLIC_KEY, 1039 TEST_VAULT_PARAMS, 1040 TEST_VAULT_CHALLENGE, 1041 ImmutableList.of(TEST_PROTECTION_PARAMS)); 1042 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) 1043 .getKeyClaimant(); 1044 SecretKey recoveryKey = randomRecoveryKey(); 1045 byte[] encryptedClaimResponse = encryptClaimResponse( 1046 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); 1047 1048 mRecoverableKeyStoreManager.recoverKeyChainSnapshot( 1049 TEST_SESSION_ID, 1050 /*encryptedRecoveryKey=*/ encryptedClaimResponse, 1051 /*applicationKeys=*/ ImmutableList.of()); 1052 } 1053 1054 @Test recoverKeyChainSnapshot_returnsDecryptedKeys()1055 public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception { 1056 mRecoverableKeyStoreManager.startRecoverySession( 1057 TEST_SESSION_ID, 1058 TEST_PUBLIC_KEY, 1059 TEST_VAULT_PARAMS, 1060 TEST_VAULT_CHALLENGE, 1061 ImmutableList.of(TEST_PROTECTION_PARAMS)); 1062 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) 1063 .getKeyClaimant(); 1064 SecretKey recoveryKey = randomRecoveryKey(); 1065 byte[] encryptedClaimResponse = encryptClaimResponse( 1066 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); 1067 byte[] applicationKeyBytes = randomBytes(32); 1068 WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder() 1069 .setAlias(TEST_ALIAS) 1070 .setEncryptedKeyMaterial( 1071 encryptedApplicationKey(recoveryKey, applicationKeyBytes)) 1072 .build(); 1073 1074 Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( 1075 TEST_SESSION_ID, 1076 encryptedClaimResponse, 1077 ImmutableList.of(applicationKey)); 1078 1079 assertThat(recoveredKeys).hasSize(1); 1080 assertThat(recoveredKeys).containsKey(TEST_ALIAS); 1081 } 1082 1083 @Test recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails()1084 public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() 1085 throws Exception { 1086 mRecoverableKeyStoreManager.startRecoverySession( 1087 TEST_SESSION_ID, 1088 TEST_PUBLIC_KEY, 1089 TEST_VAULT_PARAMS, 1090 TEST_VAULT_CHALLENGE, 1091 ImmutableList.of(TEST_PROTECTION_PARAMS)); 1092 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) 1093 .getKeyClaimant(); 1094 SecretKey recoveryKey = randomRecoveryKey(); 1095 byte[] encryptedClaimResponse = encryptClaimResponse( 1096 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); 1097 1098 byte[] applicationKeyBytes1 = randomBytes(32); 1099 byte[] applicationKeyBytes2 = randomBytes(32); 1100 WrappedApplicationKey applicationKey1 = new WrappedApplicationKey.Builder() 1101 .setAlias(TEST_ALIAS) 1102 // Use a different recovery key here, so the decryption will fail 1103 .setEncryptedKeyMaterial( 1104 encryptedApplicationKey(randomRecoveryKey(), applicationKeyBytes1)) 1105 .build(); 1106 WrappedApplicationKey applicationKey2 = new WrappedApplicationKey.Builder() 1107 .setAlias(TEST_ALIAS2) 1108 .setEncryptedKeyMaterial( 1109 encryptedApplicationKey(recoveryKey, applicationKeyBytes2)) 1110 .build(); 1111 1112 Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( 1113 TEST_SESSION_ID, 1114 encryptedClaimResponse, 1115 ImmutableList.of(applicationKey1, applicationKey2)); 1116 1117 assertThat(recoveredKeys).hasSize(1); 1118 assertThat(recoveredKeys).containsKey(TEST_ALIAS2); 1119 } 1120 1121 @Test setSnapshotCreatedPendingIntent()1122 public void setSnapshotCreatedPendingIntent() throws Exception { 1123 int uid = Binder.getCallingUid(); 1124 PendingIntent intent = PendingIntent.getBroadcast( 1125 InstrumentationRegistry.getTargetContext(), /*requestCode=*/1, 1126 new Intent() 1127 .setPackage(InstrumentationRegistry.getTargetContext().getPackageName()), 1128 /*flags=*/ PendingIntent.FLAG_MUTABLE); 1129 mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent); 1130 verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class)); 1131 } 1132 1133 @Test setServerParams_updatesServerParams()1134 public void setServerParams_updatesServerParams() throws Exception { 1135 int uid = Binder.getCallingUid(); 1136 int userId = UserHandle.getCallingUserId(); 1137 byte[] serverParams = new byte[] { 1 }; 1138 1139 mRecoverableKeyStoreManager.setServerParams(serverParams); 1140 1141 assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(serverParams); 1142 } 1143 1144 @Test setServerParams_doesNotSetSnapshotPendingIfInitializing()1145 public void setServerParams_doesNotSetSnapshotPendingIfInitializing() throws Exception { 1146 int uid = Binder.getCallingUid(); 1147 int userId = UserHandle.getCallingUserId(); 1148 byte[] serverParams = new byte[] { 1 }; 1149 1150 mRecoverableKeyStoreManager.setServerParams(serverParams); 1151 1152 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 1153 } 1154 1155 @Test setServerParams_doesNotSetSnapshotPendingIfSettingSameValue()1156 public void setServerParams_doesNotSetSnapshotPendingIfSettingSameValue() throws Exception { 1157 int uid = Binder.getCallingUid(); 1158 int userId = UserHandle.getCallingUserId(); 1159 byte[] serverParams = new byte[] { 1 }; 1160 1161 mRecoverableKeyStoreManager.setServerParams(serverParams); 1162 1163 generateKeyAndSimulateSync(userId, uid, 10); 1164 1165 mRecoverableKeyStoreManager.setServerParams(serverParams); 1166 1167 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 1168 } 1169 1170 @Test setServerParams_setsSnapshotPendingIfUpdatingValue()1171 public void setServerParams_setsSnapshotPendingIfUpdatingValue() throws Exception { 1172 int uid = Binder.getCallingUid(); 1173 int userId = UserHandle.getCallingUserId(); 1174 1175 mRecoverableKeyStoreManager.setServerParams(new byte[] { 1 }); 1176 1177 generateKeyAndSimulateSync(userId, uid, 10); 1178 1179 mRecoverableKeyStoreManager.setServerParams(new byte[] { 2 }); 1180 1181 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 1182 } 1183 1184 @Test setRecoverySecretTypes_updatesSecretTypes()1185 public void setRecoverySecretTypes_updatesSecretTypes() throws Exception { 1186 int[] types1 = new int[]{11, 2000}; 1187 int[] types2 = new int[]{1, 2, 3}; 1188 int[] types3 = new int[]{}; 1189 1190 mRecoverableKeyStoreManager.setRecoverySecretTypes(types1); 1191 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( 1192 types1); 1193 1194 mRecoverableKeyStoreManager.setRecoverySecretTypes(types2); 1195 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( 1196 types2); 1197 1198 mRecoverableKeyStoreManager.setRecoverySecretTypes(types3); 1199 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( 1200 types3); 1201 } 1202 1203 @Test setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting()1204 public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception { 1205 int uid = Binder.getCallingUid(); 1206 int userId = UserHandle.getCallingUserId(); 1207 int[] secretTypes = new int[] { 101 }; 1208 1209 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes); 1210 1211 // There were no keys. 1212 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 1213 } 1214 1215 @Test setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()1216 public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue() 1217 throws Exception { 1218 int uid = Binder.getCallingUid(); 1219 int userId = UserHandle.getCallingUserId(); 1220 int[] secretTypes = new int[] { 101 }; 1221 1222 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes); 1223 1224 generateKeyAndSimulateSync(userId, uid, 10); 1225 1226 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes); 1227 1228 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 1229 } 1230 1231 @Test setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue()1232 public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception { 1233 int uid = Binder.getCallingUid(); 1234 int userId = UserHandle.getCallingUserId(); 1235 1236 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 }); 1237 1238 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); 1239 1240 generateKeyAndSimulateSync(userId, uid, 10); 1241 1242 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 }); 1243 1244 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 1245 } 1246 1247 @Test setRecoverySecretTypes_throwsIfNullTypes()1248 public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception { 1249 try { 1250 mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null); 1251 fail("should have thrown"); 1252 } catch (NullPointerException e) { 1253 assertThat(e.getMessage()).contains("is null"); 1254 } 1255 } 1256 1257 @Test setRecoverySecretTypes_updatesShouldCreateSnapshot()1258 public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception { 1259 int uid = Binder.getCallingUid(); 1260 int userId = UserHandle.getCallingUserId(); 1261 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 }); 1262 1263 generateKeyAndSimulateSync(userId, uid, 10); 1264 1265 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 }); 1266 1267 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); 1268 } 1269 1270 @Test setRecoveryStatus()1271 public void setRecoveryStatus() throws Exception { 1272 int userId = UserHandle.getCallingUserId(); 1273 int uid = Binder.getCallingUid(); 1274 int status = 100; 1275 int status2 = 200; 1276 String alias = "key1"; 1277 byte[] keyMetadata = null; 1278 1279 WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID, 1280 status); 1281 mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); 1282 Map<String, Integer> statuses = 1283 mRecoverableKeyStoreManager.getRecoveryStatus(); 1284 assertThat(statuses).hasSize(1); 1285 assertThat(statuses).containsEntry(alias, status); 1286 1287 mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2); 1288 statuses = mRecoverableKeyStoreManager.getRecoveryStatus(); 1289 assertThat(statuses).hasSize(1); 1290 assertThat(statuses).containsEntry(alias, status2); // updated 1291 } 1292 1293 @Test setRecoveryStatus_throwsIfNullAlias()1294 public void setRecoveryStatus_throwsIfNullAlias() throws Exception { 1295 try { 1296 mRecoverableKeyStoreManager.setRecoveryStatus(/*alias=*/ null, /*status=*/ 100); 1297 fail("should have thrown"); 1298 } catch (NullPointerException e) { 1299 assertThat(e.getMessage()).contains("is null"); 1300 } 1301 } 1302 1303 @Test lockScreenSecretAvailable_syncsKeysForUser()1304 public void lockScreenSecretAvailable_syncsKeysForUser() throws Exception { 1305 mRecoverableKeyStoreManager.lockScreenSecretAvailable( 1306 LockPatternUtils.CREDENTIAL_TYPE_PATTERN, "password".getBytes(), 11); 1307 1308 verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any()); 1309 } 1310 1311 @Test lockScreenSecretChanged_syncsKeysForUser()1312 public void lockScreenSecretChanged_syncsKeysForUser() throws Exception { 1313 mRecoverableKeyStoreManager.lockScreenSecretChanged( 1314 LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 1315 "password".getBytes(), 1316 11); 1317 1318 verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any()); 1319 } 1320 1321 @Test startRemoteLockscreenValidation_credentialsNotSet()1322 public void startRemoteLockscreenValidation_credentialsNotSet() throws Exception { 1323 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1324 LockPatternUtils.CREDENTIAL_TYPE_NONE); 1325 try { 1326 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1327 fail("should have thrown"); 1328 } catch (IllegalStateException e) { 1329 assertThat(e.getMessage()).contains("not set"); 1330 } 1331 verify(mLockSettingsService).getCredentialType(mUserId); 1332 } 1333 @Test startRemoteLockscreenValidation_checksPermission()1334 public void startRemoteLockscreenValidation_checksPermission() throws Exception { 1335 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1336 LockPatternUtils.CREDENTIAL_TYPE_PIN); 1337 1338 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1339 1340 verify(mMockContext, times(1)) 1341 .enforceCallingOrSelfPermission( 1342 eq(Manifest.permission.CHECK_REMOTE_LOCKSCREEN), any()); 1343 mRemoteLockscreenValidationSessionStorage.finishSession(mUserId); 1344 } 1345 @Test startRemoteLockscreenValidation_returnsCredentailsType()1346 public void startRemoteLockscreenValidation_returnsCredentailsType() throws Exception { 1347 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1348 LockPatternUtils.CREDENTIAL_TYPE_PIN); 1349 1350 RemoteLockscreenValidationSession request = 1351 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1352 1353 assertThat(request.getLockType()).isEqualTo(KeyguardManager.PIN); 1354 assertThat(request.getRemainingAttempts()).isEqualTo(5); 1355 verify(mLockSettingsService).getCredentialType(anyInt()); 1356 } 1357 @Test startRemoteLockscreenValidation_returnsRemainingAttempts()1358 public void startRemoteLockscreenValidation_returnsRemainingAttempts() throws Exception { 1359 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1360 LockPatternUtils.CREDENTIAL_TYPE_PATTERN); 1361 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3); 1362 1363 RemoteLockscreenValidationSession request = 1364 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1365 1366 assertThat(request.getLockType()).isEqualTo(KeyguardManager.PATTERN); 1367 assertThat(request.getRemainingAttempts()).isEqualTo(2); 1368 } 1369 @Test startRemoteLockscreenValidation_password()1370 public void startRemoteLockscreenValidation_password() throws Exception { 1371 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1372 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1373 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 7); 1374 1375 RemoteLockscreenValidationSession request = 1376 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1377 1378 assertThat(request.getRemainingAttempts()).isEqualTo(0); 1379 assertThat(request.getLockType()).isEqualTo(KeyguardManager.PASSWORD); 1380 } 1381 @Test validateRemoteLockscreen_noActiveSession()1382 public void validateRemoteLockscreen_noActiveSession() throws Exception { 1383 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1384 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1385 1386 RemoteLockscreenValidationResult result = 1387 mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS, 1388 mLockSettingsService); 1389 1390 assertThat(result.getResultCode()).isEqualTo( 1391 RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED); 1392 } 1393 @Test validateRemoteLockscreen_decryptionError()1394 public void validateRemoteLockscreen_decryptionError() throws Exception { 1395 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1396 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1397 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4); 1398 1399 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1400 1401 try { 1402 mRecoverableKeyStoreManager.validateRemoteLockscreen( 1403 new byte[] {1, 2, 3}, 1404 mLockSettingsService); 1405 fail("should have thrown"); 1406 } catch (IllegalStateException e) { 1407 // Decryption error 1408 } 1409 } 1410 @Test validateRemoteLockscreen_zeroRemainingAttempts()1411 public void validateRemoteLockscreen_zeroRemainingAttempts() throws Exception { 1412 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1413 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1414 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 5); 1415 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1416 1417 RemoteLockscreenValidationResult result = 1418 mRecoverableKeyStoreManager.validateRemoteLockscreen( 1419 encryptCredentialsForNewSession(VALID_GUESS), 1420 mLockSettingsService); 1421 1422 assertThat(result.getResultCode()).isEqualTo( 1423 RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS); 1424 } 1425 @Test validateRemoteLockscreen_guessValid()1426 public void validateRemoteLockscreen_guessValid() throws Exception { 1427 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1428 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1429 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4); 1430 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1431 1432 RemoteLockscreenValidationResult result = 1433 mRecoverableKeyStoreManager.validateRemoteLockscreen( 1434 encryptCredentialsForNewSession(VALID_GUESS), 1435 mLockSettingsService); 1436 1437 assertThat(result.getResultCode()).isEqualTo( 1438 RemoteLockscreenValidationResult.RESULT_GUESS_VALID); 1439 // Valid guess resets counter 1440 assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId)).isEqualTo(0); 1441 } 1442 @Test validateRemoteLockscreen_timeout()1443 public void validateRemoteLockscreen_timeout() throws Exception { 1444 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1445 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1446 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4); 1447 1448 RemoteLockscreenValidationResult result = 1449 mRecoverableKeyStoreManager.validateRemoteLockscreen( 1450 encryptCredentialsForNewSession(GUESS_LOCKOUT), 1451 mLockSettingsService); 1452 1453 assertThat(result.getResultCode()).isEqualTo( 1454 RemoteLockscreenValidationResult.RESULT_LOCKOUT); 1455 assertThat(result.getTimeoutMillis()).isEqualTo((long) TIMEOUT_MILLIS); 1456 // Counter was not changed 1457 assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId)).isEqualTo(4); 1458 } 1459 @Test validateRemoteLockscreen_guessInvalid()1460 public void validateRemoteLockscreen_guessInvalid() throws Exception { 1461 when(mLockSettingsService.getCredentialType(anyInt())).thenReturn( 1462 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 1463 mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4); 1464 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1465 1466 RemoteLockscreenValidationResult result = 1467 mRecoverableKeyStoreManager.validateRemoteLockscreen( 1468 encryptCredentialsForNewSession(INVALID_GUESS), 1469 mLockSettingsService); 1470 1471 assertThat(result.getResultCode()).isEqualTo( 1472 RemoteLockscreenValidationResult.RESULT_GUESS_INVALID); 1473 assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId)).isEqualTo(5); 1474 } 1475 encryptCredentialsForNewSession(byte[] credentials)1476 private byte[] encryptCredentialsForNewSession(byte[] credentials) throws Exception { 1477 RemoteLockscreenValidationSession request = 1478 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService); 1479 PublicKey publicKey = SecureBox.decodePublicKey(request.getSourcePublicKey()); 1480 return SecureBox.encrypt( 1481 publicKey, 1482 /* sharedSecret= */ null, 1483 LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER, 1484 credentials); 1485 } 1486 encryptedApplicationKey( SecretKey recoveryKey, byte[] applicationKey)1487 private static byte[] encryptedApplicationKey( 1488 SecretKey recoveryKey, byte[] applicationKey) throws Exception { 1489 return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of( 1490 TEST_ALIAS, 1491 Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null) 1492 )).get(TEST_ALIAS); 1493 } 1494 encryptClaimResponse( byte[] keyClaimant, byte[] lskfHash, byte[] vaultParams, SecretKey recoveryKey)1495 private static byte[] encryptClaimResponse( 1496 byte[] keyClaimant, 1497 byte[] lskfHash, 1498 byte[] vaultParams, 1499 SecretKey recoveryKey) throws Exception { 1500 byte[] locallyEncryptedRecoveryKey = KeySyncUtils.locallyEncryptRecoveryKey( 1501 lskfHash, recoveryKey); 1502 return SecureBox.encrypt( 1503 /*theirPublicKey=*/ null, 1504 /*sharedSecret=*/ keyClaimant, 1505 /*header=*/ ArrayUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams), 1506 /*payload=*/ locallyEncryptedRecoveryKey); 1507 } 1508 randomRecoveryKey()1509 private static SecretKey randomRecoveryKey() { 1510 return new SecretKeySpec(randomBytes(32), "AES"); 1511 } 1512 getUtf8Bytes(String s)1513 private static byte[] getUtf8Bytes(String s) { 1514 return s.getBytes(StandardCharsets.UTF_8); 1515 } 1516 randomBytes(int n)1517 private static byte[] randomBytes(int n) { 1518 byte[] bytes = new byte[n]; 1519 new Random().nextBytes(bytes); 1520 return bytes; 1521 } 1522 generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion)1523 private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion) 1524 throws Exception{ 1525 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL); 1526 // Simulate key sync. 1527 mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion); 1528 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); 1529 } 1530 generateAndroidKeyStoreKey()1531 private SecretKey generateAndroidKeyStoreKey() throws Exception { 1532 KeyGenerator keyGenerator = KeyGenerator.getInstance( 1533 KEY_ALGORITHM, 1534 ANDROID_KEY_STORE_PROVIDER); 1535 keyGenerator.init(new KeyGenParameterSpec.Builder( 1536 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 1537 .setBlockModes(KeyProperties.BLOCK_MODE_GCM) 1538 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 1539 .build()); 1540 return keyGenerator.generateKey(); 1541 } 1542 } 1543