blob: 0e5b7b366357e81fc3c955457f90f285f72c2aee [file] [log] [blame]
Robert Berry4a534ec2017-12-21 15:44:02 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.locksettings.recoverablekeystore;
18
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080019import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080020import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD;
21import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PATTERN;
22import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PIN;
Robert Berry4a534ec2017-12-21 15:44:02 +000023import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
24import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
25
Dmitry Dementyev77183ef2018-01-05 15:46:00 -080026import static com.google.common.truth.Truth.assertThat;
27
Robert Berry4a534ec2017-12-21 15:44:02 +000028import static org.junit.Assert.assertArrayEquals;
29import static org.junit.Assert.assertEquals;
30import static org.junit.Assert.assertFalse;
Robert Berry2fd4b592018-03-15 15:28:05 +000031import static org.junit.Assert.assertNotNull;
Robert Berrybd086f12017-12-27 13:29:39 +000032import static org.junit.Assert.assertNull;
Robert Berry4a534ec2017-12-21 15:44:02 +000033import static org.junit.Assert.assertTrue;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070034
35import static org.mockito.ArgumentMatchers.any;
36import static org.mockito.ArgumentMatchers.anyInt;
37import static org.mockito.ArgumentMatchers.eq;
38import static org.mockito.Mockito.atLeast;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -080039import static org.mockito.Mockito.never;
Robert Berry91044042017-12-27 12:05:58 +000040import static org.mockito.Mockito.verify;
Robert Berryf0a4bea2017-12-22 13:17:32 +000041import static org.mockito.Mockito.when;
Robert Berry4a534ec2017-12-21 15:44:02 +000042
Robert Berryf0a4bea2017-12-22 13:17:32 +000043import android.content.Context;
Robert Berry56588372018-03-29 12:07:17 +010044import android.os.FileUtils;
Robert Berryf0a4bea2017-12-22 13:17:32 +000045import android.security.keystore.AndroidKeyStoreSecretKey;
46import android.security.keystore.KeyGenParameterSpec;
47import android.security.keystore.KeyProperties;
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080048import android.security.keystore.recovery.KeyChainSnapshot;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070049import android.security.keystore.recovery.KeyDerivationParams;
Aseem Kumar3326da52018-03-12 18:05:16 -070050import android.security.keystore.recovery.RecoveryController;
Bo Zhu76973432018-04-03 00:37:51 -070051import android.security.keystore.recovery.TrustedRootCertificates;
Robert Berry81ee34b2018-01-23 11:59:59 +000052import android.security.keystore.recovery.WrappedApplicationKey;
Robert Berryf0a4bea2017-12-22 13:17:32 +000053import android.support.test.InstrumentationRegistry;
Robert Berry4a534ec2017-12-21 15:44:02 +000054import android.support.test.filters.SmallTest;
55import android.support.test.runner.AndroidJUnit4;
56
Robert Berryf0a4bea2017-12-22 13:17:32 +000057import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
Robert Berrybd086f12017-12-27 13:29:39 +000058import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
Robert Berryf0a4bea2017-12-22 13:17:32 +000059
60import org.junit.After;
61import org.junit.Before;
Robert Berry4a534ec2017-12-21 15:44:02 +000062import org.junit.Test;
63import org.junit.runner.RunWith;
Robert Berryf0a4bea2017-12-22 13:17:32 +000064import org.mockito.Mock;
65import org.mockito.MockitoAnnotations;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070066import org.mockito.Spy;
Robert Berry4a534ec2017-12-21 15:44:02 +000067
Robert Berryf0a4bea2017-12-22 13:17:32 +000068import java.io.File;
Robert Berry4a534ec2017-12-21 15:44:02 +000069import java.nio.charset.StandardCharsets;
70import java.util.Arrays;
Robert Berrybd086f12017-12-27 13:29:39 +000071import java.util.List;
Robert Berry4a534ec2017-12-21 15:44:02 +000072import java.util.Random;
73
Robert Berryf0a4bea2017-12-22 13:17:32 +000074import javax.crypto.KeyGenerator;
75import javax.crypto.SecretKey;
76
Robert Berry4a534ec2017-12-21 15:44:02 +000077@SmallTest
78@RunWith(AndroidJUnit4.class)
79public class KeySyncTaskTest {
Robert Berry56588372018-03-29 12:07:17 +010080
81 private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
82
Robert Berryf0a4bea2017-12-22 13:17:32 +000083 private static final String KEY_ALGORITHM = "AES";
84 private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070085 private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
Robert Berryf0a4bea2017-12-22 13:17:32 +000086 private static final String WRAPPING_KEY_ALIAS = "KeySyncTaskTest/WrappingKey";
87 private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
88 private static final int TEST_USER_ID = 1000;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -080089 private static final int TEST_RECOVERY_AGENT_UID = 10009;
90 private static final int TEST_RECOVERY_AGENT_UID2 = 10010;
Bo Zhu4ff2b3f2018-01-17 17:34:26 -080091 private static final byte[] TEST_VAULT_HANDLE =
Bo Zhu31ccba12018-01-18 11:53:57 -080092 new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
Robert Berryf0a4bea2017-12-22 13:17:32 +000093 private static final String TEST_APP_KEY_ALIAS = "rcleaver";
94 private static final int TEST_GENERATION_ID = 2;
95 private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PASSWORD;
96 private static final String TEST_CREDENTIAL = "password1234";
97 private static final byte[] THM_ENCRYPTED_RECOVERY_KEY_HEADER =
98 "V1 THM_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
99
Robert Berryf0a4bea2017-12-22 13:17:32 +0000100 @Mock private PlatformKeyManager mPlatformKeyManager;
Robert Berry91044042017-12-27 12:05:58 +0000101 @Mock private RecoverySnapshotListenersStorage mSnapshotListenersStorage;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700102 @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
Bo Zhu76973432018-04-03 00:37:51 -0700103 @Spy private MockScrypt mMockScrypt;
Robert Berryf0a4bea2017-12-22 13:17:32 +0000104
Robert Berrybd086f12017-12-27 13:29:39 +0000105 private RecoverySnapshotStorage mRecoverySnapshotStorage;
Robert Berryf0a4bea2017-12-22 13:17:32 +0000106 private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
107 private File mDatabaseFile;
Robert Berryf0a4bea2017-12-22 13:17:32 +0000108 private AndroidKeyStoreSecretKey mWrappingKey;
109 private PlatformEncryptionKey mEncryptKey;
110
111 private KeySyncTask mKeySyncTask;
112
113 @Before
114 public void setUp() throws Exception {
115 MockitoAnnotations.initMocks(this);
116
117 Context context = InstrumentationRegistry.getTargetContext();
118 mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
119 mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
Robert Berryf0a4bea2017-12-22 13:17:32 +0000120
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800121 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
122 new int[] {TYPE_LOCKSCREEN});
123 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
124 new int[] {TYPE_LOCKSCREEN});
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700125
126 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
127 TEST_ROOT_CERT_ALIAS);
128 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
129 TEST_ROOT_CERT_ALIAS);
Robert Berry56588372018-03-29 12:07:17 +0100130 mRecoverySnapshotStorage = new RecoverySnapshotStorage(context.getFilesDir());
Robert Berrybd086f12017-12-27 13:29:39 +0000131
Robert Berryf0a4bea2017-12-22 13:17:32 +0000132 mKeySyncTask = new KeySyncTask(
133 mRecoverableKeyStoreDb,
Robert Berrybd086f12017-12-27 13:29:39 +0000134 mRecoverySnapshotStorage,
Robert Berry91044042017-12-27 12:05:58 +0000135 mSnapshotListenersStorage,
Robert Berryf0a4bea2017-12-22 13:17:32 +0000136 TEST_USER_ID,
137 TEST_CREDENTIAL_TYPE,
138 TEST_CREDENTIAL,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800139 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700140 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700141 mTestOnlyInsecureCertificateHelper,
142 mMockScrypt);
Robert Berryf0a4bea2017-12-22 13:17:32 +0000143
144 mWrappingKey = generateAndroidKeyStoreKey();
145 mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
Bo Zhu3462c832018-01-04 22:42:36 -0800146 when(mPlatformKeyManager.getDecryptKey(TEST_USER_ID)).thenReturn(
Robert Berryf0a4bea2017-12-22 13:17:32 +0000147 new PlatformDecryptionKey(TEST_GENERATION_ID, mWrappingKey));
148 }
149
150 @After
151 public void tearDown() {
152 mRecoverableKeyStoreDb.close();
153 mDatabaseFile.delete();
Robert Berry56588372018-03-29 12:07:17 +0100154
155 File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
156 SNAPSHOT_TOP_LEVEL_DIRECTORY);
157 FileUtils.deleteContentsAndDir(file);
Robert Berryf0a4bea2017-12-22 13:17:32 +0000158 }
Robert Berry4a534ec2017-12-21 15:44:02 +0000159
160 @Test
161 public void isPin_isTrueForNumericString() {
162 assertTrue(KeySyncTask.isPin("3298432574398654376547"));
163 }
164
165 @Test
166 public void isPin_isFalseForStringContainingLetters() {
167 assertFalse(KeySyncTask.isPin("398i54369548654"));
168 }
169
170 @Test
171 public void isPin_isFalseForStringContainingSymbols() {
172 assertFalse(KeySyncTask.isPin("-3987543643"));
173 }
174
175 @Test
Bo Zhu76973432018-04-03 00:37:51 -0700176 public void hashCredentialsBySaltedSha256_returnsSameHashForSameCredentialsAndSalt() {
Robert Berry4a534ec2017-12-21 15:44:02 +0000177 String credentials = "password1234";
178 byte[] salt = randomBytes(16);
179
180 assertArrayEquals(
Bo Zhu76973432018-04-03 00:37:51 -0700181 KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials),
182 KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials));
Robert Berry4a534ec2017-12-21 15:44:02 +0000183 }
184
185 @Test
Bo Zhu76973432018-04-03 00:37:51 -0700186 public void hashCredentialsBySaltedSha256_returnsDifferentHashForDifferentCredentials() {
Robert Berry4a534ec2017-12-21 15:44:02 +0000187 byte[] salt = randomBytes(16);
188
189 assertFalse(
190 Arrays.equals(
Bo Zhu76973432018-04-03 00:37:51 -0700191 KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234"),
192 KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345")));
Robert Berry4a534ec2017-12-21 15:44:02 +0000193 }
194
195 @Test
Bo Zhu76973432018-04-03 00:37:51 -0700196 public void hashCredentialsBySaltedSha256_returnsDifferentHashForDifferentSalt() {
Robert Berry4a534ec2017-12-21 15:44:02 +0000197 String credentials = "wowmuch";
198
199 assertFalse(
200 Arrays.equals(
Bo Zhu76973432018-04-03 00:37:51 -0700201 KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials),
202 KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials)));
Robert Berry4a534ec2017-12-21 15:44:02 +0000203 }
204
205 @Test
Bo Zhu76973432018-04-03 00:37:51 -0700206 public void hashCredentialsBySaltedSha256_returnsDifferentHashEvenIfConcatIsSame() {
Robert Berry4a534ec2017-12-21 15:44:02 +0000207 assertFalse(
208 Arrays.equals(
Bo Zhu76973432018-04-03 00:37:51 -0700209 KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"), "4567"),
210 KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"), "567")));
Robert Berry4a534ec2017-12-21 15:44:02 +0000211 }
212
213 @Test
214 public void getUiFormat_returnsPinIfPin() {
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800215 assertEquals(UI_FORMAT_PIN,
Robert Berry4a534ec2017-12-21 15:44:02 +0000216 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234"));
217 }
218
219 @Test
220 public void getUiFormat_returnsPasswordIfPassword() {
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800221 assertEquals(UI_FORMAT_PASSWORD,
Robert Berry4a534ec2017-12-21 15:44:02 +0000222 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a"));
223 }
224
225 @Test
226 public void getUiFormat_returnsPatternIfPattern() {
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800227 assertEquals(UI_FORMAT_PATTERN,
Robert Berry4a534ec2017-12-21 15:44:02 +0000228 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234"));
229
230 }
231
Robert Berryf0a4bea2017-12-22 13:17:32 +0000232 @Test
233 public void run_doesNotSendAnythingIfNoKeysToSync() throws Exception {
Robert Berryf0a4bea2017-12-22 13:17:32 +0000234 mKeySyncTask.run();
235
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800236 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
237 }
238
239 @Test
240 public void run_doesNotSendAnythingIfSnapshotIsUpToDate() throws Exception {
241 mKeySyncTask.run();
242
243 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berryf0a4bea2017-12-22 13:17:32 +0000244 }
245
246 @Test
Robert Berry91044042017-12-27 12:05:58 +0000247 public void run_doesNotSendAnythingIfNoRecoveryAgentSet() throws Exception {
248 SecretKey applicationKey = generateKey();
249 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
250 mRecoverableKeyStoreDb.insertKey(
251 TEST_USER_ID,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800252 TEST_RECOVERY_AGENT_UID,
Robert Berry91044042017-12-27 12:05:58 +0000253 TEST_APP_KEY_ALIAS,
254 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
255 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
256
257 mKeySyncTask.run();
258
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800259 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berry91044042017-12-27 12:05:58 +0000260 }
261
262 @Test
Robert Berry94ea4e42017-12-28 12:08:30 +0000263 public void run_doesNotSendAnythingIfNoDeviceIdIsSet() throws Exception {
264 SecretKey applicationKey = generateKey();
265 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
266 mRecoverableKeyStoreDb.insertKey(
267 TEST_USER_ID,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800268 TEST_RECOVERY_AGENT_UID,
Robert Berry94ea4e42017-12-28 12:08:30 +0000269 TEST_APP_KEY_ALIAS,
270 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700271 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700272 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Robert Berry94ea4e42017-12-28 12:08:30 +0000273 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
274
275 mKeySyncTask.run();
276
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800277 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berry94ea4e42017-12-28 12:08:30 +0000278 }
279
280 @Test
Bo Zhu76973432018-04-03 00:37:51 -0700281 public void run_useScryptToHashLongPasswordInTestMode() throws Exception {
282 String longPassword = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "0123456789";
283 String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias";
284 mKeySyncTask = new KeySyncTask(
285 mRecoverableKeyStoreDb,
286 mRecoverySnapshotStorage,
287 mSnapshotListenersStorage,
288 TEST_USER_ID,
289 CREDENTIAL_TYPE_PASSWORD,
290 /*credential=*/ longPassword,
291 /*credentialUpdated=*/ false,
292 mPlatformKeyManager,
293 mTestOnlyInsecureCertificateHelper,
294 mMockScrypt);
295 mRecoverableKeyStoreDb.setServerParams(
296 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
297 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
298 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
299 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS);
300 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
301 TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
302 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
303 TestData.getInsecureCertPathForEndpoint1());
304 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
305
306 mKeySyncTask.run();
307
308 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
309 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
310 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
311 isEqualTo(UI_FORMAT_PASSWORD);
312 verify(mMockScrypt).scrypt(eq(longPassword.getBytes()), any(),
313 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
314 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
315 KeyDerivationParams keyDerivationParams =
316 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
317 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
318 KeyDerivationParams.ALGORITHM_SCRYPT);
319 assertThat(keyDerivationParams.getMemoryDifficulty()).isEqualTo(KeySyncTask.SCRYPT_PARAM_N);
320 }
321
322 @Test
323 public void run_useSha256ToHashShortPasswordInTestMode() throws Exception {
324 String shortPassword = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "012345678";
325 String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias";
326 mKeySyncTask = new KeySyncTask(
327 mRecoverableKeyStoreDb,
328 mRecoverySnapshotStorage,
329 mSnapshotListenersStorage,
330 TEST_USER_ID,
331 CREDENTIAL_TYPE_PASSWORD,
332 /*credential=*/ shortPassword,
333 /*credentialUpdated=*/ false,
334 mPlatformKeyManager,
335 mTestOnlyInsecureCertificateHelper,
336 mMockScrypt);
337 mRecoverableKeyStoreDb.setServerParams(
338 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
339 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
340 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
341 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS);
342 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
343 TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
344 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
345 TestData.getInsecureCertPathForEndpoint1());
346 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
347
348 mKeySyncTask.run();
349
350 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
351 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
352 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
353 isEqualTo(UI_FORMAT_PASSWORD);
354 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
355 KeyDerivationParams keyDerivationParams =
356 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
357 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
358 KeyDerivationParams.ALGORITHM_SHA256);
359 }
360
361 @Test
362 public void run_useSha256ToHashShortPasswordInProdMode() throws Exception {
363 String shortPassword = "01234567890123456789abc"; // 23 chars
364 mKeySyncTask = new KeySyncTask(
365 mRecoverableKeyStoreDb,
366 mRecoverySnapshotStorage,
367 mSnapshotListenersStorage,
368 TEST_USER_ID,
369 CREDENTIAL_TYPE_PASSWORD,
370 /*credential=*/ shortPassword,
371 /*credentialUpdated=*/ false,
372 mPlatformKeyManager,
373 mTestOnlyInsecureCertificateHelper,
374 mMockScrypt);
375 mRecoverableKeyStoreDb.setServerParams(
376 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
377 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
378 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
379 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
380 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
381
382 mKeySyncTask.run();
383
384 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
385 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
386 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
387 isEqualTo(UI_FORMAT_PASSWORD);
388 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
389 KeyDerivationParams keyDerivationParams =
390 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
391 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
392 KeyDerivationParams.ALGORITHM_SHA256);
393 }
394
395 @Test
396 public void run_useSha256ToHashLongPasswordInProdMode() throws Exception {
397 String longPassword = "01234567890123456789abcd"; // 24 chars
398 mKeySyncTask = new KeySyncTask(
399 mRecoverableKeyStoreDb,
400 mRecoverySnapshotStorage,
401 mSnapshotListenersStorage,
402 TEST_USER_ID,
403 CREDENTIAL_TYPE_PASSWORD,
404 /*credential=*/ longPassword,
405 /*credentialUpdated=*/ false,
406 mPlatformKeyManager,
407 mTestOnlyInsecureCertificateHelper,
408 mMockScrypt);
409 mRecoverableKeyStoreDb.setServerParams(
410 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
411 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
412 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
413 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
414 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
415
416 mKeySyncTask.run();
417
418 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
419 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
420 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
421 isEqualTo(UI_FORMAT_PASSWORD);
422 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
423 KeyDerivationParams keyDerivationParams =
424 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
425 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
426 KeyDerivationParams.ALGORITHM_SHA256);
427 }
428
429 @Test
Robert Berry2fd4b592018-03-15 15:28:05 +0000430 public void run_stillCreatesSnapshotIfNoRecoveryAgentPendingIntentRegistered()
431 throws Exception {
432 mRecoverableKeyStoreDb.setServerParams(
433 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
434 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
435 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700436 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700437 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Robert Berry2fd4b592018-03-15 15:28:05 +0000438
439 mKeySyncTask.run();
440
441 assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
442 }
443
444 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700445 public void run_InTestModeWithWhitelistedCredentials() throws Exception {
446 mRecoverableKeyStoreDb.setServerParams(
447 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
448 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
449 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
450 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
451 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
452
453 // Enter test mode with whitelisted credentials
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700454 when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true);
455 when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any()))
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700456 .thenReturn(true);
457 mKeySyncTask.run();
458
459 verify(mTestOnlyInsecureCertificateHelper)
460 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
461
462 // run whitelist checks
463 verify(mTestOnlyInsecureCertificateHelper)
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700464 .doesCredentialSupportInsecureMode(anyInt(), any());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700465 verify(mTestOnlyInsecureCertificateHelper)
466 .keepOnlyWhitelistedInsecureKeys(any());
467
468 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
469 assertNotNull(keyChainSnapshot); // created snapshot
470 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
471 assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included
Bo Zhu76973432018-04-03 00:37:51 -0700472 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700473 }
474
475 @Test
476 public void run_InTestModeWithNonWhitelistedCredentials() throws Exception {
477 mRecoverableKeyStoreDb.setServerParams(
478 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
479 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
480 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
481 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
482 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
483
484 // Enter test mode with non whitelisted credentials
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700485 when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true);
486 when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any()))
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700487 .thenReturn(false);
488 mKeySyncTask.run();
489
490 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); // not created
491 verify(mTestOnlyInsecureCertificateHelper)
492 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
493 verify(mTestOnlyInsecureCertificateHelper)
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700494 .doesCredentialSupportInsecureMode(anyInt(), any());
Bo Zhu76973432018-04-03 00:37:51 -0700495 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700496 }
497
498 @Test
499 public void run_doesNotFilterCredentialsAndAliasesInProd() throws Exception {
500 mRecoverableKeyStoreDb.setServerParams(
501 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
502 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
503 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
504 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
505 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
506
507 mKeySyncTask.run();
508 assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
509
510 verify(mTestOnlyInsecureCertificateHelper)
511 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
512 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700513 .isTestOnlyCertificateAlias(eq(TEST_ROOT_CERT_ALIAS));
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700514
515 // no whitelists check
516 verify(mTestOnlyInsecureCertificateHelper, never())
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700517 .doesCredentialSupportInsecureMode(anyInt(), any());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700518 verify(mTestOnlyInsecureCertificateHelper, never())
519 .keepOnlyWhitelistedInsecureKeys(any());
520 }
521
522 @Test
523 public void run_replacesNullActiveRootAliasWithDefaultValue() throws Exception {
524 mRecoverableKeyStoreDb.setServerParams(
525 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
526 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
527 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
528 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
529 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
530 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
531 /*alias=*/ null);
532
533 when(mTestOnlyInsecureCertificateHelper.getDefaultCertificateAliasIfEmpty(null))
534 .thenReturn(TEST_ROOT_CERT_ALIAS); // override default.
535 mKeySyncTask.run();
536
537 verify(mTestOnlyInsecureCertificateHelper).getDefaultCertificateAliasIfEmpty(null);
538 }
539
540 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800541 public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700542 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700543 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevadd1bad2018-01-18 16:44:08 -0800544
545 mRecoverableKeyStoreDb.setServerParams(
546 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
Robert Berry91044042017-12-27 12:05:58 +0000547 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800548 SecretKey applicationKey =
549 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyevadd1bad2018-01-18 16:44:08 -0800550
Robert Berryf0a4bea2017-12-22 13:17:32 +0000551 mKeySyncTask.run();
552
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800553 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
554 KeyDerivationParams keyDerivationParams =
555 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
556 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800557 KeyDerivationParams.ALGORITHM_SHA256);
Robert Berry91044042017-12-27 12:05:58 +0000558 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
Bo Zhu76973432018-04-03 00:37:51 -0700559 byte[] lockScreenHash = KeySyncTask.hashCredentialsBySaltedSha256(
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800560 keyDerivationParams.getSalt(),
Robert Berrybd086f12017-12-27 13:29:39 +0000561 TEST_CREDENTIAL);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800562 Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
563 assertThat(counterId).isNotNull();
Robert Berryf0a4bea2017-12-22 13:17:32 +0000564 byte[] recoveryKey = decryptThmEncryptedKey(
565 lockScreenHash,
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800566 keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
Robert Berry94ea4e42017-12-28 12:08:30 +0000567 /*vaultParams=*/ KeySyncUtils.packVaultParams(
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700568 TestData.CERT_1_PUBLIC_KEY,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800569 counterId,
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800570 /*maxAttempts=*/ 10,
571 TEST_VAULT_HANDLE));
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800572 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800573 assertThat(applicationKeys).hasSize(1);
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800574 assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
575 assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700576 assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
577 .isEqualTo(TestData.CERT_PATH_1);
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800578 assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
Robert Berry5f138702018-01-17 15:18:05 +0000579 WrappedApplicationKey keyData = applicationKeys.get(0);
Dmitry Dementyev07c765552018-01-08 17:31:59 -0800580 assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800581 assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
Robert Berryf0a4bea2017-12-22 13:17:32 +0000582 byte[] appKey = KeySyncUtils.decryptApplicationKey(
Robert Berrybd086f12017-12-27 13:29:39 +0000583 recoveryKey, keyData.getEncryptedKeyMaterial());
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800584 assertThat(appKey).isEqualTo(applicationKey.getEncoded());
585 }
586
587 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800588 public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
589 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700590 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Bo Zhu14d993d2018-02-03 21:38:48 -0800591 mRecoverableKeyStoreDb.setServerParams(
592 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
593 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
594 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
595
596 mKeySyncTask.run();
597
598 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
599 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
600 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
601 assertThat(applicationKeys).hasSize(1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700602 assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
603 .isEqualTo(TestData.CERT_PATH_1);
Bo Zhu14d993d2018-02-03 21:38:48 -0800604 }
605
606 @Test
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800607 public void run_setsCorrectSnapshotVersion() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700608 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700609 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800610 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev907e2752018-01-26 10:54:52 -0800611 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800612
613 mKeySyncTask.run();
614
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800615 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
616 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800617 mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
618
619 mKeySyncTask.run();
620
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800621 keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
622 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800623 }
624
625 @Test
Dmitry Dementyev907e2752018-01-26 10:54:52 -0800626 public void run_recreatesMissingSnapshot() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700627 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700628 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev907e2752018-01-26 10:54:52 -0800629 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
630 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
631
632 mKeySyncTask.run();
633
634 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
635 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
636
637 mRecoverySnapshotStorage.remove(TEST_RECOVERY_AGENT_UID); // corrupt snapshot.
638
639 mKeySyncTask.run();
640
641 keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
642 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // Same version
643 }
644
645 @Test
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800646 public void run_setsCorrectTypeForPassword() throws Exception {
647 mKeySyncTask = new KeySyncTask(
648 mRecoverableKeyStoreDb,
649 mRecoverySnapshotStorage,
650 mSnapshotListenersStorage,
651 TEST_USER_ID,
652 CREDENTIAL_TYPE_PASSWORD,
653 "password",
654 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700655 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700656 mTestOnlyInsecureCertificateHelper,
657 mMockScrypt);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800658
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700659 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700660 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800661 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
662 SecretKey applicationKey =
663 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
664
665 mKeySyncTask.run();
666
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800667 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
668 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
669 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
670 isEqualTo(UI_FORMAT_PASSWORD);
Bo Zhu76973432018-04-03 00:37:51 -0700671 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800672 }
673
Bo Zhu76973432018-04-03 00:37:51 -0700674 @Test
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800675 public void run_setsCorrectTypeForPin() throws Exception {
676 mKeySyncTask = new KeySyncTask(
677 mRecoverableKeyStoreDb,
678 mRecoverySnapshotStorage,
679 mSnapshotListenersStorage,
680 TEST_USER_ID,
681 CREDENTIAL_TYPE_PASSWORD,
682 /*credential=*/ "1234",
683 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700684 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700685 mTestOnlyInsecureCertificateHelper,
686 mMockScrypt);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800687
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700688 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700689 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800690 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
691 SecretKey applicationKey =
692 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
693
694 mKeySyncTask.run();
695
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800696 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
697 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800698 // Password with only digits is changed to pin.
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800699 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
700 isEqualTo(UI_FORMAT_PIN);
Bo Zhu76973432018-04-03 00:37:51 -0700701 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800702 }
703
704 @Test
705 public void run_setsCorrectTypeForPattern() throws Exception {
706 mKeySyncTask = new KeySyncTask(
707 mRecoverableKeyStoreDb,
708 mRecoverySnapshotStorage,
709 mSnapshotListenersStorage,
710 TEST_USER_ID,
711 CREDENTIAL_TYPE_PATTERN,
712 "12345",
713 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700714 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700715 mTestOnlyInsecureCertificateHelper,
716 mMockScrypt);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800717
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700718 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700719 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800720 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
721 SecretKey applicationKey =
722 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
723
724 mKeySyncTask.run();
725
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800726 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
727 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
728 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
729 isEqualTo(UI_FORMAT_PATTERN);
Bo Zhu76973432018-04-03 00:37:51 -0700730 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800731 }
732
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800733 @Test
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800734 public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700735 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700736 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700737 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700738 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800739 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
740 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
741 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
742 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
743 mKeySyncTask.run();
744
745 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
746 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
747 }
748
749 @Test
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800750 public void run_sendsEncryptedKeysOnlyForAgentWhichActiveUserSecretType() throws Exception {
751 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800752 new int[] {TYPE_LOCKSCREEN, 1000});
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800753 // Snapshot will not be created during unlock event.
754 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800755 new int[] {1000});
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800756
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700757 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700758 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700759 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700760 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800761 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800762 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
763 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
764 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
765 mKeySyncTask.run();
766
767 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
768 verify(mSnapshotListenersStorage, never()).
769 recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
770 }
771
772 @Test
Robert Berry2fd4b592018-03-15 15:28:05 +0000773 public void run_notifiesNonregisteredAgent() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700774 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700775 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700776 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700777 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800778 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800779 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
780 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
781 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
782 mKeySyncTask.run();
783
784 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
Robert Berry2fd4b592018-03-15 15:28:05 +0000785 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800786 }
787
Aseem Kumar3326da52018-03-12 18:05:16 -0700788 @Test
789 public void run_customLockScreen_RecoveryStatusFailure() throws Exception {
790 mKeySyncTask = new KeySyncTask(
791 mRecoverableKeyStoreDb,
792 mRecoverySnapshotStorage,
793 mSnapshotListenersStorage,
794 TEST_USER_ID,
795 /*credentialType=*/ 3,
796 "12345",
797 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700798 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700799 mTestOnlyInsecureCertificateHelper,
800 mMockScrypt);
Aseem Kumar3326da52018-03-12 18:05:16 -0700801
802 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
803
804 int status =
805 mRecoverableKeyStoreDb
806 .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID)
807 .get(TEST_APP_KEY_ALIAS);
808 assertEquals(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS, status);
809
810 mKeySyncTask.run();
811
812 status = mRecoverableKeyStoreDb
813 .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID)
814 .get(TEST_APP_KEY_ALIAS);
815 assertEquals(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE, status);
Bo Zhu76973432018-04-03 00:37:51 -0700816 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Aseem Kumar3326da52018-03-12 18:05:16 -0700817 }
818
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800819 private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
820 throws Exception{
821 SecretKey applicationKey = generateKey();
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800822 mRecoverableKeyStoreDb.setServerParams(
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800823 userId, recoveryAgentUid, TEST_VAULT_HANDLE);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800824 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, TEST_GENERATION_ID);
825
826 // Newly added key is not synced.
827 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, recoveryAgentUid, true);
828
829 mRecoverableKeyStoreDb.insertKey(
830 userId,
831 recoveryAgentUid,
832 alias,
833 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
834 return applicationKey;
Robert Berryf0a4bea2017-12-22 13:17:32 +0000835 }
836
837 private byte[] decryptThmEncryptedKey(
838 byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
839 byte[] locallyEncryptedKey = SecureBox.decrypt(
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700840 TestData.CERT_1_PRIVATE_KEY,
Robert Berryf0a4bea2017-12-22 13:17:32 +0000841 /*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
842 /*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
843 encryptedKey
844 );
845 return KeySyncUtils.decryptRecoveryKey(lockScreenHash, locallyEncryptedKey);
846 }
847
848 private SecretKey generateKey() throws Exception {
849 KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
850 keyGenerator.init(/*keySize=*/ 256);
851 return keyGenerator.generateKey();
852 }
853
854 private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
855 KeyGenerator keyGenerator = KeyGenerator.getInstance(
856 KEY_ALGORITHM,
857 ANDROID_KEY_STORE_PROVIDER);
858 keyGenerator.init(new KeyGenParameterSpec.Builder(
859 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
860 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
861 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
862 .build());
863 return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
864 }
865
Robert Berry4a534ec2017-12-21 15:44:02 +0000866 private static byte[] utf8Bytes(String s) {
867 return s.getBytes(StandardCharsets.UTF_8);
868 }
869
870 private static byte[] randomBytes(int n) {
871 byte[] bytes = new byte[n];
872 new Random().nextBytes(bytes);
873 return bytes;
874 }
875}