blob: c78b96d2d29437be3a9351bec224f4c7ededb080 [file] [log] [blame]
Robert Berrye16fa982017-12-20 15:59:37 +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;
20import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD;
Bo Zhu41d2dd22018-03-30 12:20:06 -070021import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
Aseem Kumar23174b72018-04-03 11:35:51 -070022import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE;
Bo Zhu41d2dd22018-03-30 12:20:06 -070023import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
Robert Berrye16fa982017-12-20 15:59:37 +000024
Dmitry Dementyevad884712017-12-20 12:38:36 -080025import static com.google.common.truth.Truth.assertThat;
Brett Chabota26eda92018-07-23 13:08:30 -070026
Robert Berrye16fa982017-12-20 15:59:37 +000027import static org.junit.Assert.assertArrayEquals;
28import static org.junit.Assert.assertEquals;
Robert Berryb9a220b2017-12-21 12:41:01 +000029import static org.junit.Assert.fail;
Robert Berrye16fa982017-12-20 15:59:37 +000030import static org.mockito.ArgumentMatchers.any;
Robert Berrycfc990a2017-12-22 15:54:30 +000031import static org.mockito.ArgumentMatchers.anyInt;
32import static org.mockito.ArgumentMatchers.anyString;
Robert Berrye16fa982017-12-20 15:59:37 +000033import static org.mockito.ArgumentMatchers.eq;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070034import static org.mockito.Mockito.atLeast;
Robert Berrye16fa982017-12-20 15:59:37 +000035import static org.mockito.Mockito.times;
36import static org.mockito.Mockito.verify;
Robert Berrycfc990a2017-12-22 15:54:30 +000037import static org.mockito.Mockito.when;
Robert Berrye16fa982017-12-20 15:59:37 +000038
Brett Chabota26eda92018-07-23 13:08:30 -070039import android.Manifest;
Robert Berrycfc990a2017-12-22 15:54:30 +000040import android.app.KeyguardManager;
Dmitry Dementyev3b17c632017-12-21 17:30:48 -080041import android.app.PendingIntent;
Robert Berrye16fa982017-12-20 15:59:37 +000042import android.content.Context;
Dmitry Dementyev3b17c632017-12-21 17:30:48 -080043import android.content.Intent;
44import android.os.Binder;
Dmitry Dementyev14298312018-01-04 15:19:19 -080045import android.os.ServiceSpecificException;
Dmitry Dementyevad884712017-12-20 12:38:36 -080046import android.os.UserHandle;
Bo Zhu3462c832018-01-04 22:42:36 -080047import android.security.keystore.AndroidKeyStoreSecretKey;
48import android.security.keystore.KeyGenParameterSpec;
49import android.security.keystore.KeyProperties;
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080050import android.security.keystore.recovery.KeyChainProtectionParams;
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070051import android.security.keystore.recovery.KeyDerivationParams;
Bo Zhu7c1972f2018-02-22 21:43:52 -080052import android.security.keystore.recovery.RecoveryCertPath;
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070053import android.security.keystore.recovery.TrustedRootCertificates;
Robert Berry81ee34b2018-01-23 11:59:59 +000054import android.security.keystore.recovery.WrappedApplicationKey;
Bo Zhu7ebcd662019-01-04 17:00:58 -080055import android.util.Pair;
Brett Chabota26eda92018-07-23 13:08:30 -070056
57import androidx.test.InstrumentationRegistry;
58import androidx.test.filters.SmallTest;
59import androidx.test.runner.AndroidJUnit4;
Robert Berrye16fa982017-12-20 15:59:37 +000060
Dmitry Dementyev29b9de52018-01-31 16:09:32 -080061import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
Robert Berrye16fa982017-12-20 15:59:37 +000062import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
63import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
Robert Berrybd086f12017-12-27 13:29:39 +000064import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
Robert Berrye16fa982017-12-20 15:59:37 +000065
66import com.google.common.collect.ImmutableList;
Robert Berryb9a220b2017-12-21 12:41:01 +000067import com.google.common.collect.ImmutableMap;
Robert Berrye16fa982017-12-20 15:59:37 +000068
69import org.junit.After;
70import org.junit.Before;
71import org.junit.Test;
72import org.junit.runner.RunWith;
73import org.mockito.Mock;
74import org.mockito.MockitoAnnotations;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070075import org.mockito.Spy;
Robert Berrye16fa982017-12-20 15:59:37 +000076
77import java.io.File;
78import java.nio.charset.StandardCharsets;
Bo Zhu7c1972f2018-02-22 21:43:52 -080079import java.security.cert.CertPath;
80import java.security.cert.CertificateFactory;
81import java.security.cert.X509Certificate;
82import java.util.ArrayList;
Dmitry Dementyevad884712017-12-20 12:38:36 -080083import java.util.Map;
Robert Berryb9a220b2017-12-21 12:41:01 +000084import java.util.Random;
Brett Chabota26eda92018-07-23 13:08:30 -070085import java.util.concurrent.Executors;
Robert Berryb9a220b2017-12-21 12:41:01 +000086
Bo Zhu3462c832018-01-04 22:42:36 -080087import javax.crypto.KeyGenerator;
Robert Berryb9a220b2017-12-21 12:41:01 +000088import javax.crypto.SecretKey;
89import javax.crypto.spec.SecretKeySpec;
Robert Berrye16fa982017-12-20 15:59:37 +000090
91@SmallTest
92@RunWith(AndroidJUnit4.class)
93public class RecoverableKeyStoreManagerTest {
94 private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
95
Bo Zhub31ab672018-03-20 22:44:18 -070096 private static final String ROOT_CERTIFICATE_ALIAS = "";
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070097 private static final String DEFAULT_ROOT_CERT_ALIAS =
98 TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070099 private static final String INSECURE_CERTIFICATE_ALIAS =
100 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS;
Robert Berrye16fa982017-12-20 15:59:37 +0000101 private static final String TEST_SESSION_ID = "karlin";
Bo Zhubd7879c2018-05-11 05:10:28 +0000102 private static final byte[] TEST_PUBLIC_KEY = TestData.CERT_1_PUBLIC_KEY.getEncoded();
Robert Berrye16fa982017-12-20 15:59:37 +0000103 private static final byte[] TEST_SALT = getUtf8Bytes("salt");
104 private static final byte[] TEST_SECRET = getUtf8Bytes("password1234");
105 private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge");
Bo Zhudef7ffd2018-01-05 14:50:52 -0800106 private static final byte[] TEST_VAULT_PARAMS = new byte[] {
107 // backend_key
Bo Zhubd7879c2018-05-11 05:10:28 +0000108 (byte) 0x04, (byte) 0x8e, (byte) 0x0c, (byte) 0x11, (byte) 0x4a, (byte) 0x79, (byte) 0x20,
109 (byte) 0x7c, (byte) 0x00, (byte) 0x4c, (byte) 0xd7, (byte) 0xe9, (byte) 0x06, (byte) 0xe2,
110 (byte) 0x58, (byte) 0x21, (byte) 0x45, (byte) 0xfa, (byte) 0x24, (byte) 0xcb, (byte) 0x07,
111 (byte) 0x66, (byte) 0xde, (byte) 0xfd, (byte) 0xf1, (byte) 0x83, (byte) 0xb4, (byte) 0x26,
112 (byte) 0x55, (byte) 0x98, (byte) 0xcb, (byte) 0xa9, (byte) 0xd5, (byte) 0x55, (byte) 0xad,
113 (byte) 0x65, (byte) 0xc5, (byte) 0xff, (byte) 0x5c, (byte) 0xfb, (byte) 0x1c, (byte) 0x4e,
114 (byte) 0x34, (byte) 0x98, (byte) 0x7e, (byte) 0x4f, (byte) 0x96, (byte) 0xa2, (byte) 0xa3,
115 (byte) 0x7e, (byte) 0xf4, (byte) 0x46, (byte) 0x52, (byte) 0x04, (byte) 0xba, (byte) 0x2a,
116 (byte) 0xb9, (byte) 0x47, (byte) 0xbb, (byte) 0xc2, (byte) 0x1e, (byte) 0xdd, (byte) 0x15,
117 (byte) 0x1a, (byte) 0xc0,
Bo Zhudef7ffd2018-01-05 14:50:52 -0800118 // counter_id
119 (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x00, (byte) 0x00, (byte) 0x00,
120 (byte) 0x00,
121 // device_parameter
122 (byte) 0x78, (byte) 0x56, (byte) 0x34, (byte) 0x12, (byte) 0x00, (byte) 0x00, (byte) 0x00,
123 (byte) 0x0,
124 // max_attempts
125 (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00};
Bo Zhu3462c832018-01-04 22:42:36 -0800126 private static final int TEST_GENERATION_ID = 2;
127 private static final int TEST_USER_ID = 10009;
Robert Berrye16fa982017-12-20 15:59:37 +0000128 private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
Robert Berryb9a220b2017-12-21 12:41:01 +0000129 private static final byte[] RECOVERY_RESPONSE_HEADER =
130 "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
131 private static final String TEST_ALIAS = "nick";
Bo Zhu4857cb52018-02-06 14:34:48 -0800132 private static final String TEST_ALIAS2 = "bob";
Robert Berrycfc990a2017-12-22 15:54:30 +0000133 private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
Bo Zhu2c8e5382018-02-26 15:54:25 -0800134 private static final int APPLICATION_KEY_SIZE_BYTES = 32;
Dmitry Dementyevad884712017-12-20 12:38:36 -0800135 private static final int GENERATION_ID = 1;
136 private static final byte[] NONCE = getUtf8Bytes("nonce");
137 private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
Bo Zhu7ebcd662019-01-04 17:00:58 -0800138 private static final byte[] KEY_METADATA_NULL = null;
139 private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata");
Bo Zhu3462c832018-01-04 22:42:36 -0800140 private static final String KEY_ALGORITHM = "AES";
141 private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
142 private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700143 private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = "";
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700144 private static final KeyChainProtectionParams TEST_PROTECTION_PARAMS =
145 new KeyChainProtectionParams.Builder()
146 .setUserSecretType(TYPE_LOCKSCREEN)
147 .setLockScreenUiFormat(UI_FORMAT_PASSWORD)
148 .setKeyDerivationParams(KeyDerivationParams.createSha256Params(TEST_SALT))
149 .setSecret(TEST_SECRET)
150 .build();
Dmitry Dementyevad884712017-12-20 12:38:36 -0800151
Robert Berrye16fa982017-12-20 15:59:37 +0000152 @Mock private Context mMockContext;
Robert Berry91044042017-12-27 12:05:58 +0000153 @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
Robert Berrycfc990a2017-12-22 15:54:30 +0000154 @Mock private KeyguardManager mKeyguardManager;
Bo Zhu3462c832018-01-04 22:42:36 -0800155 @Mock private PlatformKeyManager mPlatformKeyManager;
Dmitry Dementyev29b9de52018-01-31 16:09:32 -0800156 @Mock private ApplicationKeyStorage mApplicationKeyStorage;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700157 @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
Robert Berrye16fa982017-12-20 15:59:37 +0000158
159 private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
160 private File mDatabaseFile;
161 private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
162 private RecoverySessionStorage mRecoverySessionStorage;
Robert Berrybd086f12017-12-27 13:29:39 +0000163 private RecoverySnapshotStorage mRecoverySnapshotStorage;
Bo Zhu3462c832018-01-04 22:42:36 -0800164 private PlatformEncryptionKey mPlatformEncryptionKey;
Robert Berrye16fa982017-12-20 15:59:37 +0000165
166 @Before
Bo Zhu3462c832018-01-04 22:42:36 -0800167 public void setUp() throws Exception {
Robert Berrye16fa982017-12-20 15:59:37 +0000168 MockitoAnnotations.initMocks(this);
169
170 Context context = InstrumentationRegistry.getTargetContext();
171 mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
172 mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
Robert Berrycfc990a2017-12-22 15:54:30 +0000173
Robert Berrye16fa982017-12-20 15:59:37 +0000174 mRecoverySessionStorage = new RecoverySessionStorage();
Robert Berrycfc990a2017-12-22 15:54:30 +0000175
176 when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
177 when(mMockContext.getSystemServiceName(any())).thenReturn("test");
Robert Berry3ae5bea2017-12-27 10:58:03 +0000178 when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
Bo Zhu3462c832018-01-04 22:42:36 -0800179 when(mKeyguardManager.isDeviceSecure(TEST_USER_ID)).thenReturn(true);
180
181 mPlatformEncryptionKey =
182 new PlatformEncryptionKey(TEST_GENERATION_ID, generateAndroidKeyStoreKey());
183 when(mPlatformKeyManager.getEncryptKey(anyInt())).thenReturn(mPlatformEncryptionKey);
Robert Berrycfc990a2017-12-22 15:54:30 +0000184
Robert Berrye16fa982017-12-20 15:59:37 +0000185 mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
186 mMockContext,
187 mRecoverableKeyStoreDb,
Robert Berry4a534ec2017-12-21 15:44:02 +0000188 mRecoverySessionStorage,
Dmitry Dementyev3b17c632017-12-21 17:30:48 -0800189 Executors.newSingleThreadExecutor(),
Robert Berry91044042017-12-27 12:05:58 +0000190 mRecoverySnapshotStorage,
Bo Zhu3462c832018-01-04 22:42:36 -0800191 mMockListenersStorage,
Dmitry Dementyev29b9de52018-01-31 16:09:32 -0800192 mPlatformKeyManager,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700193 mApplicationKeyStorage,
194 mTestOnlyInsecureCertificateHelper);
Robert Berrye16fa982017-12-20 15:59:37 +0000195 }
196
197 @After
198 public void tearDown() {
199 mRecoverableKeyStoreDb.close();
200 mDatabaseFile.delete();
201 }
202
203 @Test
Bo Zhu2c8e5382018-02-26 15:54:25 -0800204 public void importKey_storesTheKey() throws Exception {
205 int uid = Binder.getCallingUid();
206 int userId = UserHandle.getCallingUserId();
207 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
208
209 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
210
211 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
212 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
213 }
214
215 @Test
216 public void importKey_throwsIfInvalidLength() throws Exception {
217 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
218 try {
219 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
220 fail("should have thrown");
221 } catch (ServiceSpecificException e) {
222 assertThat(e.getMessage()).contains("not contain 256 bits");
223 }
224 }
225
226 @Test
227 public void importKey_throwsIfNullKey() throws Exception {
228 try {
229 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
230 fail("should have thrown");
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700231 } catch (NullPointerException e) {
232 assertThat(e.getMessage()).contains("is null");
Bo Zhu2c8e5382018-02-26 15:54:25 -0800233 }
234 }
235
236 @Test
Bo Zhu7ebcd662019-01-04 17:00:58 -0800237 public void importKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
238 int uid = Binder.getCallingUid();
239 int userId = UserHandle.getCallingUserId();
240 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
241
242 mRecoverableKeyStoreManager.importKeyWithMetadata(
243 TEST_ALIAS, keyMaterial, KEY_METADATA_NULL);
244
245 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
246 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
247 }
248
249 @Test
250 public void importKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
251 int uid = Binder.getCallingUid();
252 int userId = UserHandle.getCallingUserId();
253 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
254
255 mRecoverableKeyStoreManager.importKeyWithMetadata(
256 TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
257
258 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
259 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
260 }
261
262 @Test
263 public void importKeyWithMetadata_throwsIfInvalidLength() throws Exception {
264 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
265 try {
266 mRecoverableKeyStoreManager.importKeyWithMetadata(
267 TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
268 fail("should have thrown");
269 } catch (ServiceSpecificException e) {
270 assertThat(e.getMessage()).contains("not contain 256 bits");
271 }
272 }
273
274 @Test
275 public void importKeyWithMetadata_throwsIfNullKey() throws Exception {
276 try {
277 mRecoverableKeyStoreManager.importKeyWithMetadata(
278 TEST_ALIAS, /*keyBytes=*/ null, KEY_METADATA_NON_NULL);
279 fail("should have thrown");
280 } catch (NullPointerException e) {
281 assertThat(e.getMessage()).contains("is null");
282 }
283 }
284
285 @Test
286 public void generateKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
287 int uid = Binder.getCallingUid();
288 int userId = UserHandle.getCallingUserId();
289
290 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
291
292 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
293 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
294 }
295
296 @Test
297 public void generateKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
298 int uid = Binder.getCallingUid();
299 int userId = UserHandle.getCallingUserId();
300
301 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NON_NULL);
302
303 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
304 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
305 }
306
307 @Test
Robert Berry5daccec2018-01-06 19:16:25 +0000308 public void removeKey_removesAKey() throws Exception {
309 int uid = Binder.getCallingUid();
Dmitry Dementyev86f5bb12018-03-27 16:58:50 -0700310 mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
Robert Berry5daccec2018-01-06 19:16:25 +0000311
312 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
313
314 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNull();
315 }
316
317 @Test
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800318 public void removeKey_updatesShouldCreateSnapshot() throws Exception {
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800319 int uid = Binder.getCallingUid();
320 int userId = UserHandle.getCallingUserId();
Dmitry Dementyev86f5bb12018-03-27 16:58:50 -0700321 mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800322 // Pretend that key was synced
323 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
324
325 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
326
327 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
328 }
329
330 @Test
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800331 public void removeKey_failureDoesNotUpdateShouldCreateSnapshot() throws Exception {
332 int uid = Binder.getCallingUid();
333 int userId = UserHandle.getCallingUserId();
334 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
335 // Key did not exist
336 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
337
338 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
339 }
340
341 @Test
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800342 public void initRecoveryService_succeedsWithCertFile() throws Exception {
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800343 int uid = Binder.getCallingUid();
344 int userId = UserHandle.getCallingUserId();
Bo Zhu14d993d2018-02-03 21:38:48 -0800345 long certSerial = 1000L;
346 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
347
348 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
349 TestData.getCertXmlWithSerial(certSerial));
350
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700351 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
352 .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS);
353
Robert Berrye8edf972018-03-27 11:45:11 +0100354 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700355 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
356 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
357 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
358 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial);
Bo Zhu14d993d2018-02-03 21:38:48 -0800359 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
360 }
361
362 @Test
Dmitry Dementyev925f0262018-04-12 12:10:50 -0700363 public void initRecoveryService_updatesShouldCreatesnapshotOnCertUpdate() throws Exception {
364 int uid = Binder.getCallingUid();
365 int userId = UserHandle.getCallingUserId();
366 long certSerial = 1000L;
367 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
368
369 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
370 TestData.getCertXmlWithSerial(certSerial));
371
372 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
373
374 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
375 TestData.getCertXmlWithSerial(certSerial + 1));
376
377 // Since there were no recoverable keys, new snapshot will not be created.
378 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
379
380 generateKeyAndSimulateSync(userId, uid, 10);
381
382 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
383 TestData.getCertXmlWithSerial(certSerial + 2));
384
385 // Since there were a recoverable key, new serial number triggers snapshot creation
386 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
387 }
388
389 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700390 public void initRecoveryService_triesToFilterRootAlias() throws Exception {
391 int uid = Binder.getCallingUid();
392 int userId = UserHandle.getCallingUserId();
393 long certSerial = 1000L;
394 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
395
396 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
397 TestData.getCertXmlWithSerial(certSerial));
398
399 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
400 .getDefaultCertificateAliasIfEmpty(eq(ROOT_CERTIFICATE_ALIAS));
401
402 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
403 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
404
405 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
406 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
407
408 }
409
410 @Test
411 public void initRecoveryService_usesProdCertificateForEmptyRootAlias() throws Exception {
412 int uid = Binder.getCallingUid();
413 int userId = UserHandle.getCallingUserId();
414 long certSerial = 1000L;
415 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
416
417 mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ "",
418 TestData.getCertXmlWithSerial(certSerial));
419
420 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
421 .getDefaultCertificateAliasIfEmpty(eq(""));
422
423 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
424 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
425
426 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
427 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
428 }
429
430 @Test
431 public void initRecoveryService_usesProdCertificateForNullRootAlias() throws Exception {
432 int uid = Binder.getCallingUid();
433 int userId = UserHandle.getCallingUserId();
434 long certSerial = 1000L;
435 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
436
437 mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ null,
438 TestData.getCertXmlWithSerial(certSerial));
439
440 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
441 .getDefaultCertificateAliasIfEmpty(null);
442
443 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
444 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
445
446 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
447 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
448 }
449
450 @Test
Bo Zhu8d6861e2018-03-21 20:45:09 -0700451 public void initRecoveryService_regeneratesCounterId() throws Exception {
452 int uid = Binder.getCallingUid();
453 int userId = UserHandle.getCallingUserId();
454 long certSerial = 1000L;
455
456 Long counterId0 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
457 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
458 TestData.getCertXmlWithSerial(certSerial));
459 Long counterId1 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
460 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
461 TestData.getCertXmlWithSerial(certSerial + 1));
462 Long counterId2 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
463
464 assertThat(!counterId1.equals(counterId0) || !counterId2.equals(counterId1)).isTrue();
465 }
466
467 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800468 public void initRecoveryService_throwsIfInvalidCert() throws Exception {
469 byte[] modifiedCertXml = TestData.getCertXml();
470 modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate
471 try {
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700472 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
473 modifiedCertXml);
Bo Zhu7f414d92018-02-28 09:28:19 -0800474 fail("should have thrown");
475 } catch (ServiceSpecificException e) {
Bo Zhu41d2dd22018-03-30 12:20:06 -0700476 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE);
Bo Zhu7f414d92018-02-28 09:28:19 -0800477 }
478 }
479
480 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800481 public void initRecoveryService_updatesWithLargerSerial() throws Exception {
482 int uid = Binder.getCallingUid();
483 int userId = UserHandle.getCallingUserId();
484 long certSerial = 1000L;
485
486 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
487 TestData.getCertXmlWithSerial(certSerial));
488 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
489 TestData.getCertXmlWithSerial(certSerial + 1));
490
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700491 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
492 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1);
Dmitry Dementyev925f0262018-04-12 12:10:50 -0700493 // There were no keys.
494 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Bo Zhu14d993d2018-02-03 21:38:48 -0800495 }
496
497 @Test
Aseem Kumar23174b72018-04-03 11:35:51 -0700498 public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception {
Bo Zhu14d993d2018-02-03 21:38:48 -0800499 int uid = Binder.getCallingUid();
500 int userId = UserHandle.getCallingUserId();
501 long certSerial = 1000L;
502
503 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
504 TestData.getCertXmlWithSerial(certSerial));
Aseem Kumar23174b72018-04-03 11:35:51 -0700505 try {
506 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
507 TestData.getCertXmlWithSerial(certSerial - 1));
508 fail();
509 } catch (ServiceSpecificException e) {
510 assertThat(e.errorCode).isEqualTo(ERROR_DOWNGRADE_CERTIFICATE);
511 }
Bo Zhu14d993d2018-02-03 21:38:48 -0800512 }
513
514 @Test
Bo Zhub10ba442018-03-31 17:13:46 -0700515 public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception {
516 int uid = Binder.getCallingUid();
517 int userId = UserHandle.getCallingUserId();
518 int certSerial = 3333;
519
520 String testRootCertAlias = TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS;
521
522 mRecoverableKeyStoreManager.initRecoveryService(testRootCertAlias,
523 TestData.getInsecureCertXmlBytesWithEndpoint1(certSerial));
524 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
525 testRootCertAlias)).isEqualTo(certSerial);
526 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
527 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint1());
528
529 mRecoverableKeyStoreManager.initRecoveryService(testRootCertAlias,
530 TestData.getInsecureCertXmlBytesWithEndpoint2(certSerial - 1));
531 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
532 testRootCertAlias)).isEqualTo(certSerial - 1);
533 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
534 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2());
535 }
536
537 @Test
538 public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception {
539 int uid = Binder.getCallingUid();
540 int userId = UserHandle.getCallingUserId();
541
542 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
543 TestData.getCertXmlWithSerial(1111L));
544 mRecoverableKeyStoreManager.initRecoveryService(
545 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
546 TestData.getInsecureCertXmlBytesWithEndpoint1(2222));
547
548 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
549 ROOT_CERTIFICATE_ALIAS)).isEqualTo(1111L);
550 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
551 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isEqualTo(2222L);
552
553 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
554 ROOT_CERTIFICATE_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
555 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
556 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isEqualTo(
557 TestData.getInsecureCertPathForEndpoint1());
558 }
559
560 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800561 public void initRecoveryService_ignoresTheSameSerial() throws Exception {
562 int uid = Binder.getCallingUid();
563 int userId = UserHandle.getCallingUserId();
564 long certSerial = 1000L;
565
566 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
567 TestData.getCertXmlWithSerial(certSerial));
Dmitry Dementyev925f0262018-04-12 12:10:50 -0700568
569 generateKeyAndSimulateSync(userId, uid, 10);
570
Bo Zhu14d993d2018-02-03 21:38:48 -0800571 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
572 TestData.getCertXmlWithSerial(certSerial));
573
Bo Zhu14d993d2018-02-03 21:38:48 -0800574 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
575 }
576
577 @Test
Bo Zhuc98c8432018-03-31 13:08:49 -0700578 public void initRecoveryService_throwsIfRawPublicKey() throws Exception {
Bo Zhu14d993d2018-02-03 21:38:48 -0800579 int uid = Binder.getCallingUid();
580 int userId = UserHandle.getCallingUserId();
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800581 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
582
Bo Zhuc98c8432018-03-31 13:08:49 -0700583 try {
584 mRecoverableKeyStoreManager
585 .initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
586 fail("should have thrown");
587 } catch (ServiceSpecificException e) {
588 assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT);
589 }
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800590
Bo Zhuc98c8432018-03-31 13:08:49 -0700591 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700592 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
593 DEFAULT_ROOT_CERT_ALIAS)).isNull();
594 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
595 DEFAULT_ROOT_CERT_ALIAS)).isNull();
Bo Zhuc98c8432018-03-31 13:08:49 -0700596 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800597 }
598
599 @Test
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700600 public void initRecoveryService_throwsIfUnknownRootCertAlias() throws Exception {
601 try {
602 mRecoverableKeyStoreManager.initRecoveryService(
603 "unknown-root-cert-alias", TestData.getCertXml());
604 fail("should have thrown");
605 } catch (ServiceSpecificException e) {
606 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE);
607 }
608 }
609
610 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800611 public void initRecoveryServiceWithSigFile_succeeds() throws Exception {
612 int uid = Binder.getCallingUid();
613 int userId = UserHandle.getCallingUserId();
614 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
615
616 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
617 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
618
Robert Berrye8edf972018-03-27 11:45:11 +0100619 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700620 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
621 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
Bo Zhu7f414d92018-02-28 09:28:19 -0800622 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
623 }
624
625 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700626 public void initRecoveryServiceWithSigFile_usesProdCertificateForNullRootAlias()
627 throws Exception {
628 int uid = Binder.getCallingUid();
629 int userId = UserHandle.getCallingUserId();
630 long certSerial = 1000L;
631 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
632
633 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
634 /*rootCertificateAlias=*/null, TestData.getCertXml(), TestData.getSigXml());
635
636 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
637 .getDefaultCertificateAliasIfEmpty(null);
638
639 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
640 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
641 }
642
643 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800644 public void initRecoveryServiceWithSigFile_throwsIfNullCertFile() throws Exception {
645 try {
646 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
647 ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null,
648 TestData.getSigXml());
649 fail("should have thrown");
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700650 } catch (NullPointerException e) {
Bo Zhu7f414d92018-02-28 09:28:19 -0800651 assertThat(e.getMessage()).contains("is null");
652 }
653 }
654
655 @Test
656 public void initRecoveryServiceWithSigFile_throwsIfNullSigFile() throws Exception {
657 try {
658 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
659 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
660 /*recoveryServiceSigFile=*/ null);
661 fail("should have thrown");
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700662 } catch (NullPointerException e) {
Bo Zhu7f414d92018-02-28 09:28:19 -0800663 assertThat(e.getMessage()).contains("is null");
664 }
665 }
666
667 @Test
668 public void initRecoveryServiceWithSigFile_throwsIfWrongSigFileFormat() throws Exception {
669 try {
670 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
671 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
672 getUtf8Bytes("wrong-sig-file-format"));
673 fail("should have thrown");
674 } catch (ServiceSpecificException e) {
Bo Zhu41d2dd22018-03-30 12:20:06 -0700675 assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT);
Bo Zhu7f414d92018-02-28 09:28:19 -0800676 }
677 }
678
679 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700680 public void initRecoveryServiceWithSigFile_throwsIfTestAliasUsedWithProdCert()
681 throws Exception {
682 try {
683 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
684 INSECURE_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
685 fail("should have thrown");
686 } catch (ServiceSpecificException e) {
Bo Zhu41d2dd22018-03-30 12:20:06 -0700687 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE);
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700688 }
689 }
690
691 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800692 public void initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature() throws Exception {
693 byte[] modifiedCertXml = TestData.getCertXml();
694 modifiedCertXml[modifiedCertXml.length - 1] = 0; // Change the last new line char to a zero
695 try {
696 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
697 ROOT_CERTIFICATE_ALIAS, modifiedCertXml, TestData.getSigXml());
698 fail("should have thrown");
699 } catch (ServiceSpecificException e) {
700 assertThat(e.getMessage()).contains("is invalid");
701 }
702 }
703
704 @Test
Robert Berrye16fa982017-12-20 15:59:37 +0000705 public void startRecoverySession_checksPermissionFirst() throws Exception {
706 mRecoverableKeyStoreManager.startRecoverySession(
707 TEST_SESSION_ID,
708 TEST_PUBLIC_KEY,
709 TEST_VAULT_PARAMS,
710 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700711 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berrye16fa982017-12-20 15:59:37 +0000712
Dmitry Dementyevad884712017-12-20 12:38:36 -0800713 verify(mMockContext, times(1))
714 .enforceCallingOrSelfPermission(
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800715 eq(Manifest.permission.RECOVER_KEYSTORE), any());
Robert Berrye16fa982017-12-20 15:59:37 +0000716 }
717
718 @Test
Bo Zhu7c1972f2018-02-22 21:43:52 -0800719 public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
720 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
721 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700722 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800723 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
724 TEST_VAULT_PARAMS,
725 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700726 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800727
728 assertEquals(1, mRecoverySessionStorage.size());
729 RecoverySessionStorage.Entry entry =
730 mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
731 assertArrayEquals(TEST_SECRET, entry.getLskfHash());
732 assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
733 }
734
735 @Test
736 public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
737 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
738 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700739 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800740 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
741 TEST_VAULT_PARAMS,
742 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700743 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800744
745 verify(mMockContext, times(2))
746 .enforceCallingOrSelfPermission(
747 eq(Manifest.permission.RECOVER_KEYSTORE), any());
748 }
749
750 @Test
Robert Berrye16fa982017-12-20 15:59:37 +0000751 public void startRecoverySession_storesTheSessionInfo() throws Exception {
752 mRecoverableKeyStoreManager.startRecoverySession(
753 TEST_SESSION_ID,
754 TEST_PUBLIC_KEY,
755 TEST_VAULT_PARAMS,
756 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700757 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berrye16fa982017-12-20 15:59:37 +0000758
759 assertEquals(1, mRecoverySessionStorage.size());
Dmitry Dementyevad884712017-12-20 12:38:36 -0800760 RecoverySessionStorage.Entry entry =
Dmitry Dementyev14298312018-01-04 15:19:19 -0800761 mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
Robert Berrye16fa982017-12-20 15:59:37 +0000762 assertArrayEquals(TEST_SECRET, entry.getLskfHash());
763 assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
764 }
765
766 @Test
Robert Berry2bcdad92018-01-18 12:53:29 +0000767 public void closeSession_closesASession() throws Exception {
768 mRecoverableKeyStoreManager.startRecoverySession(
769 TEST_SESSION_ID,
770 TEST_PUBLIC_KEY,
771 TEST_VAULT_PARAMS,
772 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700773 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berry2bcdad92018-01-18 12:53:29 +0000774
775 mRecoverableKeyStoreManager.closeSession(TEST_SESSION_ID);
776
777 assertEquals(0, mRecoverySessionStorage.size());
778 }
779
780 @Test
781 public void closeSession_doesNotCloseUnrelatedSessions() throws Exception {
782 mRecoverableKeyStoreManager.startRecoverySession(
783 TEST_SESSION_ID,
784 TEST_PUBLIC_KEY,
785 TEST_VAULT_PARAMS,
786 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700787 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berry2bcdad92018-01-18 12:53:29 +0000788
789 mRecoverableKeyStoreManager.closeSession("some random session");
790
791 assertEquals(1, mRecoverySessionStorage.size());
792 }
793
794 @Test
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700795 public void closeSession_throwsIfNullSession() throws Exception {
796 try {
797 mRecoverableKeyStoreManager.closeSession(/*sessionId=*/ null);
798 fail("should have thrown");
799 } catch (NullPointerException e) {
800 assertThat(e.getMessage()).contains("invalid");
801 }
802 }
803
804 @Test
Robert Berrye16fa982017-12-20 15:59:37 +0000805 public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception {
806 try {
807 mRecoverableKeyStoreManager.startRecoverySession(
808 TEST_SESSION_ID,
809 TEST_PUBLIC_KEY,
810 TEST_VAULT_PARAMS,
811 TEST_VAULT_CHALLENGE,
Dmitry Dementyev14298312018-01-04 15:19:19 -0800812 ImmutableList.of());
Robert Berryb9a220b2017-12-21 12:41:01 +0000813 fail("should have thrown");
Dmitry Dementyevae6ec6d2018-01-18 14:29:49 -0800814 } catch (UnsupportedOperationException e) {
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800815 assertThat(e.getMessage()).startsWith(
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800816 "Only a single KeyChainProtectionParams is supported");
Robert Berrye16fa982017-12-20 15:59:37 +0000817 }
818 }
819
820 @Test
Bo Zhudef7ffd2018-01-05 14:50:52 -0800821 public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception {
822 byte[] vaultParams = TEST_VAULT_PARAMS.clone();
823 vaultParams[1] ^= (byte) 1; // Flip 1 bit
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700824
Bo Zhudef7ffd2018-01-05 14:50:52 -0800825 try {
826 mRecoverableKeyStoreManager.startRecoverySession(
827 TEST_SESSION_ID,
828 TEST_PUBLIC_KEY,
829 vaultParams,
830 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700831 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhudef7ffd2018-01-05 14:50:52 -0800832 fail("should have thrown");
833 } catch (ServiceSpecificException e) {
834 assertThat(e.getMessage()).contains("do not match");
835 }
836 }
837
838 @Test
Bo Zhu7c1972f2018-02-22 21:43:52 -0800839 public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception {
840 try {
841 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
842 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700843 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800844 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
845 TEST_VAULT_PARAMS,
846 TEST_VAULT_CHALLENGE,
847 ImmutableList.of());
848 fail("should have thrown");
849 } catch (UnsupportedOperationException e) {
850 assertThat(e.getMessage()).startsWith(
851 "Only a single KeyChainProtectionParams is supported");
852 }
853 }
854
855 @Test
856 public void startRecoverySessionWithCertPath_throwsIfPublicKeysMismatch() throws Exception {
857 byte[] vaultParams = TEST_VAULT_PARAMS.clone();
858 vaultParams[1] ^= (byte) 1; // Flip 1 bit
859 try {
860 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
861 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700862 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800863 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
864 vaultParams,
865 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700866 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800867 fail("should have thrown");
868 } catch (ServiceSpecificException e) {
869 assertThat(e.getMessage()).contains("do not match");
870 }
871 }
872
873 @Test
874 public void startRecoverySessionWithCertPath_throwsIfEmptyCertPath() throws Exception {
875 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
876 CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>());
877 try {
878 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
879 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700880 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800881 RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
882 TEST_VAULT_PARAMS,
883 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700884 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800885 fail("should have thrown");
886 } catch (ServiceSpecificException e) {
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800887 assertThat(e.getMessage()).contains("empty");
888 }
889 }
890
891 @Test
892 public void startRecoverySessionWithCertPath_throwsIfInvalidCertPath() throws Exception {
893 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
894 CertPath shortCertPath = certFactory.generateCertPath(
895 TestData.CERT_PATH_1.getCertificates()
896 .subList(0, TestData.CERT_PATH_1.getCertificates().size() - 1));
897 try {
898 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
899 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700900 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800901 RecoveryCertPath.createRecoveryCertPath(shortCertPath),
902 TEST_VAULT_PARAMS,
903 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700904 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800905 fail("should have thrown");
906 } catch (ServiceSpecificException e) {
907 // expected
Bo Zhu7c1972f2018-02-22 21:43:52 -0800908 }
909 }
910
911 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +0000912 public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +0000913 try {
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700914 WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder()
915 .setAlias(TEST_ALIAS)
916 .setEncryptedKeyMaterial(randomBytes(32))
917 .build();
Robert Berry4a5c87d2018-03-19 18:00:46 +0000918 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +0000919 TEST_SESSION_ID,
920 /*recoveryKeyBlob=*/ randomBytes(32),
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700921 /*applicationKeys=*/ ImmutableList.of(applicationKey));
Robert Berryb9a220b2017-12-21 12:41:01 +0000922 fail("should have thrown");
Dmitry Dementyev14298312018-01-04 15:19:19 -0800923 } catch (ServiceSpecificException e) {
924 // expected
Robert Berryb9a220b2017-12-21 12:41:01 +0000925 }
926 }
927
928 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +0000929 public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +0000930 mRecoverableKeyStoreManager.startRecoverySession(
931 TEST_SESSION_ID,
932 TEST_PUBLIC_KEY,
933 TEST_VAULT_PARAMS,
934 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700935 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berryb9a220b2017-12-21 12:41:01 +0000936
937 try {
Robert Berry4a5c87d2018-03-19 18:00:46 +0000938 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +0000939 TEST_SESSION_ID,
940 /*encryptedRecoveryKey=*/ randomBytes(60),
Dmitry Dementyev14298312018-01-04 15:19:19 -0800941 /*applicationKeys=*/ ImmutableList.of());
Robert Berryb9a220b2017-12-21 12:41:01 +0000942 fail("should have thrown");
Dmitry Dementyev14298312018-01-04 15:19:19 -0800943 } catch (ServiceSpecificException e) {
944 assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key");
Robert Berryb9a220b2017-12-21 12:41:01 +0000945 }
946 }
947
948 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700949 public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys()
950 throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +0000951 mRecoverableKeyStoreManager.startRecoverySession(
952 TEST_SESSION_ID,
953 TEST_PUBLIC_KEY,
954 TEST_VAULT_PARAMS,
955 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700956 ImmutableList.of(TEST_PROTECTION_PARAMS));
Dmitry Dementyev14298312018-01-04 15:19:19 -0800957 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
Robert Berryb9a220b2017-12-21 12:41:01 +0000958 .getKeyClaimant();
959 SecretKey recoveryKey = randomRecoveryKey();
960 byte[] encryptedClaimResponse = encryptClaimResponse(
961 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700962 WrappedApplicationKey badApplicationKey = new WrappedApplicationKey.Builder()
963 .setAlias(TEST_ALIAS)
964 .setEncryptedKeyMaterial(
965 encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)))
966 .build();
Robert Berryb9a220b2017-12-21 12:41:01 +0000967 try {
Robert Berry4a5c87d2018-03-19 18:00:46 +0000968 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +0000969 TEST_SESSION_ID,
970 /*encryptedRecoveryKey=*/ encryptedClaimResponse,
Dmitry Dementyev14298312018-01-04 15:19:19 -0800971 /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
Robert Berryb9a220b2017-12-21 12:41:01 +0000972 fail("should have thrown");
Bo Zhu3462c832018-01-04 22:42:36 -0800973 } catch (ServiceSpecificException e) {
Bo Zhu4857cb52018-02-06 14:34:48 -0800974 assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys");
Robert Berryb9a220b2017-12-21 12:41:01 +0000975 }
976 }
977
978 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +0000979 public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted()
980 throws Exception {
Bo Zhuae0682d2018-02-13 10:23:39 -0800981 mRecoverableKeyStoreManager.startRecoverySession(
982 TEST_SESSION_ID,
983 TEST_PUBLIC_KEY,
984 TEST_VAULT_PARAMS,
985 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700986 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhuae0682d2018-02-13 10:23:39 -0800987 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
988 .getKeyClaimant();
989 SecretKey recoveryKey = randomRecoveryKey();
990 byte[] encryptedClaimResponse = encryptClaimResponse(
991 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
992
Robert Berry4a5c87d2018-03-19 18:00:46 +0000993 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Bo Zhuae0682d2018-02-13 10:23:39 -0800994 TEST_SESSION_ID,
995 /*encryptedRecoveryKey=*/ encryptedClaimResponse,
996 /*applicationKeys=*/ ImmutableList.of());
997 }
998
999 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +00001000 public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +00001001 mRecoverableKeyStoreManager.startRecoverySession(
1002 TEST_SESSION_ID,
1003 TEST_PUBLIC_KEY,
1004 TEST_VAULT_PARAMS,
1005 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -07001006 ImmutableList.of(TEST_PROTECTION_PARAMS));
Dmitry Dementyev14298312018-01-04 15:19:19 -08001007 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
Robert Berryb9a220b2017-12-21 12:41:01 +00001008 .getKeyClaimant();
1009 SecretKey recoveryKey = randomRecoveryKey();
1010 byte[] encryptedClaimResponse = encryptClaimResponse(
1011 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
Robert Berrybd4c43c2017-12-22 11:35:14 +00001012 byte[] applicationKeyBytes = randomBytes(32);
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -07001013 WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder()
1014 .setAlias(TEST_ALIAS)
1015 .setEncryptedKeyMaterial(
1016 encryptedApplicationKey(recoveryKey, applicationKeyBytes))
1017 .build();
Robert Berryb9a220b2017-12-21 12:41:01 +00001018
Robert Berry4a5c87d2018-03-19 18:00:46 +00001019 Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +00001020 TEST_SESSION_ID,
1021 encryptedClaimResponse,
Dmitry Dementyev14298312018-01-04 15:19:19 -08001022 ImmutableList.of(applicationKey));
Robert Berrybd4c43c2017-12-22 11:35:14 +00001023
1024 assertThat(recoveredKeys).hasSize(1);
Robert Berry4a5c87d2018-03-19 18:00:46 +00001025 assertThat(recoveredKeys).containsKey(TEST_ALIAS);
Robert Berryb9a220b2017-12-21 12:41:01 +00001026 }
1027
Dmitry Dementyev3b17c632017-12-21 17:30:48 -08001028 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -07001029 public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails()
1030 throws Exception {
Bo Zhu4857cb52018-02-06 14:34:48 -08001031 mRecoverableKeyStoreManager.startRecoverySession(
1032 TEST_SESSION_ID,
1033 TEST_PUBLIC_KEY,
1034 TEST_VAULT_PARAMS,
1035 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -07001036 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu4857cb52018-02-06 14:34:48 -08001037 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
1038 .getKeyClaimant();
1039 SecretKey recoveryKey = randomRecoveryKey();
1040 byte[] encryptedClaimResponse = encryptClaimResponse(
1041 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
1042
1043 byte[] applicationKeyBytes1 = randomBytes(32);
1044 byte[] applicationKeyBytes2 = randomBytes(32);
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -07001045 WrappedApplicationKey applicationKey1 = new WrappedApplicationKey.Builder()
1046 .setAlias(TEST_ALIAS)
1047 // Use a different recovery key here, so the decryption will fail
1048 .setEncryptedKeyMaterial(
1049 encryptedApplicationKey(randomRecoveryKey(), applicationKeyBytes1))
1050 .build();
1051 WrappedApplicationKey applicationKey2 = new WrappedApplicationKey.Builder()
1052 .setAlias(TEST_ALIAS2)
1053 .setEncryptedKeyMaterial(
1054 encryptedApplicationKey(recoveryKey, applicationKeyBytes2))
1055 .build();
Bo Zhu4857cb52018-02-06 14:34:48 -08001056
Robert Berry4a5c87d2018-03-19 18:00:46 +00001057 Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Bo Zhu4857cb52018-02-06 14:34:48 -08001058 TEST_SESSION_ID,
1059 encryptedClaimResponse,
1060 ImmutableList.of(applicationKey1, applicationKey2));
1061
1062 assertThat(recoveredKeys).hasSize(1);
Robert Berry4a5c87d2018-03-19 18:00:46 +00001063 assertThat(recoveredKeys).containsKey(TEST_ALIAS2);
Bo Zhu4857cb52018-02-06 14:34:48 -08001064 }
1065
1066 @Test
Dmitry Dementyev3b17c632017-12-21 17:30:48 -08001067 public void setSnapshotCreatedPendingIntent() throws Exception {
1068 int uid = Binder.getCallingUid();
1069 PendingIntent intent = PendingIntent.getBroadcast(
1070 InstrumentationRegistry.getTargetContext(), /*requestCode=*/1,
1071 new Intent(), /*flags=*/ 0);
Dmitry Dementyev14298312018-01-04 15:19:19 -08001072 mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent);
Dmitry Dementyev3b17c632017-12-21 17:30:48 -08001073 verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class));
1074 }
1075
Dmitry Dementyevad884712017-12-20 12:38:36 -08001076 @Test
Robert Berry8f9038c2018-03-26 11:36:40 +01001077 public void setServerParams_updatesServerParams() throws Exception {
1078 int uid = Binder.getCallingUid();
1079 int userId = UserHandle.getCallingUserId();
1080 byte[] serverParams = new byte[] { 1 };
1081
1082 mRecoverableKeyStoreManager.setServerParams(serverParams);
1083
1084 assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(serverParams);
1085 }
1086
1087 @Test
1088 public void setServerParams_doesNotSetSnapshotPendingIfInitializing() throws Exception {
1089 int uid = Binder.getCallingUid();
1090 int userId = UserHandle.getCallingUserId();
1091 byte[] serverParams = new byte[] { 1 };
1092
1093 mRecoverableKeyStoreManager.setServerParams(serverParams);
1094
1095 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1096 }
1097
1098 @Test
1099 public void setServerParams_doesNotSetSnapshotPendingIfSettingSameValue() throws Exception {
1100 int uid = Binder.getCallingUid();
1101 int userId = UserHandle.getCallingUserId();
1102 byte[] serverParams = new byte[] { 1 };
1103
1104 mRecoverableKeyStoreManager.setServerParams(serverParams);
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001105
1106 generateKeyAndSimulateSync(userId, uid, 10);
1107
Robert Berry8f9038c2018-03-26 11:36:40 +01001108 mRecoverableKeyStoreManager.setServerParams(serverParams);
1109
1110 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1111 }
1112
1113 @Test
1114 public void setServerParams_setsSnapshotPendingIfUpdatingValue() throws Exception {
1115 int uid = Binder.getCallingUid();
1116 int userId = UserHandle.getCallingUserId();
1117
1118 mRecoverableKeyStoreManager.setServerParams(new byte[] { 1 });
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001119
1120 generateKeyAndSimulateSync(userId, uid, 10);
1121
Robert Berry8f9038c2018-03-26 11:36:40 +01001122 mRecoverableKeyStoreManager.setServerParams(new byte[] { 2 });
1123
1124 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
1125 }
1126
1127 @Test
Robert Berry5a1acefb2018-03-26 14:41:30 +01001128 public void setRecoverySecretTypes_updatesSecretTypes() throws Exception {
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001129 int[] types1 = new int[]{11, 2000};
1130 int[] types2 = new int[]{1, 2, 3};
1131 int[] types3 = new int[]{};
1132
Dmitry Dementyev14298312018-01-04 15:19:19 -08001133 mRecoverableKeyStoreManager.setRecoverySecretTypes(types1);
1134 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001135 types1);
1136
Dmitry Dementyev14298312018-01-04 15:19:19 -08001137 mRecoverableKeyStoreManager.setRecoverySecretTypes(types2);
1138 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001139 types2);
1140
Dmitry Dementyev14298312018-01-04 15:19:19 -08001141 mRecoverableKeyStoreManager.setRecoverySecretTypes(types3);
1142 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001143 types3);
1144 }
1145
1146 @Test
Robert Berry5a1acefb2018-03-26 14:41:30 +01001147 public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception {
1148 int uid = Binder.getCallingUid();
1149 int userId = UserHandle.getCallingUserId();
1150 int[] secretTypes = new int[] { 101 };
1151
1152 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
1153
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001154 // There were no keys.
Robert Berry5a1acefb2018-03-26 14:41:30 +01001155 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1156 }
1157
1158 @Test
1159 public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()
1160 throws Exception {
1161 int uid = Binder.getCallingUid();
1162 int userId = UserHandle.getCallingUserId();
1163 int[] secretTypes = new int[] { 101 };
1164
1165 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001166
1167 generateKeyAndSimulateSync(userId, uid, 10);
1168
Robert Berry5a1acefb2018-03-26 14:41:30 +01001169 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
1170
1171 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1172 }
1173
1174 @Test
1175 public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception {
1176 int uid = Binder.getCallingUid();
1177 int userId = UserHandle.getCallingUserId();
1178
1179 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 });
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001180
1181 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1182
1183 generateKeyAndSimulateSync(userId, uid, 10);
1184
Robert Berry5a1acefb2018-03-26 14:41:30 +01001185 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 });
1186
1187 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
1188 }
1189
1190 @Test
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -07001191 public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
1192 try {
1193 mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
1194 fail("should have thrown");
1195 } catch (NullPointerException e) {
1196 assertThat(e.getMessage()).contains("is null");
1197 }
1198 }
1199
1200 @Test
Dmitry Dementyev40dadb02018-01-10 18:03:37 -08001201 public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
1202 int uid = Binder.getCallingUid();
1203 int userId = UserHandle.getCallingUserId();
Robert Berrye8edf972018-03-27 11:45:11 +01001204 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 });
Dmitry Dementyev40dadb02018-01-10 18:03:37 -08001205
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001206 generateKeyAndSimulateSync(userId, uid, 10);
1207
Robert Berrye8edf972018-03-27 11:45:11 +01001208 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 });
Dmitry Dementyev40dadb02018-01-10 18:03:37 -08001209
1210 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
1211 }
1212
1213 @Test
Robert Berrybbe02ae2018-02-20 19:47:43 +00001214 public void setRecoveryStatus() throws Exception {
Dmitry Dementyevad884712017-12-20 12:38:36 -08001215 int userId = UserHandle.getCallingUserId();
1216 int uid = Binder.getCallingUid();
1217 int status = 100;
1218 int status2 = 200;
1219 String alias = "key1";
Bo Zhu7ebcd662019-01-04 17:00:58 -08001220 byte[] keyMetadata = null;
1221
1222 WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID,
1223 status);
Dmitry Dementyevad884712017-12-20 12:38:36 -08001224 mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
1225 Map<String, Integer> statuses =
Robert Berry56f06b42018-02-23 13:31:32 +00001226 mRecoverableKeyStoreManager.getRecoveryStatus();
Dmitry Dementyevad884712017-12-20 12:38:36 -08001227 assertThat(statuses).hasSize(1);
1228 assertThat(statuses).containsEntry(alias, status);
1229
Robert Berrybbe02ae2018-02-20 19:47:43 +00001230 mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2);
Robert Berry56f06b42018-02-23 13:31:32 +00001231 statuses = mRecoverableKeyStoreManager.getRecoveryStatus();
Dmitry Dementyevad884712017-12-20 12:38:36 -08001232 assertThat(statuses).hasSize(1);
1233 assertThat(statuses).containsEntry(alias, status2); // updated
1234 }
1235
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -07001236 @Test
1237 public void setRecoveryStatus_throwsIfNullAlias() throws Exception {
1238 try {
1239 mRecoverableKeyStoreManager.setRecoveryStatus(/*alias=*/ null, /*status=*/ 100);
1240 fail("should have thrown");
1241 } catch (NullPointerException e) {
1242 assertThat(e.getMessage()).contains("is null");
1243 }
1244 }
1245
Robert Berrybd4c43c2017-12-22 11:35:14 +00001246 private static byte[] encryptedApplicationKey(
1247 SecretKey recoveryKey, byte[] applicationKey) throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +00001248 return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
Bo Zhu7ebcd662019-01-04 17:00:58 -08001249 TEST_ALIAS,
1250 Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null)
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -07001251 )).get(TEST_ALIAS);
Robert Berryb9a220b2017-12-21 12:41:01 +00001252 }
1253
1254 private static byte[] encryptClaimResponse(
1255 byte[] keyClaimant,
1256 byte[] lskfHash,
1257 byte[] vaultParams,
1258 SecretKey recoveryKey) throws Exception {
1259 byte[] locallyEncryptedRecoveryKey = KeySyncUtils.locallyEncryptRecoveryKey(
1260 lskfHash, recoveryKey);
1261 return SecureBox.encrypt(
1262 /*theirPublicKey=*/ null,
1263 /*sharedSecret=*/ keyClaimant,
1264 /*header=*/ KeySyncUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
1265 /*payload=*/ locallyEncryptedRecoveryKey);
1266 }
1267
1268 private static SecretKey randomRecoveryKey() {
1269 return new SecretKeySpec(randomBytes(32), "AES");
1270 }
1271
Robert Berrye16fa982017-12-20 15:59:37 +00001272 private static byte[] getUtf8Bytes(String s) {
1273 return s.getBytes(StandardCharsets.UTF_8);
1274 }
Robert Berryb9a220b2017-12-21 12:41:01 +00001275
1276 private static byte[] randomBytes(int n) {
1277 byte[] bytes = new byte[n];
1278 new Random().nextBytes(bytes);
1279 return bytes;
1280 }
Bo Zhu3462c832018-01-04 22:42:36 -08001281
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001282 private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion)
1283 throws Exception{
Bo Zhu7ebcd662019-01-04 17:00:58 -08001284 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001285 // Simulate key sync.
1286 mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion);
1287 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
1288 }
1289
Bo Zhu3462c832018-01-04 22:42:36 -08001290 private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
1291 KeyGenerator keyGenerator = KeyGenerator.getInstance(
1292 KEY_ALGORITHM,
1293 ANDROID_KEY_STORE_PROVIDER);
1294 keyGenerator.init(new KeyGenParameterSpec.Builder(
1295 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
1296 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
1297 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
1298 .build());
1299 return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
1300 }
Robert Berrye16fa982017-12-20 15:59:37 +00001301}