blob: a9d6c29e57ce403073923d45c6cb22c9d1800bee [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;
Bo Zhuc3aefbd2018-04-06 09:57:02 -070095 private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PATTERN;
96 private static final String TEST_CREDENTIAL = "pas123";
Robert Berryf0a4bea2017-12-22 13:17:32 +000097 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 Zhuc3aefbd2018-04-06 09:57:02 -0700281 public void run_useScryptToHashPasswordInTestMode() throws Exception {
282 String password = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + ""; // The shortest
Bo Zhu76973432018-04-03 00:37:51 -0700283 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,
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700290 /*credential=*/ password,
Bo Zhu76973432018-04-03 00:37:51 -0700291 /*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);
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700312 verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
Bo Zhu76973432018-04-03 00:37:51 -0700313 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
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700323 public void run_useSha256ToHashPatternInProdMode() throws Exception {
324 String pattern = "123456";
Bo Zhu76973432018-04-03 00:37:51 -0700325 mKeySyncTask = new KeySyncTask(
326 mRecoverableKeyStoreDb,
327 mRecoverySnapshotStorage,
328 mSnapshotListenersStorage,
329 TEST_USER_ID,
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700330 CREDENTIAL_TYPE_PATTERN,
331 /*credential=*/ pattern,
Bo Zhu76973432018-04-03 00:37:51 -0700332 /*credentialUpdated=*/ false,
333 mPlatformKeyManager,
334 mTestOnlyInsecureCertificateHelper,
335 mMockScrypt);
336 mRecoverableKeyStoreDb.setServerParams(
337 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
338 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700339 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Bo Zhu76973432018-04-03 00:37:51 -0700340 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700341 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Bo Zhu76973432018-04-03 00:37:51 -0700342
343 mKeySyncTask.run();
344
345 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
346 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
347 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700348 isEqualTo(UI_FORMAT_PATTERN);
Bo Zhu76973432018-04-03 00:37:51 -0700349 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
350 KeyDerivationParams keyDerivationParams =
351 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
352 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
353 KeyDerivationParams.ALGORITHM_SHA256);
354 }
355
356 @Test
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700357 public void run_useScryptToHashPasswordInProdMode() throws Exception {
358 String shortPassword = "abc";
Bo Zhu76973432018-04-03 00:37:51 -0700359 mKeySyncTask = new KeySyncTask(
360 mRecoverableKeyStoreDb,
361 mRecoverySnapshotStorage,
362 mSnapshotListenersStorage,
363 TEST_USER_ID,
364 CREDENTIAL_TYPE_PASSWORD,
365 /*credential=*/ shortPassword,
366 /*credentialUpdated=*/ false,
367 mPlatformKeyManager,
368 mTestOnlyInsecureCertificateHelper,
369 mMockScrypt);
370 mRecoverableKeyStoreDb.setServerParams(
371 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
372 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
373 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
374 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
375 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
376
377 mKeySyncTask.run();
378
379 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
380 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
381 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
382 isEqualTo(UI_FORMAT_PASSWORD);
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700383 verify(mMockScrypt).scrypt(eq(shortPassword.getBytes()), any(),
384 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
385 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
Bo Zhu76973432018-04-03 00:37:51 -0700386 KeyDerivationParams keyDerivationParams =
387 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
388 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700389 KeyDerivationParams.ALGORITHM_SCRYPT);
Bo Zhu76973432018-04-03 00:37:51 -0700390 }
391
392 @Test
Robert Berry2fd4b592018-03-15 15:28:05 +0000393 public void run_stillCreatesSnapshotIfNoRecoveryAgentPendingIntentRegistered()
394 throws Exception {
395 mRecoverableKeyStoreDb.setServerParams(
396 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
397 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
398 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700399 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700400 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Robert Berry2fd4b592018-03-15 15:28:05 +0000401
402 mKeySyncTask.run();
403
404 assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
405 }
406
407 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700408 public void run_InTestModeWithWhitelistedCredentials() throws Exception {
409 mRecoverableKeyStoreDb.setServerParams(
410 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
411 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
412 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
413 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
414 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
415
416 // Enter test mode with whitelisted credentials
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700417 when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true);
418 when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any()))
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700419 .thenReturn(true);
420 mKeySyncTask.run();
421
422 verify(mTestOnlyInsecureCertificateHelper)
423 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
424
425 // run whitelist checks
426 verify(mTestOnlyInsecureCertificateHelper)
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700427 .doesCredentialSupportInsecureMode(anyInt(), any());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700428 verify(mTestOnlyInsecureCertificateHelper)
429 .keepOnlyWhitelistedInsecureKeys(any());
430
431 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
432 assertNotNull(keyChainSnapshot); // created snapshot
433 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
434 assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included
Bo Zhu76973432018-04-03 00:37:51 -0700435 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700436 }
437
438 @Test
439 public void run_InTestModeWithNonWhitelistedCredentials() throws Exception {
440 mRecoverableKeyStoreDb.setServerParams(
441 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
442 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
443 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
444 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
445 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
446
447 // Enter test mode with non whitelisted credentials
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700448 when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true);
449 when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any()))
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700450 .thenReturn(false);
451 mKeySyncTask.run();
452
453 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); // not created
454 verify(mTestOnlyInsecureCertificateHelper)
455 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
456 verify(mTestOnlyInsecureCertificateHelper)
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700457 .doesCredentialSupportInsecureMode(anyInt(), any());
Bo Zhu76973432018-04-03 00:37:51 -0700458 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700459 }
460
461 @Test
462 public void run_doesNotFilterCredentialsAndAliasesInProd() throws Exception {
463 mRecoverableKeyStoreDb.setServerParams(
464 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
465 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
466 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
467 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
468 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
469
470 mKeySyncTask.run();
471 assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
472
473 verify(mTestOnlyInsecureCertificateHelper)
474 .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
475 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700476 .isTestOnlyCertificateAlias(eq(TEST_ROOT_CERT_ALIAS));
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700477
478 // no whitelists check
479 verify(mTestOnlyInsecureCertificateHelper, never())
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700480 .doesCredentialSupportInsecureMode(anyInt(), any());
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700481 verify(mTestOnlyInsecureCertificateHelper, never())
482 .keepOnlyWhitelistedInsecureKeys(any());
483 }
484
485 @Test
486 public void run_replacesNullActiveRootAliasWithDefaultValue() throws Exception {
487 mRecoverableKeyStoreDb.setServerParams(
488 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
489 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
490 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
491 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
492 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
493 mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
494 /*alias=*/ null);
495
496 when(mTestOnlyInsecureCertificateHelper.getDefaultCertificateAliasIfEmpty(null))
497 .thenReturn(TEST_ROOT_CERT_ALIAS); // override default.
498 mKeySyncTask.run();
499
500 verify(mTestOnlyInsecureCertificateHelper).getDefaultCertificateAliasIfEmpty(null);
501 }
502
503 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800504 public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700505 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700506 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevadd1bad2018-01-18 16:44:08 -0800507
508 mRecoverableKeyStoreDb.setServerParams(
509 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
Robert Berry91044042017-12-27 12:05:58 +0000510 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800511 SecretKey applicationKey =
512 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyevadd1bad2018-01-18 16:44:08 -0800513
Robert Berryf0a4bea2017-12-22 13:17:32 +0000514 mKeySyncTask.run();
515
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800516 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
517 KeyDerivationParams keyDerivationParams =
518 keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
519 assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800520 KeyDerivationParams.ALGORITHM_SHA256);
Robert Berry91044042017-12-27 12:05:58 +0000521 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
Bo Zhu76973432018-04-03 00:37:51 -0700522 byte[] lockScreenHash = KeySyncTask.hashCredentialsBySaltedSha256(
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800523 keyDerivationParams.getSalt(),
Robert Berrybd086f12017-12-27 13:29:39 +0000524 TEST_CREDENTIAL);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800525 Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
526 assertThat(counterId).isNotNull();
Robert Berryf0a4bea2017-12-22 13:17:32 +0000527 byte[] recoveryKey = decryptThmEncryptedKey(
528 lockScreenHash,
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800529 keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
Robert Berry94ea4e42017-12-28 12:08:30 +0000530 /*vaultParams=*/ KeySyncUtils.packVaultParams(
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700531 TestData.CERT_1_PUBLIC_KEY,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800532 counterId,
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800533 /*maxAttempts=*/ 10,
534 TEST_VAULT_HANDLE));
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800535 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800536 assertThat(applicationKeys).hasSize(1);
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800537 assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
538 assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700539 assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
540 .isEqualTo(TestData.CERT_PATH_1);
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800541 assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
Robert Berry5f138702018-01-17 15:18:05 +0000542 WrappedApplicationKey keyData = applicationKeys.get(0);
Dmitry Dementyev07c765552018-01-08 17:31:59 -0800543 assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800544 assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
Robert Berryf0a4bea2017-12-22 13:17:32 +0000545 byte[] appKey = KeySyncUtils.decryptApplicationKey(
Robert Berrybd086f12017-12-27 13:29:39 +0000546 recoveryKey, keyData.getEncryptedKeyMaterial());
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800547 assertThat(appKey).isEqualTo(applicationKey.getEncoded());
548 }
549
550 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800551 public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
552 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700553 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Bo Zhu14d993d2018-02-03 21:38:48 -0800554 mRecoverableKeyStoreDb.setServerParams(
555 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
556 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
557 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
558
559 mKeySyncTask.run();
560
561 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
562 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
563 List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
564 assertThat(applicationKeys).hasSize(1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700565 assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
566 .isEqualTo(TestData.CERT_PATH_1);
Bo Zhu14d993d2018-02-03 21:38:48 -0800567 }
568
569 @Test
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800570 public void run_setsCorrectSnapshotVersion() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700571 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700572 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800573 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev907e2752018-01-26 10:54:52 -0800574 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800575
576 mKeySyncTask.run();
577
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800578 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
579 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800580 mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
581
582 mKeySyncTask.run();
583
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800584 keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
585 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800586 }
587
588 @Test
Dmitry Dementyev907e2752018-01-26 10:54:52 -0800589 public void run_recreatesMissingSnapshot() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700590 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700591 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev907e2752018-01-26 10:54:52 -0800592 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
593 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
594
595 mKeySyncTask.run();
596
597 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
598 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
599
600 mRecoverySnapshotStorage.remove(TEST_RECOVERY_AGENT_UID); // corrupt snapshot.
601
602 mKeySyncTask.run();
603
604 keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
605 assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // Same version
606 }
607
608 @Test
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800609 public void run_setsCorrectTypeForPassword() throws Exception {
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700610 String password = "password";
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800611 mKeySyncTask = new KeySyncTask(
612 mRecoverableKeyStoreDb,
613 mRecoverySnapshotStorage,
614 mSnapshotListenersStorage,
615 TEST_USER_ID,
616 CREDENTIAL_TYPE_PASSWORD,
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700617 password,
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800618 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700619 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700620 mTestOnlyInsecureCertificateHelper,
621 mMockScrypt);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800622
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700623 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700624 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800625 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700626 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800627
628 mKeySyncTask.run();
629
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800630 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
631 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
632 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
633 isEqualTo(UI_FORMAT_PASSWORD);
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700634 verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
635 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
636 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800637 }
638
Bo Zhu76973432018-04-03 00:37:51 -0700639 @Test
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800640 public void run_setsCorrectTypeForPin() throws Exception {
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700641 String pin = "1234";
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800642 mKeySyncTask = new KeySyncTask(
643 mRecoverableKeyStoreDb,
644 mRecoverySnapshotStorage,
645 mSnapshotListenersStorage,
646 TEST_USER_ID,
647 CREDENTIAL_TYPE_PASSWORD,
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700648 /*credential=*/ pin,
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800649 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700650 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700651 mTestOnlyInsecureCertificateHelper,
652 mMockScrypt);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800653
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700654 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700655 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800656 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
657 SecretKey applicationKey =
658 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
659
660 mKeySyncTask.run();
661
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800662 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
663 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800664 // Password with only digits is changed to pin.
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800665 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
666 isEqualTo(UI_FORMAT_PIN);
Bo Zhuc3aefbd2018-04-06 09:57:02 -0700667 verify(mMockScrypt).scrypt(eq(pin.getBytes()), any(),
668 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
669 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800670 }
671
672 @Test
673 public void run_setsCorrectTypeForPattern() throws Exception {
674 mKeySyncTask = new KeySyncTask(
675 mRecoverableKeyStoreDb,
676 mRecoverySnapshotStorage,
677 mSnapshotListenersStorage,
678 TEST_USER_ID,
679 CREDENTIAL_TYPE_PATTERN,
680 "12345",
681 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700682 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700683 mTestOnlyInsecureCertificateHelper,
684 mMockScrypt);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800685
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700686 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700687 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800688 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
689 SecretKey applicationKey =
690 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
691
692 mKeySyncTask.run();
693
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800694 KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
695 assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
696 assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
697 isEqualTo(UI_FORMAT_PATTERN);
Bo Zhu76973432018-04-03 00:37:51 -0700698 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800699 }
700
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800701 @Test
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800702 public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700703 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700704 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700705 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700706 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800707 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
708 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
709 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
710 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
711 mKeySyncTask.run();
712
713 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
714 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
715 }
716
717 @Test
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800718 public void run_sendsEncryptedKeysOnlyForAgentWhichActiveUserSecretType() throws Exception {
719 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800720 new int[] {TYPE_LOCKSCREEN, 1000});
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800721 // Snapshot will not be created during unlock event.
722 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800723 new int[] {1000});
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800724
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700725 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700726 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700727 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700728 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800729 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800730 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
731 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
732 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
733 mKeySyncTask.run();
734
735 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
736 verify(mSnapshotListenersStorage, never()).
737 recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
738 }
739
740 @Test
Robert Berry2fd4b592018-03-15 15:28:05 +0000741 public void run_notifiesNonregisteredAgent() throws Exception {
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700742 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700743 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700744 mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700745 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800746 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800747 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
748 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
749 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
750 mKeySyncTask.run();
751
752 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
Robert Berry2fd4b592018-03-15 15:28:05 +0000753 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800754 }
755
Aseem Kumar3326da52018-03-12 18:05:16 -0700756 @Test
757 public void run_customLockScreen_RecoveryStatusFailure() throws Exception {
758 mKeySyncTask = new KeySyncTask(
759 mRecoverableKeyStoreDb,
760 mRecoverySnapshotStorage,
761 mSnapshotListenersStorage,
762 TEST_USER_ID,
763 /*credentialType=*/ 3,
764 "12345",
765 /*credentialUpdated=*/ false,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700766 mPlatformKeyManager,
Bo Zhu76973432018-04-03 00:37:51 -0700767 mTestOnlyInsecureCertificateHelper,
768 mMockScrypt);
Aseem Kumar3326da52018-03-12 18:05:16 -0700769
770 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
771
772 int status =
773 mRecoverableKeyStoreDb
774 .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID)
775 .get(TEST_APP_KEY_ALIAS);
776 assertEquals(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS, status);
777
778 mKeySyncTask.run();
779
780 status = mRecoverableKeyStoreDb
781 .getStatusForAllKeys(TEST_RECOVERY_AGENT_UID)
782 .get(TEST_APP_KEY_ALIAS);
783 assertEquals(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE, status);
Bo Zhu76973432018-04-03 00:37:51 -0700784 verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
Aseem Kumar3326da52018-03-12 18:05:16 -0700785 }
786
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800787 private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
788 throws Exception{
789 SecretKey applicationKey = generateKey();
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800790 mRecoverableKeyStoreDb.setServerParams(
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800791 userId, recoveryAgentUid, TEST_VAULT_HANDLE);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800792 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, TEST_GENERATION_ID);
793
794 // Newly added key is not synced.
795 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, recoveryAgentUid, true);
796
797 mRecoverableKeyStoreDb.insertKey(
798 userId,
799 recoveryAgentUid,
800 alias,
801 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
802 return applicationKey;
Robert Berryf0a4bea2017-12-22 13:17:32 +0000803 }
804
805 private byte[] decryptThmEncryptedKey(
806 byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
807 byte[] locallyEncryptedKey = SecureBox.decrypt(
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700808 TestData.CERT_1_PRIVATE_KEY,
Robert Berryf0a4bea2017-12-22 13:17:32 +0000809 /*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
810 /*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
811 encryptedKey
812 );
813 return KeySyncUtils.decryptRecoveryKey(lockScreenHash, locallyEncryptedKey);
814 }
815
816 private SecretKey generateKey() throws Exception {
817 KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
818 keyGenerator.init(/*keySize=*/ 256);
819 return keyGenerator.generateKey();
820 }
821
822 private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
823 KeyGenerator keyGenerator = KeyGenerator.getInstance(
824 KEY_ALGORITHM,
825 ANDROID_KEY_STORE_PROVIDER);
826 keyGenerator.init(new KeyGenParameterSpec.Builder(
827 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
828 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
829 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
830 .build());
831 return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
832 }
833
Robert Berry4a534ec2017-12-21 15:44:02 +0000834 private static byte[] utf8Bytes(String s) {
835 return s.getBytes(StandardCharsets.UTF_8);
836 }
837
838 private static byte[] randomBytes(int n) {
839 byte[] bytes = new byte[n];
840 new Random().nextBytes(bytes);
841 return bytes;
842 }
843}