blob: 5bab65c8b64275ae923207592f5e773bf82a6913 [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;
Dmitry Dementyev89f12d52019-02-28 12:26:01 -080062import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
Robert Berrye16fa982017-12-20 15:59:37 +000063import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
64import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
Robert Berrybd086f12017-12-27 13:29:39 +000065import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
Robert Berrye16fa982017-12-20 15:59:37 +000066
67import com.google.common.collect.ImmutableList;
Robert Berryb9a220b2017-12-21 12:41:01 +000068import com.google.common.collect.ImmutableMap;
Robert Berrye16fa982017-12-20 15:59:37 +000069
70import org.junit.After;
71import org.junit.Before;
72import org.junit.Test;
73import org.junit.runner.RunWith;
74import org.mockito.Mock;
75import org.mockito.MockitoAnnotations;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -070076import org.mockito.Spy;
Robert Berrye16fa982017-12-20 15:59:37 +000077
78import java.io.File;
79import java.nio.charset.StandardCharsets;
Bo Zhu7c1972f2018-02-22 21:43:52 -080080import java.security.cert.CertPath;
81import java.security.cert.CertificateFactory;
82import java.security.cert.X509Certificate;
83import java.util.ArrayList;
Dmitry Dementyevad884712017-12-20 12:38:36 -080084import java.util.Map;
Robert Berryb9a220b2017-12-21 12:41:01 +000085import java.util.Random;
Brett Chabota26eda92018-07-23 13:08:30 -070086import java.util.concurrent.Executors;
Robert Berryb9a220b2017-12-21 12:41:01 +000087
Bo Zhu3462c832018-01-04 22:42:36 -080088import javax.crypto.KeyGenerator;
Robert Berryb9a220b2017-12-21 12:41:01 +000089import javax.crypto.SecretKey;
90import javax.crypto.spec.SecretKeySpec;
Robert Berrye16fa982017-12-20 15:59:37 +000091
92@SmallTest
93@RunWith(AndroidJUnit4.class)
94public class RecoverableKeyStoreManagerTest {
95 private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
96
Bo Zhub31ab672018-03-20 22:44:18 -070097 private static final String ROOT_CERTIFICATE_ALIAS = "";
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070098 private static final String DEFAULT_ROOT_CERT_ALIAS =
99 TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700100 private static final String INSECURE_CERTIFICATE_ALIAS =
101 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS;
Robert Berrye16fa982017-12-20 15:59:37 +0000102 private static final String TEST_SESSION_ID = "karlin";
Bo Zhubd7879c2018-05-11 05:10:28 +0000103 private static final byte[] TEST_PUBLIC_KEY = TestData.CERT_1_PUBLIC_KEY.getEncoded();
Robert Berrye16fa982017-12-20 15:59:37 +0000104 private static final byte[] TEST_SALT = getUtf8Bytes("salt");
105 private static final byte[] TEST_SECRET = getUtf8Bytes("password1234");
106 private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge");
Bo Zhudef7ffd2018-01-05 14:50:52 -0800107 private static final byte[] TEST_VAULT_PARAMS = new byte[] {
108 // backend_key
Bo Zhubd7879c2018-05-11 05:10:28 +0000109 (byte) 0x04, (byte) 0x8e, (byte) 0x0c, (byte) 0x11, (byte) 0x4a, (byte) 0x79, (byte) 0x20,
110 (byte) 0x7c, (byte) 0x00, (byte) 0x4c, (byte) 0xd7, (byte) 0xe9, (byte) 0x06, (byte) 0xe2,
111 (byte) 0x58, (byte) 0x21, (byte) 0x45, (byte) 0xfa, (byte) 0x24, (byte) 0xcb, (byte) 0x07,
112 (byte) 0x66, (byte) 0xde, (byte) 0xfd, (byte) 0xf1, (byte) 0x83, (byte) 0xb4, (byte) 0x26,
113 (byte) 0x55, (byte) 0x98, (byte) 0xcb, (byte) 0xa9, (byte) 0xd5, (byte) 0x55, (byte) 0xad,
114 (byte) 0x65, (byte) 0xc5, (byte) 0xff, (byte) 0x5c, (byte) 0xfb, (byte) 0x1c, (byte) 0x4e,
115 (byte) 0x34, (byte) 0x98, (byte) 0x7e, (byte) 0x4f, (byte) 0x96, (byte) 0xa2, (byte) 0xa3,
116 (byte) 0x7e, (byte) 0xf4, (byte) 0x46, (byte) 0x52, (byte) 0x04, (byte) 0xba, (byte) 0x2a,
117 (byte) 0xb9, (byte) 0x47, (byte) 0xbb, (byte) 0xc2, (byte) 0x1e, (byte) 0xdd, (byte) 0x15,
118 (byte) 0x1a, (byte) 0xc0,
Bo Zhudef7ffd2018-01-05 14:50:52 -0800119 // counter_id
120 (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x00, (byte) 0x00, (byte) 0x00,
121 (byte) 0x00,
122 // device_parameter
123 (byte) 0x78, (byte) 0x56, (byte) 0x34, (byte) 0x12, (byte) 0x00, (byte) 0x00, (byte) 0x00,
124 (byte) 0x0,
125 // max_attempts
126 (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00};
Bo Zhu3462c832018-01-04 22:42:36 -0800127 private static final int TEST_GENERATION_ID = 2;
128 private static final int TEST_USER_ID = 10009;
Robert Berrye16fa982017-12-20 15:59:37 +0000129 private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
Robert Berryb9a220b2017-12-21 12:41:01 +0000130 private static final byte[] RECOVERY_RESPONSE_HEADER =
131 "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
132 private static final String TEST_ALIAS = "nick";
Bo Zhu4857cb52018-02-06 14:34:48 -0800133 private static final String TEST_ALIAS2 = "bob";
Robert Berrycfc990a2017-12-22 15:54:30 +0000134 private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
Bo Zhu2c8e5382018-02-26 15:54:25 -0800135 private static final int APPLICATION_KEY_SIZE_BYTES = 32;
Dmitry Dementyevad884712017-12-20 12:38:36 -0800136 private static final int GENERATION_ID = 1;
137 private static final byte[] NONCE = getUtf8Bytes("nonce");
138 private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
Bo Zhu7ebcd662019-01-04 17:00:58 -0800139 private static final byte[] KEY_METADATA_NULL = null;
140 private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata");
Bo Zhu3462c832018-01-04 22:42:36 -0800141 private static final String KEY_ALGORITHM = "AES";
142 private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
143 private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700144 private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = "";
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700145 private static final KeyChainProtectionParams TEST_PROTECTION_PARAMS =
146 new KeyChainProtectionParams.Builder()
147 .setUserSecretType(TYPE_LOCKSCREEN)
148 .setLockScreenUiFormat(UI_FORMAT_PASSWORD)
149 .setKeyDerivationParams(KeyDerivationParams.createSha256Params(TEST_SALT))
150 .setSecret(TEST_SECRET)
151 .build();
Dmitry Dementyevad884712017-12-20 12:38:36 -0800152
Robert Berrye16fa982017-12-20 15:59:37 +0000153 @Mock private Context mMockContext;
Robert Berry91044042017-12-27 12:05:58 +0000154 @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
Robert Berrycfc990a2017-12-22 15:54:30 +0000155 @Mock private KeyguardManager mKeyguardManager;
Bo Zhu3462c832018-01-04 22:42:36 -0800156 @Mock private PlatformKeyManager mPlatformKeyManager;
Dmitry Dementyev29b9de52018-01-31 16:09:32 -0800157 @Mock private ApplicationKeyStorage mApplicationKeyStorage;
Dmitry Dementyev89f12d52019-02-28 12:26:01 -0800158 @Mock private CleanupManager mCleanupManager;
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700159 @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
Robert Berrye16fa982017-12-20 15:59:37 +0000160
161 private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
162 private File mDatabaseFile;
163 private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
164 private RecoverySessionStorage mRecoverySessionStorage;
Robert Berrybd086f12017-12-27 13:29:39 +0000165 private RecoverySnapshotStorage mRecoverySnapshotStorage;
Bo Zhu3462c832018-01-04 22:42:36 -0800166 private PlatformEncryptionKey mPlatformEncryptionKey;
Robert Berrye16fa982017-12-20 15:59:37 +0000167
168 @Before
Bo Zhu3462c832018-01-04 22:42:36 -0800169 public void setUp() throws Exception {
Robert Berrye16fa982017-12-20 15:59:37 +0000170 MockitoAnnotations.initMocks(this);
171
172 Context context = InstrumentationRegistry.getTargetContext();
173 mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
174 mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
Robert Berrycfc990a2017-12-22 15:54:30 +0000175
Robert Berrye16fa982017-12-20 15:59:37 +0000176 mRecoverySessionStorage = new RecoverySessionStorage();
Robert Berrycfc990a2017-12-22 15:54:30 +0000177
178 when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
179 when(mMockContext.getSystemServiceName(any())).thenReturn("test");
Robert Berry3ae5bea2017-12-27 10:58:03 +0000180 when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
Bo Zhu3462c832018-01-04 22:42:36 -0800181 when(mKeyguardManager.isDeviceSecure(TEST_USER_ID)).thenReturn(true);
182
183 mPlatformEncryptionKey =
184 new PlatformEncryptionKey(TEST_GENERATION_ID, generateAndroidKeyStoreKey());
185 when(mPlatformKeyManager.getEncryptKey(anyInt())).thenReturn(mPlatformEncryptionKey);
Robert Berrycfc990a2017-12-22 15:54:30 +0000186
Robert Berrye16fa982017-12-20 15:59:37 +0000187 mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
188 mMockContext,
189 mRecoverableKeyStoreDb,
Robert Berry4a534ec2017-12-21 15:44:02 +0000190 mRecoverySessionStorage,
Dmitry Dementyev3b17c632017-12-21 17:30:48 -0800191 Executors.newSingleThreadExecutor(),
Robert Berry91044042017-12-27 12:05:58 +0000192 mRecoverySnapshotStorage,
Bo Zhu3462c832018-01-04 22:42:36 -0800193 mMockListenersStorage,
Dmitry Dementyev29b9de52018-01-31 16:09:32 -0800194 mPlatformKeyManager,
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700195 mApplicationKeyStorage,
Dmitry Dementyev89f12d52019-02-28 12:26:01 -0800196 mTestOnlyInsecureCertificateHelper,
197 mCleanupManager);
Robert Berrye16fa982017-12-20 15:59:37 +0000198 }
199
200 @After
201 public void tearDown() {
202 mRecoverableKeyStoreDb.close();
203 mDatabaseFile.delete();
204 }
205
206 @Test
Bo Zhu2c8e5382018-02-26 15:54:25 -0800207 public void importKey_storesTheKey() throws Exception {
208 int uid = Binder.getCallingUid();
209 int userId = UserHandle.getCallingUserId();
210 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
211
212 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
213
214 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
215 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
216 }
217
218 @Test
219 public void importKey_throwsIfInvalidLength() throws Exception {
220 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
221 try {
222 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
223 fail("should have thrown");
224 } catch (ServiceSpecificException e) {
225 assertThat(e.getMessage()).contains("not contain 256 bits");
226 }
227 }
228
229 @Test
230 public void importKey_throwsIfNullKey() throws Exception {
231 try {
232 mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
233 fail("should have thrown");
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700234 } catch (NullPointerException e) {
235 assertThat(e.getMessage()).contains("is null");
Bo Zhu2c8e5382018-02-26 15:54:25 -0800236 }
237 }
238
239 @Test
Bo Zhu7ebcd662019-01-04 17:00:58 -0800240 public void importKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
241 int uid = Binder.getCallingUid();
242 int userId = UserHandle.getCallingUserId();
243 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
244
245 mRecoverableKeyStoreManager.importKeyWithMetadata(
246 TEST_ALIAS, keyMaterial, KEY_METADATA_NULL);
247
248 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
249 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
250 }
251
252 @Test
253 public void importKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
254 int uid = Binder.getCallingUid();
255 int userId = UserHandle.getCallingUserId();
256 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
257
258 mRecoverableKeyStoreManager.importKeyWithMetadata(
259 TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
260
261 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
262 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
263 }
264
265 @Test
266 public void importKeyWithMetadata_throwsIfInvalidLength() throws Exception {
267 byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
268 try {
269 mRecoverableKeyStoreManager.importKeyWithMetadata(
270 TEST_ALIAS, keyMaterial, KEY_METADATA_NON_NULL);
271 fail("should have thrown");
272 } catch (ServiceSpecificException e) {
273 assertThat(e.getMessage()).contains("not contain 256 bits");
274 }
275 }
276
277 @Test
278 public void importKeyWithMetadata_throwsIfNullKey() throws Exception {
279 try {
280 mRecoverableKeyStoreManager.importKeyWithMetadata(
281 TEST_ALIAS, /*keyBytes=*/ null, KEY_METADATA_NON_NULL);
282 fail("should have thrown");
283 } catch (NullPointerException e) {
284 assertThat(e.getMessage()).contains("is null");
285 }
286 }
287
288 @Test
289 public void generateKeyWithMetadata_nullMetadata_storesTheKey() throws Exception {
290 int uid = Binder.getCallingUid();
291 int userId = UserHandle.getCallingUserId();
292
293 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
294
295 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
296 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
297 }
298
299 @Test
300 public void generateKeyWithMetadata_nonNullMetadata_storesTheKey() throws Exception {
301 int uid = Binder.getCallingUid();
302 int userId = UserHandle.getCallingUserId();
303
304 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NON_NULL);
305
306 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
307 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
308 }
309
310 @Test
Robert Berry5daccec2018-01-06 19:16:25 +0000311 public void removeKey_removesAKey() throws Exception {
312 int uid = Binder.getCallingUid();
Dmitry Dementyev86f5bb12018-03-27 16:58:50 -0700313 mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
Robert Berry5daccec2018-01-06 19:16:25 +0000314
315 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
316
317 assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNull();
318 }
319
320 @Test
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800321 public void removeKey_updatesShouldCreateSnapshot() throws Exception {
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800322 int uid = Binder.getCallingUid();
323 int userId = UserHandle.getCallingUserId();
Dmitry Dementyev86f5bb12018-03-27 16:58:50 -0700324 mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800325 // Pretend that key was synced
326 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
327
328 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
329
330 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
331 }
332
333 @Test
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800334 public void removeKey_failureDoesNotUpdateShouldCreateSnapshot() throws Exception {
335 int uid = Binder.getCallingUid();
336 int userId = UserHandle.getCallingUserId();
337 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
338 // Key did not exist
339 mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
340
341 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
342 }
343
344 @Test
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800345 public void initRecoveryService_succeedsWithCertFile() throws Exception {
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800346 int uid = Binder.getCallingUid();
347 int userId = UserHandle.getCallingUserId();
Bo Zhu14d993d2018-02-03 21:38:48 -0800348 long certSerial = 1000L;
349 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
350
351 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
352 TestData.getCertXmlWithSerial(certSerial));
353
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700354 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
355 .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS);
356
Robert Berrye8edf972018-03-27 11:45:11 +0100357 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700358 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
359 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
360 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
361 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial);
Bo Zhu14d993d2018-02-03 21:38:48 -0800362 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
363 }
364
365 @Test
Dmitry Dementyev925f0262018-04-12 12:10:50 -0700366 public void initRecoveryService_updatesShouldCreatesnapshotOnCertUpdate() throws Exception {
367 int uid = Binder.getCallingUid();
368 int userId = UserHandle.getCallingUserId();
369 long certSerial = 1000L;
370 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
371
372 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
373 TestData.getCertXmlWithSerial(certSerial));
374
375 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
376
377 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
378 TestData.getCertXmlWithSerial(certSerial + 1));
379
380 // Since there were no recoverable keys, new snapshot will not be created.
381 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
382
383 generateKeyAndSimulateSync(userId, uid, 10);
384
385 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
386 TestData.getCertXmlWithSerial(certSerial + 2));
387
388 // Since there were a recoverable key, new serial number triggers snapshot creation
389 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
390 }
391
392 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700393 public void initRecoveryService_triesToFilterRootAlias() throws Exception {
394 int uid = Binder.getCallingUid();
395 int userId = UserHandle.getCallingUserId();
396 long certSerial = 1000L;
397 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
398
399 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
400 TestData.getCertXmlWithSerial(certSerial));
401
402 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
403 .getDefaultCertificateAliasIfEmpty(eq(ROOT_CERTIFICATE_ALIAS));
404
405 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
406 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
407
408 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
409 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
410
411 }
412
413 @Test
414 public void initRecoveryService_usesProdCertificateForEmptyRootAlias() throws Exception {
415 int uid = Binder.getCallingUid();
416 int userId = UserHandle.getCallingUserId();
417 long certSerial = 1000L;
418 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
419
420 mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ "",
421 TestData.getCertXmlWithSerial(certSerial));
422
423 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
424 .getDefaultCertificateAliasIfEmpty(eq(""));
425
426 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
427 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
428
429 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
430 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
431 }
432
433 @Test
434 public void initRecoveryService_usesProdCertificateForNullRootAlias() throws Exception {
435 int uid = Binder.getCallingUid();
436 int userId = UserHandle.getCallingUserId();
437 long certSerial = 1000L;
438 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
439
440 mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ null,
441 TestData.getCertXmlWithSerial(certSerial));
442
443 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
444 .getDefaultCertificateAliasIfEmpty(null);
445
446 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
447 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
448
449 String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
450 assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
451 }
452
453 @Test
Bo Zhu8d6861e2018-03-21 20:45:09 -0700454 public void initRecoveryService_regeneratesCounterId() throws Exception {
455 int uid = Binder.getCallingUid();
456 int userId = UserHandle.getCallingUserId();
457 long certSerial = 1000L;
458
459 Long counterId0 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
460 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
461 TestData.getCertXmlWithSerial(certSerial));
462 Long counterId1 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
463 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
464 TestData.getCertXmlWithSerial(certSerial + 1));
465 Long counterId2 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
466
467 assertThat(!counterId1.equals(counterId0) || !counterId2.equals(counterId1)).isTrue();
468 }
469
470 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800471 public void initRecoveryService_throwsIfInvalidCert() throws Exception {
472 byte[] modifiedCertXml = TestData.getCertXml();
473 modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate
474 try {
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700475 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
476 modifiedCertXml);
Bo Zhu7f414d92018-02-28 09:28:19 -0800477 fail("should have thrown");
478 } catch (ServiceSpecificException e) {
Bo Zhu41d2dd22018-03-30 12:20:06 -0700479 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE);
Bo Zhu7f414d92018-02-28 09:28:19 -0800480 }
481 }
482
483 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800484 public void initRecoveryService_updatesWithLargerSerial() throws Exception {
485 int uid = Binder.getCallingUid();
486 int userId = UserHandle.getCallingUserId();
487 long certSerial = 1000L;
488
489 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
490 TestData.getCertXmlWithSerial(certSerial));
491 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
492 TestData.getCertXmlWithSerial(certSerial + 1));
493
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700494 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
495 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1);
Dmitry Dementyev925f0262018-04-12 12:10:50 -0700496 // There were no keys.
497 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Bo Zhu14d993d2018-02-03 21:38:48 -0800498 }
499
500 @Test
Aseem Kumar23174b72018-04-03 11:35:51 -0700501 public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception {
Bo Zhu14d993d2018-02-03 21:38:48 -0800502 int uid = Binder.getCallingUid();
503 int userId = UserHandle.getCallingUserId();
504 long certSerial = 1000L;
505
506 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
507 TestData.getCertXmlWithSerial(certSerial));
Aseem Kumar23174b72018-04-03 11:35:51 -0700508 try {
509 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
510 TestData.getCertXmlWithSerial(certSerial - 1));
511 fail();
512 } catch (ServiceSpecificException e) {
513 assertThat(e.errorCode).isEqualTo(ERROR_DOWNGRADE_CERTIFICATE);
514 }
Bo Zhu14d993d2018-02-03 21:38:48 -0800515 }
516
517 @Test
Bo Zhub10ba442018-03-31 17:13:46 -0700518 public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception {
519 int uid = Binder.getCallingUid();
520 int userId = UserHandle.getCallingUserId();
521 int certSerial = 3333;
522
523 String testRootCertAlias = TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS;
524
525 mRecoverableKeyStoreManager.initRecoveryService(testRootCertAlias,
526 TestData.getInsecureCertXmlBytesWithEndpoint1(certSerial));
527 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
528 testRootCertAlias)).isEqualTo(certSerial);
529 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
530 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint1());
531
532 mRecoverableKeyStoreManager.initRecoveryService(testRootCertAlias,
533 TestData.getInsecureCertXmlBytesWithEndpoint2(certSerial - 1));
534 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
535 testRootCertAlias)).isEqualTo(certSerial - 1);
536 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
537 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2());
538 }
539
540 @Test
541 public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception {
542 int uid = Binder.getCallingUid();
543 int userId = UserHandle.getCallingUserId();
544
545 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
546 TestData.getCertXmlWithSerial(1111L));
547 mRecoverableKeyStoreManager.initRecoveryService(
548 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
549 TestData.getInsecureCertXmlBytesWithEndpoint1(2222));
550
551 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
552 ROOT_CERTIFICATE_ALIAS)).isEqualTo(1111L);
553 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
554 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isEqualTo(2222L);
555
556 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
557 ROOT_CERTIFICATE_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
558 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
559 TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isEqualTo(
560 TestData.getInsecureCertPathForEndpoint1());
561 }
562
563 @Test
Bo Zhu14d993d2018-02-03 21:38:48 -0800564 public void initRecoveryService_ignoresTheSameSerial() throws Exception {
565 int uid = Binder.getCallingUid();
566 int userId = UserHandle.getCallingUserId();
567 long certSerial = 1000L;
568
569 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
570 TestData.getCertXmlWithSerial(certSerial));
Dmitry Dementyev925f0262018-04-12 12:10:50 -0700571
572 generateKeyAndSimulateSync(userId, uid, 10);
573
Bo Zhu14d993d2018-02-03 21:38:48 -0800574 mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
575 TestData.getCertXmlWithSerial(certSerial));
576
Bo Zhu14d993d2018-02-03 21:38:48 -0800577 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
578 }
579
580 @Test
Bo Zhuc98c8432018-03-31 13:08:49 -0700581 public void initRecoveryService_throwsIfRawPublicKey() throws Exception {
Bo Zhu14d993d2018-02-03 21:38:48 -0800582 int uid = Binder.getCallingUid();
583 int userId = UserHandle.getCallingUserId();
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800584 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
585
Bo Zhuc98c8432018-03-31 13:08:49 -0700586 try {
587 mRecoverableKeyStoreManager
588 .initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
589 fail("should have thrown");
590 } catch (ServiceSpecificException e) {
591 assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT);
592 }
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800593
Bo Zhuc98c8432018-03-31 13:08:49 -0700594 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700595 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
596 DEFAULT_ROOT_CERT_ALIAS)).isNull();
597 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
598 DEFAULT_ROOT_CERT_ALIAS)).isNull();
Bo Zhuc98c8432018-03-31 13:08:49 -0700599 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
Dmitry Dementyev40dadb02018-01-10 18:03:37 -0800600 }
601
602 @Test
Bo Zhu0b8c82e2018-03-30 11:31:53 -0700603 public void initRecoveryService_throwsIfUnknownRootCertAlias() throws Exception {
604 try {
605 mRecoverableKeyStoreManager.initRecoveryService(
606 "unknown-root-cert-alias", TestData.getCertXml());
607 fail("should have thrown");
608 } catch (ServiceSpecificException e) {
609 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE);
610 }
611 }
612
613 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800614 public void initRecoveryServiceWithSigFile_succeeds() throws Exception {
615 int uid = Binder.getCallingUid();
616 int userId = UserHandle.getCallingUserId();
617 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
618
619 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
620 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
621
Robert Berrye8edf972018-03-27 11:45:11 +0100622 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700623 assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
624 DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
Bo Zhu7f414d92018-02-28 09:28:19 -0800625 assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
626 }
627
628 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700629 public void initRecoveryServiceWithSigFile_usesProdCertificateForNullRootAlias()
630 throws Exception {
631 int uid = Binder.getCallingUid();
632 int userId = UserHandle.getCallingUserId();
633 long certSerial = 1000L;
634 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
635
636 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
637 /*rootCertificateAlias=*/null, TestData.getCertXml(), TestData.getSigXml());
638
639 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
640 .getDefaultCertificateAliasIfEmpty(null);
641
642 verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
643 .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
644 }
645
646 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800647 public void initRecoveryServiceWithSigFile_throwsIfNullCertFile() throws Exception {
648 try {
649 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
650 ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null,
651 TestData.getSigXml());
652 fail("should have thrown");
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700653 } catch (NullPointerException e) {
Bo Zhu7f414d92018-02-28 09:28:19 -0800654 assertThat(e.getMessage()).contains("is null");
655 }
656 }
657
658 @Test
659 public void initRecoveryServiceWithSigFile_throwsIfNullSigFile() throws Exception {
660 try {
661 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
662 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
663 /*recoveryServiceSigFile=*/ null);
664 fail("should have thrown");
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700665 } catch (NullPointerException e) {
Bo Zhu7f414d92018-02-28 09:28:19 -0800666 assertThat(e.getMessage()).contains("is null");
667 }
668 }
669
670 @Test
671 public void initRecoveryServiceWithSigFile_throwsIfWrongSigFileFormat() throws Exception {
672 try {
673 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
674 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
675 getUtf8Bytes("wrong-sig-file-format"));
676 fail("should have thrown");
677 } catch (ServiceSpecificException e) {
Bo Zhu41d2dd22018-03-30 12:20:06 -0700678 assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT);
Bo Zhu7f414d92018-02-28 09:28:19 -0800679 }
680 }
681
682 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700683 public void initRecoveryServiceWithSigFile_throwsIfTestAliasUsedWithProdCert()
684 throws Exception {
685 try {
686 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
687 INSECURE_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
688 fail("should have thrown");
689 } catch (ServiceSpecificException e) {
Bo Zhu41d2dd22018-03-30 12:20:06 -0700690 assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE);
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700691 }
692 }
693
694 @Test
Bo Zhu7f414d92018-02-28 09:28:19 -0800695 public void initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature() throws Exception {
696 byte[] modifiedCertXml = TestData.getCertXml();
697 modifiedCertXml[modifiedCertXml.length - 1] = 0; // Change the last new line char to a zero
698 try {
699 mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
700 ROOT_CERTIFICATE_ALIAS, modifiedCertXml, TestData.getSigXml());
701 fail("should have thrown");
702 } catch (ServiceSpecificException e) {
703 assertThat(e.getMessage()).contains("is invalid");
704 }
705 }
706
707 @Test
Robert Berrye16fa982017-12-20 15:59:37 +0000708 public void startRecoverySession_checksPermissionFirst() throws Exception {
709 mRecoverableKeyStoreManager.startRecoverySession(
710 TEST_SESSION_ID,
711 TEST_PUBLIC_KEY,
712 TEST_VAULT_PARAMS,
713 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700714 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berrye16fa982017-12-20 15:59:37 +0000715
Dmitry Dementyevad884712017-12-20 12:38:36 -0800716 verify(mMockContext, times(1))
717 .enforceCallingOrSelfPermission(
Dmitry Dementyeved89ea02018-01-11 13:53:52 -0800718 eq(Manifest.permission.RECOVER_KEYSTORE), any());
Robert Berrye16fa982017-12-20 15:59:37 +0000719 }
720
721 @Test
Bo Zhu7c1972f2018-02-22 21:43:52 -0800722 public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
723 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
724 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700725 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800726 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
727 TEST_VAULT_PARAMS,
728 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700729 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800730
731 assertEquals(1, mRecoverySessionStorage.size());
732 RecoverySessionStorage.Entry entry =
733 mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
734 assertArrayEquals(TEST_SECRET, entry.getLskfHash());
735 assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
736 }
737
738 @Test
739 public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
740 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
741 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700742 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800743 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
744 TEST_VAULT_PARAMS,
745 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700746 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800747
748 verify(mMockContext, times(2))
749 .enforceCallingOrSelfPermission(
750 eq(Manifest.permission.RECOVER_KEYSTORE), any());
751 }
752
753 @Test
Robert Berrye16fa982017-12-20 15:59:37 +0000754 public void startRecoverySession_storesTheSessionInfo() throws Exception {
755 mRecoverableKeyStoreManager.startRecoverySession(
756 TEST_SESSION_ID,
757 TEST_PUBLIC_KEY,
758 TEST_VAULT_PARAMS,
759 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700760 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berrye16fa982017-12-20 15:59:37 +0000761
762 assertEquals(1, mRecoverySessionStorage.size());
Dmitry Dementyevad884712017-12-20 12:38:36 -0800763 RecoverySessionStorage.Entry entry =
Dmitry Dementyev14298312018-01-04 15:19:19 -0800764 mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
Robert Berrye16fa982017-12-20 15:59:37 +0000765 assertArrayEquals(TEST_SECRET, entry.getLskfHash());
766 assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
767 }
768
769 @Test
Robert Berry2bcdad92018-01-18 12:53:29 +0000770 public void closeSession_closesASession() throws Exception {
771 mRecoverableKeyStoreManager.startRecoverySession(
772 TEST_SESSION_ID,
773 TEST_PUBLIC_KEY,
774 TEST_VAULT_PARAMS,
775 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700776 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berry2bcdad92018-01-18 12:53:29 +0000777
778 mRecoverableKeyStoreManager.closeSession(TEST_SESSION_ID);
779
780 assertEquals(0, mRecoverySessionStorage.size());
781 }
782
783 @Test
784 public void closeSession_doesNotCloseUnrelatedSessions() throws Exception {
785 mRecoverableKeyStoreManager.startRecoverySession(
786 TEST_SESSION_ID,
787 TEST_PUBLIC_KEY,
788 TEST_VAULT_PARAMS,
789 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700790 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berry2bcdad92018-01-18 12:53:29 +0000791
792 mRecoverableKeyStoreManager.closeSession("some random session");
793
794 assertEquals(1, mRecoverySessionStorage.size());
795 }
796
797 @Test
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -0700798 public void closeSession_throwsIfNullSession() throws Exception {
799 try {
800 mRecoverableKeyStoreManager.closeSession(/*sessionId=*/ null);
801 fail("should have thrown");
802 } catch (NullPointerException e) {
803 assertThat(e.getMessage()).contains("invalid");
804 }
805 }
806
807 @Test
Robert Berrye16fa982017-12-20 15:59:37 +0000808 public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception {
809 try {
810 mRecoverableKeyStoreManager.startRecoverySession(
811 TEST_SESSION_ID,
812 TEST_PUBLIC_KEY,
813 TEST_VAULT_PARAMS,
814 TEST_VAULT_CHALLENGE,
Dmitry Dementyev14298312018-01-04 15:19:19 -0800815 ImmutableList.of());
Robert Berryb9a220b2017-12-21 12:41:01 +0000816 fail("should have thrown");
Dmitry Dementyevae6ec6d2018-01-18 14:29:49 -0800817 } catch (UnsupportedOperationException e) {
Dmitry Dementyev77183ef2018-01-05 15:46:00 -0800818 assertThat(e.getMessage()).startsWith(
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800819 "Only a single KeyChainProtectionParams is supported");
Robert Berrye16fa982017-12-20 15:59:37 +0000820 }
821 }
822
823 @Test
Bo Zhudef7ffd2018-01-05 14:50:52 -0800824 public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception {
825 byte[] vaultParams = TEST_VAULT_PARAMS.clone();
826 vaultParams[1] ^= (byte) 1; // Flip 1 bit
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700827
Bo Zhudef7ffd2018-01-05 14:50:52 -0800828 try {
829 mRecoverableKeyStoreManager.startRecoverySession(
830 TEST_SESSION_ID,
831 TEST_PUBLIC_KEY,
832 vaultParams,
833 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700834 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhudef7ffd2018-01-05 14:50:52 -0800835 fail("should have thrown");
836 } catch (ServiceSpecificException e) {
837 assertThat(e.getMessage()).contains("do not match");
838 }
839 }
840
841 @Test
Bo Zhu7c1972f2018-02-22 21:43:52 -0800842 public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception {
843 try {
844 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
845 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700846 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800847 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
848 TEST_VAULT_PARAMS,
849 TEST_VAULT_CHALLENGE,
850 ImmutableList.of());
851 fail("should have thrown");
852 } catch (UnsupportedOperationException e) {
853 assertThat(e.getMessage()).startsWith(
854 "Only a single KeyChainProtectionParams is supported");
855 }
856 }
857
858 @Test
859 public void startRecoverySessionWithCertPath_throwsIfPublicKeysMismatch() throws Exception {
860 byte[] vaultParams = TEST_VAULT_PARAMS.clone();
861 vaultParams[1] ^= (byte) 1; // Flip 1 bit
862 try {
863 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
864 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700865 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800866 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
867 vaultParams,
868 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700869 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800870 fail("should have thrown");
871 } catch (ServiceSpecificException e) {
872 assertThat(e.getMessage()).contains("do not match");
873 }
874 }
875
876 @Test
877 public void startRecoverySessionWithCertPath_throwsIfEmptyCertPath() throws Exception {
878 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
879 CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>());
880 try {
881 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
882 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700883 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7c1972f2018-02-22 21:43:52 -0800884 RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
885 TEST_VAULT_PARAMS,
886 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700887 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7c1972f2018-02-22 21:43:52 -0800888 fail("should have thrown");
889 } catch (ServiceSpecificException e) {
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800890 assertThat(e.getMessage()).contains("empty");
891 }
892 }
893
894 @Test
895 public void startRecoverySessionWithCertPath_throwsIfInvalidCertPath() throws Exception {
896 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
897 CertPath shortCertPath = certFactory.generateCertPath(
898 TestData.CERT_PATH_1.getCertificates()
899 .subList(0, TestData.CERT_PATH_1.getCertificates().size() - 1));
900 try {
901 mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
902 TEST_SESSION_ID,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700903 TEST_DEFAULT_ROOT_CERT_ALIAS,
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800904 RecoveryCertPath.createRecoveryCertPath(shortCertPath),
905 TEST_VAULT_PARAMS,
906 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700907 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu7ce4ea52018-02-27 23:52:19 -0800908 fail("should have thrown");
909 } catch (ServiceSpecificException e) {
910 // expected
Bo Zhu7c1972f2018-02-22 21:43:52 -0800911 }
912 }
913
914 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +0000915 public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +0000916 try {
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700917 WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder()
918 .setAlias(TEST_ALIAS)
919 .setEncryptedKeyMaterial(randomBytes(32))
920 .build();
Robert Berry4a5c87d2018-03-19 18:00:46 +0000921 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +0000922 TEST_SESSION_ID,
923 /*recoveryKeyBlob=*/ randomBytes(32),
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700924 /*applicationKeys=*/ ImmutableList.of(applicationKey));
Robert Berryb9a220b2017-12-21 12:41:01 +0000925 fail("should have thrown");
Dmitry Dementyev14298312018-01-04 15:19:19 -0800926 } catch (ServiceSpecificException e) {
927 // expected
Robert Berryb9a220b2017-12-21 12:41:01 +0000928 }
929 }
930
931 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +0000932 public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +0000933 mRecoverableKeyStoreManager.startRecoverySession(
934 TEST_SESSION_ID,
935 TEST_PUBLIC_KEY,
936 TEST_VAULT_PARAMS,
937 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700938 ImmutableList.of(TEST_PROTECTION_PARAMS));
Robert Berryb9a220b2017-12-21 12:41:01 +0000939
940 try {
Robert Berry4a5c87d2018-03-19 18:00:46 +0000941 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +0000942 TEST_SESSION_ID,
943 /*encryptedRecoveryKey=*/ randomBytes(60),
Dmitry Dementyev14298312018-01-04 15:19:19 -0800944 /*applicationKeys=*/ ImmutableList.of());
Robert Berryb9a220b2017-12-21 12:41:01 +0000945 fail("should have thrown");
Dmitry Dementyev14298312018-01-04 15:19:19 -0800946 } catch (ServiceSpecificException e) {
947 assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key");
Robert Berryb9a220b2017-12-21 12:41:01 +0000948 }
949 }
950
951 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -0700952 public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys()
953 throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +0000954 mRecoverableKeyStoreManager.startRecoverySession(
955 TEST_SESSION_ID,
956 TEST_PUBLIC_KEY,
957 TEST_VAULT_PARAMS,
958 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700959 ImmutableList.of(TEST_PROTECTION_PARAMS));
Dmitry Dementyev14298312018-01-04 15:19:19 -0800960 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
Robert Berryb9a220b2017-12-21 12:41:01 +0000961 .getKeyClaimant();
962 SecretKey recoveryKey = randomRecoveryKey();
963 byte[] encryptedClaimResponse = encryptClaimResponse(
964 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700965 WrappedApplicationKey badApplicationKey = new WrappedApplicationKey.Builder()
966 .setAlias(TEST_ALIAS)
967 .setEncryptedKeyMaterial(
968 encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)))
969 .build();
Robert Berryb9a220b2017-12-21 12:41:01 +0000970 try {
Robert Berry4a5c87d2018-03-19 18:00:46 +0000971 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +0000972 TEST_SESSION_ID,
973 /*encryptedRecoveryKey=*/ encryptedClaimResponse,
Dmitry Dementyev14298312018-01-04 15:19:19 -0800974 /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
Robert Berryb9a220b2017-12-21 12:41:01 +0000975 fail("should have thrown");
Bo Zhu3462c832018-01-04 22:42:36 -0800976 } catch (ServiceSpecificException e) {
Bo Zhu4857cb52018-02-06 14:34:48 -0800977 assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys");
Robert Berryb9a220b2017-12-21 12:41:01 +0000978 }
979 }
980
981 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +0000982 public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted()
983 throws Exception {
Bo Zhuae0682d2018-02-13 10:23:39 -0800984 mRecoverableKeyStoreManager.startRecoverySession(
985 TEST_SESSION_ID,
986 TEST_PUBLIC_KEY,
987 TEST_VAULT_PARAMS,
988 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700989 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhuae0682d2018-02-13 10:23:39 -0800990 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
991 .getKeyClaimant();
992 SecretKey recoveryKey = randomRecoveryKey();
993 byte[] encryptedClaimResponse = encryptClaimResponse(
994 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
995
Robert Berry4a5c87d2018-03-19 18:00:46 +0000996 mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Bo Zhuae0682d2018-02-13 10:23:39 -0800997 TEST_SESSION_ID,
998 /*encryptedRecoveryKey=*/ encryptedClaimResponse,
999 /*applicationKeys=*/ ImmutableList.of());
1000 }
1001
1002 @Test
Robert Berry4a5c87d2018-03-19 18:00:46 +00001003 public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +00001004 mRecoverableKeyStoreManager.startRecoverySession(
1005 TEST_SESSION_ID,
1006 TEST_PUBLIC_KEY,
1007 TEST_VAULT_PARAMS,
1008 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -07001009 ImmutableList.of(TEST_PROTECTION_PARAMS));
Dmitry Dementyev14298312018-01-04 15:19:19 -08001010 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
Robert Berryb9a220b2017-12-21 12:41:01 +00001011 .getKeyClaimant();
1012 SecretKey recoveryKey = randomRecoveryKey();
1013 byte[] encryptedClaimResponse = encryptClaimResponse(
1014 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
Robert Berrybd4c43c2017-12-22 11:35:14 +00001015 byte[] applicationKeyBytes = randomBytes(32);
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -07001016 WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder()
1017 .setAlias(TEST_ALIAS)
1018 .setEncryptedKeyMaterial(
1019 encryptedApplicationKey(recoveryKey, applicationKeyBytes))
1020 .build();
Robert Berryb9a220b2017-12-21 12:41:01 +00001021
Robert Berry4a5c87d2018-03-19 18:00:46 +00001022 Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Robert Berryb9a220b2017-12-21 12:41:01 +00001023 TEST_SESSION_ID,
1024 encryptedClaimResponse,
Dmitry Dementyev14298312018-01-04 15:19:19 -08001025 ImmutableList.of(applicationKey));
Robert Berrybd4c43c2017-12-22 11:35:14 +00001026
1027 assertThat(recoveredKeys).hasSize(1);
Robert Berry4a5c87d2018-03-19 18:00:46 +00001028 assertThat(recoveredKeys).containsKey(TEST_ALIAS);
Robert Berryb9a220b2017-12-21 12:41:01 +00001029 }
1030
Dmitry Dementyev3b17c632017-12-21 17:30:48 -08001031 @Test
Dmitry Dementyev57ca3da2018-03-28 12:36:45 -07001032 public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails()
1033 throws Exception {
Bo Zhu4857cb52018-02-06 14:34:48 -08001034 mRecoverableKeyStoreManager.startRecoverySession(
1035 TEST_SESSION_ID,
1036 TEST_PUBLIC_KEY,
1037 TEST_VAULT_PARAMS,
1038 TEST_VAULT_CHALLENGE,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -07001039 ImmutableList.of(TEST_PROTECTION_PARAMS));
Bo Zhu4857cb52018-02-06 14:34:48 -08001040 byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
1041 .getKeyClaimant();
1042 SecretKey recoveryKey = randomRecoveryKey();
1043 byte[] encryptedClaimResponse = encryptClaimResponse(
1044 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
1045
1046 byte[] applicationKeyBytes1 = randomBytes(32);
1047 byte[] applicationKeyBytes2 = randomBytes(32);
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -07001048 WrappedApplicationKey applicationKey1 = new WrappedApplicationKey.Builder()
1049 .setAlias(TEST_ALIAS)
1050 // Use a different recovery key here, so the decryption will fail
1051 .setEncryptedKeyMaterial(
1052 encryptedApplicationKey(randomRecoveryKey(), applicationKeyBytes1))
1053 .build();
1054 WrappedApplicationKey applicationKey2 = new WrappedApplicationKey.Builder()
1055 .setAlias(TEST_ALIAS2)
1056 .setEncryptedKeyMaterial(
1057 encryptedApplicationKey(recoveryKey, applicationKeyBytes2))
1058 .build();
Bo Zhu4857cb52018-02-06 14:34:48 -08001059
Robert Berry4a5c87d2018-03-19 18:00:46 +00001060 Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
Bo Zhu4857cb52018-02-06 14:34:48 -08001061 TEST_SESSION_ID,
1062 encryptedClaimResponse,
1063 ImmutableList.of(applicationKey1, applicationKey2));
1064
1065 assertThat(recoveredKeys).hasSize(1);
Robert Berry4a5c87d2018-03-19 18:00:46 +00001066 assertThat(recoveredKeys).containsKey(TEST_ALIAS2);
Bo Zhu4857cb52018-02-06 14:34:48 -08001067 }
1068
1069 @Test
Dmitry Dementyev3b17c632017-12-21 17:30:48 -08001070 public void setSnapshotCreatedPendingIntent() throws Exception {
1071 int uid = Binder.getCallingUid();
1072 PendingIntent intent = PendingIntent.getBroadcast(
1073 InstrumentationRegistry.getTargetContext(), /*requestCode=*/1,
1074 new Intent(), /*flags=*/ 0);
Dmitry Dementyev14298312018-01-04 15:19:19 -08001075 mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent);
Dmitry Dementyev3b17c632017-12-21 17:30:48 -08001076 verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class));
1077 }
1078
Dmitry Dementyevad884712017-12-20 12:38:36 -08001079 @Test
Robert Berry8f9038c2018-03-26 11:36:40 +01001080 public void setServerParams_updatesServerParams() throws Exception {
1081 int uid = Binder.getCallingUid();
1082 int userId = UserHandle.getCallingUserId();
1083 byte[] serverParams = new byte[] { 1 };
1084
1085 mRecoverableKeyStoreManager.setServerParams(serverParams);
1086
1087 assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(serverParams);
1088 }
1089
1090 @Test
1091 public void setServerParams_doesNotSetSnapshotPendingIfInitializing() throws Exception {
1092 int uid = Binder.getCallingUid();
1093 int userId = UserHandle.getCallingUserId();
1094 byte[] serverParams = new byte[] { 1 };
1095
1096 mRecoverableKeyStoreManager.setServerParams(serverParams);
1097
1098 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1099 }
1100
1101 @Test
1102 public void setServerParams_doesNotSetSnapshotPendingIfSettingSameValue() throws Exception {
1103 int uid = Binder.getCallingUid();
1104 int userId = UserHandle.getCallingUserId();
1105 byte[] serverParams = new byte[] { 1 };
1106
1107 mRecoverableKeyStoreManager.setServerParams(serverParams);
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001108
1109 generateKeyAndSimulateSync(userId, uid, 10);
1110
Robert Berry8f9038c2018-03-26 11:36:40 +01001111 mRecoverableKeyStoreManager.setServerParams(serverParams);
1112
1113 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1114 }
1115
1116 @Test
1117 public void setServerParams_setsSnapshotPendingIfUpdatingValue() throws Exception {
1118 int uid = Binder.getCallingUid();
1119 int userId = UserHandle.getCallingUserId();
1120
1121 mRecoverableKeyStoreManager.setServerParams(new byte[] { 1 });
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001122
1123 generateKeyAndSimulateSync(userId, uid, 10);
1124
Robert Berry8f9038c2018-03-26 11:36:40 +01001125 mRecoverableKeyStoreManager.setServerParams(new byte[] { 2 });
1126
1127 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
1128 }
1129
1130 @Test
Robert Berry5a1acefb2018-03-26 14:41:30 +01001131 public void setRecoverySecretTypes_updatesSecretTypes() throws Exception {
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001132 int[] types1 = new int[]{11, 2000};
1133 int[] types2 = new int[]{1, 2, 3};
1134 int[] types3 = new int[]{};
1135
Dmitry Dementyev14298312018-01-04 15:19:19 -08001136 mRecoverableKeyStoreManager.setRecoverySecretTypes(types1);
1137 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001138 types1);
1139
Dmitry Dementyev14298312018-01-04 15:19:19 -08001140 mRecoverableKeyStoreManager.setRecoverySecretTypes(types2);
1141 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001142 types2);
1143
Dmitry Dementyev14298312018-01-04 15:19:19 -08001144 mRecoverableKeyStoreManager.setRecoverySecretTypes(types3);
1145 assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
Dmitry Dementyevbdfdf532017-12-27 11:58:45 -08001146 types3);
1147 }
1148
1149 @Test
Robert Berry5a1acefb2018-03-26 14:41:30 +01001150 public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception {
1151 int uid = Binder.getCallingUid();
1152 int userId = UserHandle.getCallingUserId();
1153 int[] secretTypes = new int[] { 101 };
1154
1155 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
1156
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001157 // There were no keys.
Robert Berry5a1acefb2018-03-26 14:41:30 +01001158 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1159 }
1160
1161 @Test
1162 public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()
1163 throws Exception {
1164 int uid = Binder.getCallingUid();
1165 int userId = UserHandle.getCallingUserId();
1166 int[] secretTypes = new int[] { 101 };
1167
1168 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001169
1170 generateKeyAndSimulateSync(userId, uid, 10);
1171
Robert Berry5a1acefb2018-03-26 14:41:30 +01001172 mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
1173
1174 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1175 }
1176
1177 @Test
1178 public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception {
1179 int uid = Binder.getCallingUid();
1180 int userId = UserHandle.getCallingUserId();
1181
1182 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 });
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001183
1184 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
1185
1186 generateKeyAndSimulateSync(userId, uid, 10);
1187
Robert Berry5a1acefb2018-03-26 14:41:30 +01001188 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 });
1189
1190 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
1191 }
1192
1193 @Test
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -07001194 public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
1195 try {
1196 mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
1197 fail("should have thrown");
1198 } catch (NullPointerException e) {
1199 assertThat(e.getMessage()).contains("is null");
1200 }
1201 }
1202
1203 @Test
Dmitry Dementyev40dadb02018-01-10 18:03:37 -08001204 public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
1205 int uid = Binder.getCallingUid();
1206 int userId = UserHandle.getCallingUserId();
Robert Berrye8edf972018-03-27 11:45:11 +01001207 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 });
Dmitry Dementyev40dadb02018-01-10 18:03:37 -08001208
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001209 generateKeyAndSimulateSync(userId, uid, 10);
1210
Robert Berrye8edf972018-03-27 11:45:11 +01001211 mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 });
Dmitry Dementyev40dadb02018-01-10 18:03:37 -08001212
1213 assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
1214 }
1215
1216 @Test
Robert Berrybbe02ae2018-02-20 19:47:43 +00001217 public void setRecoveryStatus() throws Exception {
Dmitry Dementyevad884712017-12-20 12:38:36 -08001218 int userId = UserHandle.getCallingUserId();
1219 int uid = Binder.getCallingUid();
1220 int status = 100;
1221 int status2 = 200;
1222 String alias = "key1";
Bo Zhu7ebcd662019-01-04 17:00:58 -08001223 byte[] keyMetadata = null;
1224
1225 WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID,
1226 status);
Dmitry Dementyevad884712017-12-20 12:38:36 -08001227 mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
1228 Map<String, Integer> statuses =
Robert Berry56f06b42018-02-23 13:31:32 +00001229 mRecoverableKeyStoreManager.getRecoveryStatus();
Dmitry Dementyevad884712017-12-20 12:38:36 -08001230 assertThat(statuses).hasSize(1);
1231 assertThat(statuses).containsEntry(alias, status);
1232
Robert Berrybbe02ae2018-02-20 19:47:43 +00001233 mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2);
Robert Berry56f06b42018-02-23 13:31:32 +00001234 statuses = mRecoverableKeyStoreManager.getRecoveryStatus();
Dmitry Dementyevad884712017-12-20 12:38:36 -08001235 assertThat(statuses).hasSize(1);
1236 assertThat(statuses).containsEntry(alias, status2); // updated
1237 }
1238
Dmitry Dementyev1e6a9dc2018-03-21 13:52:00 -07001239 @Test
1240 public void setRecoveryStatus_throwsIfNullAlias() throws Exception {
1241 try {
1242 mRecoverableKeyStoreManager.setRecoveryStatus(/*alias=*/ null, /*status=*/ 100);
1243 fail("should have thrown");
1244 } catch (NullPointerException e) {
1245 assertThat(e.getMessage()).contains("is null");
1246 }
1247 }
1248
Robert Berrybd4c43c2017-12-22 11:35:14 +00001249 private static byte[] encryptedApplicationKey(
1250 SecretKey recoveryKey, byte[] applicationKey) throws Exception {
Robert Berryb9a220b2017-12-21 12:41:01 +00001251 return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
Bo Zhu7ebcd662019-01-04 17:00:58 -08001252 TEST_ALIAS,
1253 Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null)
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -07001254 )).get(TEST_ALIAS);
Robert Berryb9a220b2017-12-21 12:41:01 +00001255 }
1256
1257 private static byte[] encryptClaimResponse(
1258 byte[] keyClaimant,
1259 byte[] lskfHash,
1260 byte[] vaultParams,
1261 SecretKey recoveryKey) throws Exception {
1262 byte[] locallyEncryptedRecoveryKey = KeySyncUtils.locallyEncryptRecoveryKey(
1263 lskfHash, recoveryKey);
1264 return SecureBox.encrypt(
1265 /*theirPublicKey=*/ null,
1266 /*sharedSecret=*/ keyClaimant,
1267 /*header=*/ KeySyncUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
1268 /*payload=*/ locallyEncryptedRecoveryKey);
1269 }
1270
1271 private static SecretKey randomRecoveryKey() {
1272 return new SecretKeySpec(randomBytes(32), "AES");
1273 }
1274
Robert Berrye16fa982017-12-20 15:59:37 +00001275 private static byte[] getUtf8Bytes(String s) {
1276 return s.getBytes(StandardCharsets.UTF_8);
1277 }
Robert Berryb9a220b2017-12-21 12:41:01 +00001278
1279 private static byte[] randomBytes(int n) {
1280 byte[] bytes = new byte[n];
1281 new Random().nextBytes(bytes);
1282 return bytes;
1283 }
Bo Zhu3462c832018-01-04 22:42:36 -08001284
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001285 private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion)
1286 throws Exception{
Bo Zhu7ebcd662019-01-04 17:00:58 -08001287 mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL);
Dmitry Dementyev925f0262018-04-12 12:10:50 -07001288 // Simulate key sync.
1289 mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion);
1290 mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
1291 }
1292
Bo Zhu3462c832018-01-04 22:42:36 -08001293 private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
1294 KeyGenerator keyGenerator = KeyGenerator.getInstance(
1295 KEY_ALGORITHM,
1296 ANDROID_KEY_STORE_PROVIDER);
1297 keyGenerator.init(new KeyGenParameterSpec.Builder(
1298 WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
1299 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
1300 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
1301 .build());
1302 return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
1303 }
Robert Berrye16fa982017-12-20 15:59:37 +00001304}