blob: c6fc675db6b15d349e79c3c3109ceefe3cd49f2a [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
Robert Berry9e1bd362018-01-17 23:28:45 +000019import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
Dmitry Dementyev122bfe12018-01-10 18:56:36 -080020
Robert Berry9e1bd362018-01-17 23:28:45 +000021import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD;
22import static android.security.keystore.KeychainProtectionParams.TYPE_PATTERN;
23import static android.security.keystore.KeychainProtectionParams.TYPE_PIN;
Robert Berry4a534ec2017-12-21 15:44:02 +000024
25import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
26import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
27
Dmitry Dementyev77183ef2018-01-05 15:46:00 -080028import static com.google.common.truth.Truth.assertThat;
29
Robert Berry4a534ec2017-12-21 15:44:02 +000030import static org.junit.Assert.assertArrayEquals;
31import static org.junit.Assert.assertEquals;
32import static org.junit.Assert.assertFalse;
Robert Berrybd086f12017-12-27 13:29:39 +000033import static org.junit.Assert.assertNull;
Robert Berry4a534ec2017-12-21 15:44:02 +000034import static org.junit.Assert.assertTrue;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -080035import static org.mockito.Mockito.never;
Robert Berry91044042017-12-27 12:05:58 +000036import static org.mockito.Mockito.verify;
Robert Berryf0a4bea2017-12-22 13:17:32 +000037import static org.mockito.Mockito.when;
Robert Berry4a534ec2017-12-21 15:44:02 +000038
Robert Berryf0a4bea2017-12-22 13:17:32 +000039import android.content.Context;
40import android.security.keystore.AndroidKeyStoreSecretKey;
41import android.security.keystore.KeyGenParameterSpec;
42import android.security.keystore.KeyProperties;
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -080043import android.security.keystore.KeyDerivationParams;
Robert Berry5f138702018-01-17 15:18:05 +000044import android.security.keystore.KeychainSnapshot;
45import android.security.keystore.WrappedApplicationKey;
Robert Berryf0a4bea2017-12-22 13:17:32 +000046import android.support.test.InstrumentationRegistry;
Robert Berry4a534ec2017-12-21 15:44:02 +000047import android.support.test.filters.SmallTest;
48import android.support.test.runner.AndroidJUnit4;
49
Robert Berryf0a4bea2017-12-22 13:17:32 +000050import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
Robert Berrybd086f12017-12-27 13:29:39 +000051import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
Robert Berryf0a4bea2017-12-22 13:17:32 +000052
53import org.junit.After;
54import org.junit.Before;
Robert Berry4a534ec2017-12-21 15:44:02 +000055import org.junit.Test;
56import org.junit.runner.RunWith;
Robert Berryf0a4bea2017-12-22 13:17:32 +000057import org.mockito.Mock;
58import org.mockito.MockitoAnnotations;
Robert Berry4a534ec2017-12-21 15:44:02 +000059
Robert Berryf0a4bea2017-12-22 13:17:32 +000060import java.io.File;
Robert Berry4a534ec2017-12-21 15:44:02 +000061import java.nio.charset.StandardCharsets;
Robert Berryf0a4bea2017-12-22 13:17:32 +000062import java.security.KeyPair;
Robert Berry4a534ec2017-12-21 15:44:02 +000063import java.util.Arrays;
Robert Berrybd086f12017-12-27 13:29:39 +000064import java.util.List;
Robert Berry4a534ec2017-12-21 15:44:02 +000065import java.util.Random;
66
Robert Berryf0a4bea2017-12-22 13:17:32 +000067import javax.crypto.KeyGenerator;
68import javax.crypto.SecretKey;
69
Robert Berry4a534ec2017-12-21 15:44:02 +000070@SmallTest
71@RunWith(AndroidJUnit4.class)
72public class KeySyncTaskTest {
Robert Berryf0a4bea2017-12-22 13:17:32 +000073 private static final String KEY_ALGORITHM = "AES";
74 private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
75 private static final String WRAPPING_KEY_ALIAS = "KeySyncTaskTest/WrappingKey";
76 private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
77 private static final int TEST_USER_ID = 1000;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -080078 private static final int TEST_RECOVERY_AGENT_UID = 10009;
79 private static final int TEST_RECOVERY_AGENT_UID2 = 10010;
Bo Zhu4ff2b3f2018-01-17 17:34:26 -080080 private static final byte[] TEST_VAULT_HANDLE =
81 new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17};
Robert Berryf0a4bea2017-12-22 13:17:32 +000082 private static final String TEST_APP_KEY_ALIAS = "rcleaver";
83 private static final int TEST_GENERATION_ID = 2;
84 private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PASSWORD;
85 private static final String TEST_CREDENTIAL = "password1234";
86 private static final byte[] THM_ENCRYPTED_RECOVERY_KEY_HEADER =
87 "V1 THM_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
88
Robert Berryf0a4bea2017-12-22 13:17:32 +000089 @Mock private PlatformKeyManager mPlatformKeyManager;
Robert Berry91044042017-12-27 12:05:58 +000090 @Mock private RecoverySnapshotListenersStorage mSnapshotListenersStorage;
Robert Berryf0a4bea2017-12-22 13:17:32 +000091
Robert Berrybd086f12017-12-27 13:29:39 +000092 private RecoverySnapshotStorage mRecoverySnapshotStorage;
Robert Berryf0a4bea2017-12-22 13:17:32 +000093 private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
94 private File mDatabaseFile;
95 private KeyPair mKeyPair;
96 private AndroidKeyStoreSecretKey mWrappingKey;
97 private PlatformEncryptionKey mEncryptKey;
98
99 private KeySyncTask mKeySyncTask;
100
101 @Before
102 public void setUp() throws Exception {
103 MockitoAnnotations.initMocks(this);
104
105 Context context = InstrumentationRegistry.getTargetContext();
106 mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
107 mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
108 mKeyPair = SecureBox.genKeyPair();
109
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800110 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
111 new int[] {TYPE_LOCKSCREEN});
112 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
113 new int[] {TYPE_LOCKSCREEN});
Robert Berrybd086f12017-12-27 13:29:39 +0000114 mRecoverySnapshotStorage = new RecoverySnapshotStorage();
115
Robert Berryf0a4bea2017-12-22 13:17:32 +0000116 mKeySyncTask = new KeySyncTask(
117 mRecoverableKeyStoreDb,
Robert Berrybd086f12017-12-27 13:29:39 +0000118 mRecoverySnapshotStorage,
Robert Berry91044042017-12-27 12:05:58 +0000119 mSnapshotListenersStorage,
Robert Berryf0a4bea2017-12-22 13:17:32 +0000120 TEST_USER_ID,
121 TEST_CREDENTIAL_TYPE,
122 TEST_CREDENTIAL,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800123 /*credentialUpdated=*/ false,
Robert Berryaa3f4ca2017-12-27 10:53:58 +0000124 () -> mPlatformKeyManager);
Robert Berryf0a4bea2017-12-22 13:17:32 +0000125
126 mWrappingKey = generateAndroidKeyStoreKey();
127 mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
Bo Zhu3462c832018-01-04 22:42:36 -0800128 when(mPlatformKeyManager.getDecryptKey(TEST_USER_ID)).thenReturn(
Robert Berryf0a4bea2017-12-22 13:17:32 +0000129 new PlatformDecryptionKey(TEST_GENERATION_ID, mWrappingKey));
130 }
131
132 @After
133 public void tearDown() {
134 mRecoverableKeyStoreDb.close();
135 mDatabaseFile.delete();
136 }
Robert Berry4a534ec2017-12-21 15:44:02 +0000137
138 @Test
139 public void isPin_isTrueForNumericString() {
140 assertTrue(KeySyncTask.isPin("3298432574398654376547"));
141 }
142
143 @Test
144 public void isPin_isFalseForStringContainingLetters() {
145 assertFalse(KeySyncTask.isPin("398i54369548654"));
146 }
147
148 @Test
149 public void isPin_isFalseForStringContainingSymbols() {
150 assertFalse(KeySyncTask.isPin("-3987543643"));
151 }
152
153 @Test
154 public void hashCredentials_returnsSameHashForSameCredentialsAndSalt() {
155 String credentials = "password1234";
156 byte[] salt = randomBytes(16);
157
158 assertArrayEquals(
159 KeySyncTask.hashCredentials(salt, credentials),
160 KeySyncTask.hashCredentials(salt, credentials));
161 }
162
163 @Test
164 public void hashCredentials_returnsDifferentHashForDifferentCredentials() {
165 byte[] salt = randomBytes(16);
166
167 assertFalse(
168 Arrays.equals(
169 KeySyncTask.hashCredentials(salt, "password1234"),
170 KeySyncTask.hashCredentials(salt, "password12345")));
171 }
172
173 @Test
174 public void hashCredentials_returnsDifferentHashForDifferentSalt() {
175 String credentials = "wowmuch";
176
177 assertFalse(
178 Arrays.equals(
179 KeySyncTask.hashCredentials(randomBytes(64), credentials),
180 KeySyncTask.hashCredentials(randomBytes(64), credentials)));
181 }
182
183 @Test
184 public void hashCredentials_returnsDifferentHashEvenIfConcatIsSame() {
185 assertFalse(
186 Arrays.equals(
187 KeySyncTask.hashCredentials(utf8Bytes("123"), "4567"),
188 KeySyncTask.hashCredentials(utf8Bytes("1234"), "567")));
189 }
190
191 @Test
192 public void getUiFormat_returnsPinIfPin() {
193 assertEquals(TYPE_PIN,
194 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234"));
195 }
196
197 @Test
198 public void getUiFormat_returnsPasswordIfPassword() {
199 assertEquals(TYPE_PASSWORD,
200 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a"));
201 }
202
203 @Test
204 public void getUiFormat_returnsPatternIfPattern() {
205 assertEquals(TYPE_PATTERN,
206 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234"));
207
208 }
209
Robert Berryf0a4bea2017-12-22 13:17:32 +0000210 @Test
211 public void run_doesNotSendAnythingIfNoKeysToSync() throws Exception {
Robert Berryf0a4bea2017-12-22 13:17:32 +0000212 mKeySyncTask.run();
213
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800214 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
215 }
216
217 @Test
218 public void run_doesNotSendAnythingIfSnapshotIsUpToDate() throws Exception {
219 mKeySyncTask.run();
220
221 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berryf0a4bea2017-12-22 13:17:32 +0000222 }
223
224 @Test
Robert Berry91044042017-12-27 12:05:58 +0000225 public void run_doesNotSendAnythingIfNoRecoveryAgentSet() throws Exception {
226 SecretKey applicationKey = generateKey();
227 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
228 mRecoverableKeyStoreDb.insertKey(
229 TEST_USER_ID,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800230 TEST_RECOVERY_AGENT_UID,
Robert Berry91044042017-12-27 12:05:58 +0000231 TEST_APP_KEY_ALIAS,
232 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
233 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
234
235 mKeySyncTask.run();
236
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800237 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berry91044042017-12-27 12:05:58 +0000238 }
239
240 @Test
241 public void run_doesNotSendAnythingIfNoRecoveryAgentPendingIntentRegistered() throws Exception {
242 SecretKey applicationKey = generateKey();
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800243 mRecoverableKeyStoreDb.setServerParams(
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800244 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
Robert Berry91044042017-12-27 12:05:58 +0000245 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
246 mRecoverableKeyStoreDb.insertKey(
247 TEST_USER_ID,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800248 TEST_RECOVERY_AGENT_UID,
Robert Berry91044042017-12-27 12:05:58 +0000249 TEST_APP_KEY_ALIAS,
250 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
251 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
252 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
253
254 mKeySyncTask.run();
255
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800256 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berry91044042017-12-27 12:05:58 +0000257 }
258
259 @Test
Robert Berry94ea4e42017-12-28 12:08:30 +0000260 public void run_doesNotSendAnythingIfNoDeviceIdIsSet() throws Exception {
261 SecretKey applicationKey = generateKey();
262 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
263 mRecoverableKeyStoreDb.insertKey(
264 TEST_USER_ID,
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800265 TEST_RECOVERY_AGENT_UID,
Robert Berry94ea4e42017-12-28 12:08:30 +0000266 TEST_APP_KEY_ALIAS,
267 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
268 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
269 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
270 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
271
272 mKeySyncTask.run();
273
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800274 assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
Robert Berry94ea4e42017-12-28 12:08:30 +0000275 }
276
277 @Test
Robert Berryf0a4bea2017-12-22 13:17:32 +0000278 public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception {
Robert Berry91044042017-12-27 12:05:58 +0000279 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
280 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
281 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800282 SecretKey applicationKey =
283 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
Robert Berryf0a4bea2017-12-22 13:17:32 +0000284 mKeySyncTask.run();
285
Robert Berry5f138702018-01-17 15:18:05 +0000286 KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800287 KeyDerivationParams KeyDerivationParams =
Robert Berry5f138702018-01-17 15:18:05 +0000288 keychainSnapshot.getKeychainProtectionParams().get(0).getKeyDerivationParams();
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800289 assertThat(KeyDerivationParams.getAlgorithm()).isEqualTo(
290 KeyDerivationParams.ALGORITHM_SHA256);
Robert Berry91044042017-12-27 12:05:58 +0000291 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
Robert Berryf0a4bea2017-12-22 13:17:32 +0000292 byte[] lockScreenHash = KeySyncTask.hashCredentials(
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800293 KeyDerivationParams.getSalt(),
Robert Berrybd086f12017-12-27 13:29:39 +0000294 TEST_CREDENTIAL);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800295 Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
296 assertThat(counterId).isNotNull();
Robert Berryf0a4bea2017-12-22 13:17:32 +0000297 byte[] recoveryKey = decryptThmEncryptedKey(
298 lockScreenHash,
Robert Berry5f138702018-01-17 15:18:05 +0000299 keychainSnapshot.getEncryptedRecoveryKeyBlob(),
Robert Berry94ea4e42017-12-28 12:08:30 +0000300 /*vaultParams=*/ KeySyncUtils.packVaultParams(
301 mKeyPair.getPublic(),
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800302 counterId,
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800303 /*maxAttempts=*/ 10,
304 TEST_VAULT_HANDLE));
Robert Berry5f138702018-01-17 15:18:05 +0000305 List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys();
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800306 assertThat(applicationKeys).hasSize(1);
Robert Berry5f138702018-01-17 15:18:05 +0000307 WrappedApplicationKey keyData = applicationKeys.get(0);
Dmitry Dementyev07c765552018-01-08 17:31:59 -0800308 assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800309 assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
Robert Berryf0a4bea2017-12-22 13:17:32 +0000310 byte[] appKey = KeySyncUtils.decryptApplicationKey(
Robert Berrybd086f12017-12-27 13:29:39 +0000311 recoveryKey, keyData.getEncryptedKeyMaterial());
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800312 assertThat(appKey).isEqualTo(applicationKey.getEncoded());
313 }
314
315 @Test
316 public void run_setsCorrectSnapshotVersion() throws Exception {
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800317 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
318 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
319 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
320 SecretKey applicationKey =
321 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
322
323 mKeySyncTask.run();
324
Robert Berry5f138702018-01-17 15:18:05 +0000325 KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
326 assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800327 mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
328
329 mKeySyncTask.run();
330
Robert Berry5f138702018-01-17 15:18:05 +0000331 keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
332 assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800333 }
334
335 @Test
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800336 public void run_setsCorrectTypeForPassword() throws Exception {
337 mKeySyncTask = new KeySyncTask(
338 mRecoverableKeyStoreDb,
339 mRecoverySnapshotStorage,
340 mSnapshotListenersStorage,
341 TEST_USER_ID,
342 CREDENTIAL_TYPE_PASSWORD,
343 "password",
344 /*credentialUpdated=*/ false,
345 () -> mPlatformKeyManager);
346
347 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
348 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
349 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
350 SecretKey applicationKey =
351 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
352
353 mKeySyncTask.run();
354
Robert Berry5f138702018-01-17 15:18:05 +0000355 KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
356 assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
357 assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800358 isEqualTo(TYPE_PASSWORD);
359 }
360
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800361 @Test
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800362 public void run_setsCorrectTypeForPin() throws Exception {
363 mKeySyncTask = new KeySyncTask(
364 mRecoverableKeyStoreDb,
365 mRecoverySnapshotStorage,
366 mSnapshotListenersStorage,
367 TEST_USER_ID,
368 CREDENTIAL_TYPE_PASSWORD,
369 /*credential=*/ "1234",
370 /*credentialUpdated=*/ false,
371 () -> mPlatformKeyManager);
372
373 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
374 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
375 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
376 SecretKey applicationKey =
377 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
378
379 mKeySyncTask.run();
380
Robert Berry5f138702018-01-17 15:18:05 +0000381 KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
382 assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800383 // Password with only digits is changed to pin.
Robert Berry5f138702018-01-17 15:18:05 +0000384 assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800385 isEqualTo(TYPE_PIN);
386 }
387
388 @Test
389 public void run_setsCorrectTypeForPattern() throws Exception {
390 mKeySyncTask = new KeySyncTask(
391 mRecoverableKeyStoreDb,
392 mRecoverySnapshotStorage,
393 mSnapshotListenersStorage,
394 TEST_USER_ID,
395 CREDENTIAL_TYPE_PATTERN,
396 "12345",
397 /*credentialUpdated=*/ false,
398 () -> mPlatformKeyManager);
399
400 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
401 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
402 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
403 SecretKey applicationKey =
404 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
405
406 mKeySyncTask.run();
407
Robert Berry5f138702018-01-17 15:18:05 +0000408 KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
409 assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
410 assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800411 isEqualTo(TYPE_PATTERN);
412 }
413
Dmitry Dementyevabd713c2018-01-09 15:08:13 -0800414 @Test
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800415 public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800416 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
417 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
418 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
419 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
420 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
421 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
422 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
423 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
424 mKeySyncTask.run();
425
426 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
427 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
428 }
429
430 @Test
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800431 public void run_sendsEncryptedKeysOnlyForAgentWhichActiveUserSecretType() throws Exception {
432 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800433 new int[] {TYPE_LOCKSCREEN, 1000});
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800434 // Snapshot will not be created during unlock event.
435 mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800436 new int[] {1000});
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800437
438 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
439 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
440 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
441 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
442 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev122bfe12018-01-10 18:56:36 -0800443 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
444 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
445 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
446 mKeySyncTask.run();
447
448 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
449 verify(mSnapshotListenersStorage, never()).
450 recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
451 }
452
453 @Test
454 public void run_doesNotSendKeyToNonregisteredAgent() throws Exception {
455 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
456 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
457 mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
458 TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
459 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800460 when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
461 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
462 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
463 mKeySyncTask.run();
464
465 verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
466 verify(mSnapshotListenersStorage, never()).
467 recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
468 }
469
470 private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
471 throws Exception{
472 SecretKey applicationKey = generateKey();
Dmitry Dementyev7d8c78a2018-01-12 19:14:07 -0800473 mRecoverableKeyStoreDb.setServerParams(
Bo Zhu4ff2b3f2018-01-17 17:34:26 -0800474 userId, recoveryAgentUid, TEST_VAULT_HANDLE);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800475 mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, TEST_GENERATION_ID);
476
477 // Newly added key is not synced.
478 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, recoveryAgentUid, true);
479
480 mRecoverableKeyStoreDb.insertKey(
481 userId,
482 recoveryAgentUid,
483 alias,
484 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
485 return applicationKey;
Robert Berryf0a4bea2017-12-22 13:17:32 +0000486 }
487
488 private byte[] decryptThmEncryptedKey(
489 byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
490 byte[] locallyEncryptedKey = SecureBox.decrypt(
491 mKeyPair.getPrivate(),
492 /*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
493 /*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
494 encryptedKey
495 );
496 return KeySyncUtils.decryptRecoveryKey(lockScreenHash, locallyEncryptedKey);
497 }
498
499 private SecretKey generateKey() throws Exception {
500 KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
501 keyGenerator.init(/*keySize=*/ 256);
502 return keyGenerator.generateKey();
503 }
504
505 private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
506 KeyGenerator keyGenerator = KeyGenerator.getInstance(
507 KEY_ALGORITHM,
508 ANDROID_KEY_STORE_PROVIDER);
509 keyGenerator.init(new KeyGenParameterSpec.Builder(
510 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
511 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
512 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
513 .build());
514 return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
515 }
516
Robert Berry4a534ec2017-12-21 15:44:02 +0000517 private static byte[] utf8Bytes(String s) {
518 return s.getBytes(StandardCharsets.UTF_8);
519 }
520
521 private static byte[] randomBytes(int n) {
522 byte[] bytes = new byte[n];
523 new Random().nextBytes(bytes);
524 return bytes;
525 }
526}