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