Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.server.locksettings.recoverablekeystore; |
| 18 | |
Dmitry Dementyev | 0916e7c | 2018-01-23 13:02:08 -0800 | [diff] [blame] | 19 | import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN; |
| 20 | import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD; |
Bo Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 21 | import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT; |
Aseem Kumar | 23174b7 | 2018-04-03 11:35:51 -0700 | [diff] [blame] | 22 | import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE; |
Bo Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 23 | import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 24 | |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 25 | import static com.google.common.truth.Truth.assertThat; |
Brett Chabot | a26eda9 | 2018-07-23 13:08:30 -0700 | [diff] [blame] | 26 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 27 | import static org.junit.Assert.assertArrayEquals; |
| 28 | import static org.junit.Assert.assertEquals; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 29 | import static org.junit.Assert.fail; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 30 | import static org.mockito.ArgumentMatchers.any; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 31 | import static org.mockito.ArgumentMatchers.anyInt; |
| 32 | import static org.mockito.ArgumentMatchers.anyString; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 33 | import static org.mockito.ArgumentMatchers.eq; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 34 | import static org.mockito.Mockito.atLeast; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 35 | import static org.mockito.Mockito.times; |
| 36 | import static org.mockito.Mockito.verify; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 37 | import static org.mockito.Mockito.when; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 38 | |
Brett Chabot | a26eda9 | 2018-07-23 13:08:30 -0700 | [diff] [blame] | 39 | import android.Manifest; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 40 | import android.app.KeyguardManager; |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 41 | import android.app.PendingIntent; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 42 | import android.content.Context; |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 43 | import android.content.Intent; |
| 44 | import android.os.Binder; |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 45 | import android.os.ServiceSpecificException; |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 46 | import android.os.UserHandle; |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 47 | import android.security.keystore.AndroidKeyStoreSecretKey; |
| 48 | import android.security.keystore.KeyGenParameterSpec; |
| 49 | import android.security.keystore.KeyProperties; |
Dmitry Dementyev | 0916e7c | 2018-01-23 13:02:08 -0800 | [diff] [blame] | 50 | import android.security.keystore.recovery.KeyChainProtectionParams; |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 51 | import android.security.keystore.recovery.KeyDerivationParams; |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 52 | import android.security.keystore.recovery.RecoveryCertPath; |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 53 | import android.security.keystore.recovery.TrustedRootCertificates; |
Robert Berry | 81ee34b | 2018-01-23 11:59:59 +0000 | [diff] [blame] | 54 | import android.security.keystore.recovery.WrappedApplicationKey; |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 55 | import android.util.Pair; |
Brett Chabot | a26eda9 | 2018-07-23 13:08:30 -0700 | [diff] [blame] | 56 | |
| 57 | import androidx.test.InstrumentationRegistry; |
| 58 | import androidx.test.filters.SmallTest; |
| 59 | import androidx.test.runner.AndroidJUnit4; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 60 | |
Dmitry Dementyev | 29b9de5 | 2018-01-31 16:09:32 -0800 | [diff] [blame] | 61 | import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 62 | import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; |
| 63 | import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage; |
Robert Berry | bd086f1 | 2017-12-27 13:29:39 +0000 | [diff] [blame] | 64 | import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 65 | |
| 66 | import com.google.common.collect.ImmutableList; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 67 | import com.google.common.collect.ImmutableMap; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 68 | |
| 69 | import org.junit.After; |
| 70 | import org.junit.Before; |
| 71 | import org.junit.Test; |
| 72 | import org.junit.runner.RunWith; |
| 73 | import org.mockito.Mock; |
| 74 | import org.mockito.MockitoAnnotations; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 75 | import org.mockito.Spy; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 76 | |
| 77 | import java.io.File; |
| 78 | import java.nio.charset.StandardCharsets; |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 79 | import java.security.cert.CertPath; |
| 80 | import java.security.cert.CertificateFactory; |
| 81 | import java.security.cert.X509Certificate; |
| 82 | import java.util.ArrayList; |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 83 | import java.util.Map; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 84 | import java.util.Random; |
Brett Chabot | a26eda9 | 2018-07-23 13:08:30 -0700 | [diff] [blame] | 85 | import java.util.concurrent.Executors; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 86 | |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 87 | import javax.crypto.KeyGenerator; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 88 | import javax.crypto.SecretKey; |
| 89 | import javax.crypto.spec.SecretKeySpec; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 90 | |
| 91 | @SmallTest |
| 92 | @RunWith(AndroidJUnit4.class) |
| 93 | public class RecoverableKeyStoreManagerTest { |
| 94 | private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; |
| 95 | |
Bo Zhu | b31ab67 | 2018-03-20 22:44:18 -0700 | [diff] [blame] | 96 | private static final String ROOT_CERTIFICATE_ALIAS = ""; |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 97 | private static final String DEFAULT_ROOT_CERT_ALIAS = |
| 98 | TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 99 | private static final String INSECURE_CERTIFICATE_ALIAS = |
| 100 | TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 101 | private static final String TEST_SESSION_ID = "karlin"; |
Bo Zhu | bd7879c | 2018-05-11 05:10:28 +0000 | [diff] [blame] | 102 | private static final byte[] TEST_PUBLIC_KEY = TestData.CERT_1_PUBLIC_KEY.getEncoded(); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 103 | 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 Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 106 | private static final byte[] TEST_VAULT_PARAMS = new byte[] { |
| 107 | // backend_key |
Bo Zhu | bd7879c | 2018-05-11 05:10:28 +0000 | [diff] [blame] | 108 | (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 Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 118 | // 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 Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 126 | private static final int TEST_GENERATION_ID = 2; |
| 127 | private static final int TEST_USER_ID = 10009; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 128 | private static final int KEY_CLAIMANT_LENGTH_BYTES = 16; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 129 | 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 Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 132 | private static final String TEST_ALIAS2 = "bob"; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 133 | private static final int RECOVERABLE_KEY_SIZE_BYTES = 32; |
Bo Zhu | 2c8e538 | 2018-02-26 15:54:25 -0800 | [diff] [blame] | 134 | private static final int APPLICATION_KEY_SIZE_BYTES = 32; |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 135 | 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 Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 138 | private static final byte[] KEY_METADATA_NULL = null; |
| 139 | private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata"); |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 140 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 143 | private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = ""; |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 144 | 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 Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 151 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 152 | @Mock private Context mMockContext; |
Robert Berry | 9104404 | 2017-12-27 12:05:58 +0000 | [diff] [blame] | 153 | @Mock private RecoverySnapshotListenersStorage mMockListenersStorage; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 154 | @Mock private KeyguardManager mKeyguardManager; |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 155 | @Mock private PlatformKeyManager mPlatformKeyManager; |
Dmitry Dementyev | 29b9de5 | 2018-01-31 16:09:32 -0800 | [diff] [blame] | 156 | @Mock private ApplicationKeyStorage mApplicationKeyStorage; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 157 | @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 158 | |
| 159 | private RecoverableKeyStoreDb mRecoverableKeyStoreDb; |
| 160 | private File mDatabaseFile; |
| 161 | private RecoverableKeyStoreManager mRecoverableKeyStoreManager; |
| 162 | private RecoverySessionStorage mRecoverySessionStorage; |
Robert Berry | bd086f1 | 2017-12-27 13:29:39 +0000 | [diff] [blame] | 163 | private RecoverySnapshotStorage mRecoverySnapshotStorage; |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 164 | private PlatformEncryptionKey mPlatformEncryptionKey; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 165 | |
| 166 | @Before |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 167 | public void setUp() throws Exception { |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 168 | MockitoAnnotations.initMocks(this); |
| 169 | |
| 170 | Context context = InstrumentationRegistry.getTargetContext(); |
| 171 | mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME); |
| 172 | mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context); |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 173 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 174 | mRecoverySessionStorage = new RecoverySessionStorage(); |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 175 | |
| 176 | when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager); |
| 177 | when(mMockContext.getSystemServiceName(any())).thenReturn("test"); |
Robert Berry | 3ae5bea | 2017-12-27 10:58:03 +0000 | [diff] [blame] | 178 | when(mMockContext.getApplicationContext()).thenReturn(mMockContext); |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 179 | 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 Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 184 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 185 | mRecoverableKeyStoreManager = new RecoverableKeyStoreManager( |
| 186 | mMockContext, |
| 187 | mRecoverableKeyStoreDb, |
Robert Berry | 4a534ec | 2017-12-21 15:44:02 +0000 | [diff] [blame] | 188 | mRecoverySessionStorage, |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 189 | Executors.newSingleThreadExecutor(), |
Robert Berry | 9104404 | 2017-12-27 12:05:58 +0000 | [diff] [blame] | 190 | mRecoverySnapshotStorage, |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 191 | mMockListenersStorage, |
Dmitry Dementyev | 29b9de5 | 2018-01-31 16:09:32 -0800 | [diff] [blame] | 192 | mPlatformKeyManager, |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 193 | mApplicationKeyStorage, |
| 194 | mTestOnlyInsecureCertificateHelper); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | @After |
| 198 | public void tearDown() { |
| 199 | mRecoverableKeyStoreDb.close(); |
| 200 | mDatabaseFile.delete(); |
| 201 | } |
| 202 | |
| 203 | @Test |
Bo Zhu | 2c8e538 | 2018-02-26 15:54:25 -0800 | [diff] [blame] | 204 | 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 Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 231 | } catch (NullPointerException e) { |
| 232 | assertThat(e.getMessage()).contains("is null"); |
Bo Zhu | 2c8e538 | 2018-02-26 15:54:25 -0800 | [diff] [blame] | 233 | } |
| 234 | } |
| 235 | |
| 236 | @Test |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 237 | 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 Berry | 5daccec | 2018-01-06 19:16:25 +0000 | [diff] [blame] | 308 | public void removeKey_removesAKey() throws Exception { |
| 309 | int uid = Binder.getCallingUid(); |
Dmitry Dementyev | 86f5bb1 | 2018-03-27 16:58:50 -0700 | [diff] [blame] | 310 | mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); |
Robert Berry | 5daccec | 2018-01-06 19:16:25 +0000 | [diff] [blame] | 311 | |
| 312 | mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); |
| 313 | |
| 314 | assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNull(); |
| 315 | } |
| 316 | |
| 317 | @Test |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 318 | public void removeKey_updatesShouldCreateSnapshot() throws Exception { |
Dmitry Dementyev | 77183ef | 2018-01-05 15:46:00 -0800 | [diff] [blame] | 319 | int uid = Binder.getCallingUid(); |
| 320 | int userId = UserHandle.getCallingUserId(); |
Dmitry Dementyev | 86f5bb1 | 2018-03-27 16:58:50 -0700 | [diff] [blame] | 321 | mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); |
Dmitry Dementyev | 77183ef | 2018-01-05 15:46:00 -0800 | [diff] [blame] | 322 | // 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 Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 331 | 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 Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 342 | public void initRecoveryService_succeedsWithCertFile() throws Exception { |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 343 | int uid = Binder.getCallingUid(); |
| 344 | int userId = UserHandle.getCallingUserId(); |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 345 | long certSerial = 1000L; |
| 346 | mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); |
| 347 | |
| 348 | mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, |
| 349 | TestData.getCertXmlWithSerial(certSerial)); |
| 350 | |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 351 | verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) |
| 352 | .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS); |
| 353 | |
Robert Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 354 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 355 | 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 Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 359 | assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); |
| 360 | } |
| 361 | |
| 362 | @Test |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 363 | 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 Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 390 | 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 Zhu | 8d6861e | 2018-03-21 20:45:09 -0700 | [diff] [blame] | 451 | 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 Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 468 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 472 | mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, |
| 473 | modifiedCertXml); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 474 | fail("should have thrown"); |
| 475 | } catch (ServiceSpecificException e) { |
Bo Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 476 | assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 477 | } |
| 478 | } |
| 479 | |
| 480 | @Test |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 481 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 491 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, |
| 492 | DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1); |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 493 | // There were no keys. |
| 494 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 495 | } |
| 496 | |
| 497 | @Test |
Aseem Kumar | 23174b7 | 2018-04-03 11:35:51 -0700 | [diff] [blame] | 498 | public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception { |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 499 | 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 Kumar | 23174b7 | 2018-04-03 11:35:51 -0700 | [diff] [blame] | 505 | 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 Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 512 | } |
| 513 | |
| 514 | @Test |
Bo Zhu | b10ba44 | 2018-03-31 17:13:46 -0700 | [diff] [blame] | 515 | 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 Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 561 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 568 | |
| 569 | generateKeyAndSimulateSync(userId, uid, 10); |
| 570 | |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 571 | mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, |
| 572 | TestData.getCertXmlWithSerial(certSerial)); |
| 573 | |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 574 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
| 575 | } |
| 576 | |
| 577 | @Test |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 578 | public void initRecoveryService_throwsIfRawPublicKey() throws Exception { |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 579 | int uid = Binder.getCallingUid(); |
| 580 | int userId = UserHandle.getCallingUserId(); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 581 | mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); |
| 582 | |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 583 | 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 Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 590 | |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 591 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 592 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, |
| 593 | DEFAULT_ROOT_CERT_ALIAS)).isNull(); |
| 594 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, |
| 595 | DEFAULT_ROOT_CERT_ALIAS)).isNull(); |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 596 | assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 597 | } |
| 598 | |
| 599 | @Test |
Bo Zhu | 0b8c82e | 2018-03-30 11:31:53 -0700 | [diff] [blame] | 600 | 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 Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 611 | 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 Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 619 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 620 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, |
| 621 | DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 622 | assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); |
| 623 | } |
| 624 | |
| 625 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 626 | 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 Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 644 | 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 Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 650 | } catch (NullPointerException e) { |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 651 | 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 Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 662 | } catch (NullPointerException e) { |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 663 | 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 Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 675 | assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 676 | } |
| 677 | } |
| 678 | |
| 679 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 680 | 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 Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 687 | assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 688 | } |
| 689 | } |
| 690 | |
| 691 | @Test |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 692 | 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 705 | 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 711 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 712 | |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 713 | verify(mMockContext, times(1)) |
| 714 | .enforceCallingOrSelfPermission( |
Dmitry Dementyev | ed89ea0 | 2018-01-11 13:53:52 -0800 | [diff] [blame] | 715 | eq(Manifest.permission.RECOVER_KEYSTORE), any()); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 716 | } |
| 717 | |
| 718 | @Test |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 719 | public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception { |
| 720 | mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( |
| 721 | TEST_SESSION_ID, |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 722 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 723 | RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), |
| 724 | TEST_VAULT_PARAMS, |
| 725 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 726 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 727 | |
| 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 739 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 740 | RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), |
| 741 | TEST_VAULT_PARAMS, |
| 742 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 743 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 744 | |
| 745 | verify(mMockContext, times(2)) |
| 746 | .enforceCallingOrSelfPermission( |
| 747 | eq(Manifest.permission.RECOVER_KEYSTORE), any()); |
| 748 | } |
| 749 | |
| 750 | @Test |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 751 | 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 757 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 758 | |
| 759 | assertEquals(1, mRecoverySessionStorage.size()); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 760 | RecoverySessionStorage.Entry entry = |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 761 | mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 762 | assertArrayEquals(TEST_SECRET, entry.getLskfHash()); |
| 763 | assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length); |
| 764 | } |
| 765 | |
| 766 | @Test |
Robert Berry | 2bcdad9 | 2018-01-18 12:53:29 +0000 | [diff] [blame] | 767 | 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 773 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | 2bcdad9 | 2018-01-18 12:53:29 +0000 | [diff] [blame] | 774 | |
| 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 787 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | 2bcdad9 | 2018-01-18 12:53:29 +0000 | [diff] [blame] | 788 | |
| 789 | mRecoverableKeyStoreManager.closeSession("some random session"); |
| 790 | |
| 791 | assertEquals(1, mRecoverySessionStorage.size()); |
| 792 | } |
| 793 | |
| 794 | @Test |
Dmitry Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 795 | 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 805 | 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 Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 812 | ImmutableList.of()); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 813 | fail("should have thrown"); |
Dmitry Dementyev | ae6ec6d | 2018-01-18 14:29:49 -0800 | [diff] [blame] | 814 | } catch (UnsupportedOperationException e) { |
Dmitry Dementyev | 77183ef | 2018-01-05 15:46:00 -0800 | [diff] [blame] | 815 | assertThat(e.getMessage()).startsWith( |
Dmitry Dementyev | 0916e7c | 2018-01-23 13:02:08 -0800 | [diff] [blame] | 816 | "Only a single KeyChainProtectionParams is supported"); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 817 | } |
| 818 | } |
| 819 | |
| 820 | @Test |
Bo Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 821 | public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception { |
| 822 | byte[] vaultParams = TEST_VAULT_PARAMS.clone(); |
| 823 | vaultParams[1] ^= (byte) 1; // Flip 1 bit |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 824 | |
Bo Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 825 | try { |
| 826 | mRecoverableKeyStoreManager.startRecoverySession( |
| 827 | TEST_SESSION_ID, |
| 828 | TEST_PUBLIC_KEY, |
| 829 | vaultParams, |
| 830 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 831 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 832 | fail("should have thrown"); |
| 833 | } catch (ServiceSpecificException e) { |
| 834 | assertThat(e.getMessage()).contains("do not match"); |
| 835 | } |
| 836 | } |
| 837 | |
| 838 | @Test |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 839 | public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception { |
| 840 | try { |
| 841 | mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( |
| 842 | TEST_SESSION_ID, |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 843 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 844 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 862 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 863 | RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), |
| 864 | vaultParams, |
| 865 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 866 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 867 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 880 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 881 | RecoveryCertPath.createRecoveryCertPath(emptyCertPath), |
| 882 | TEST_VAULT_PARAMS, |
| 883 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 884 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 885 | fail("should have thrown"); |
| 886 | } catch (ServiceSpecificException e) { |
Bo Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 887 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 900 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 901 | RecoveryCertPath.createRecoveryCertPath(shortCertPath), |
| 902 | TEST_VAULT_PARAMS, |
| 903 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 904 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 905 | fail("should have thrown"); |
| 906 | } catch (ServiceSpecificException e) { |
| 907 | // expected |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 908 | } |
| 909 | } |
| 910 | |
| 911 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 912 | public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 913 | try { |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 914 | WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder() |
| 915 | .setAlias(TEST_ALIAS) |
| 916 | .setEncryptedKeyMaterial(randomBytes(32)) |
| 917 | .build(); |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 918 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 919 | TEST_SESSION_ID, |
| 920 | /*recoveryKeyBlob=*/ randomBytes(32), |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 921 | /*applicationKeys=*/ ImmutableList.of(applicationKey)); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 922 | fail("should have thrown"); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 923 | } catch (ServiceSpecificException e) { |
| 924 | // expected |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 925 | } |
| 926 | } |
| 927 | |
| 928 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 929 | public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 930 | mRecoverableKeyStoreManager.startRecoverySession( |
| 931 | TEST_SESSION_ID, |
| 932 | TEST_PUBLIC_KEY, |
| 933 | TEST_VAULT_PARAMS, |
| 934 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 935 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 936 | |
| 937 | try { |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 938 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 939 | TEST_SESSION_ID, |
| 940 | /*encryptedRecoveryKey=*/ randomBytes(60), |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 941 | /*applicationKeys=*/ ImmutableList.of()); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 942 | fail("should have thrown"); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 943 | } catch (ServiceSpecificException e) { |
| 944 | assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key"); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 945 | } |
| 946 | } |
| 947 | |
| 948 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 949 | public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() |
| 950 | throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 951 | mRecoverableKeyStoreManager.startRecoverySession( |
| 952 | TEST_SESSION_ID, |
| 953 | TEST_PUBLIC_KEY, |
| 954 | TEST_VAULT_PARAMS, |
| 955 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 956 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 957 | byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 958 | .getKeyClaimant(); |
| 959 | SecretKey recoveryKey = randomRecoveryKey(); |
| 960 | byte[] encryptedClaimResponse = encryptClaimResponse( |
| 961 | keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 962 | WrappedApplicationKey badApplicationKey = new WrappedApplicationKey.Builder() |
| 963 | .setAlias(TEST_ALIAS) |
| 964 | .setEncryptedKeyMaterial( |
| 965 | encryptedApplicationKey(randomRecoveryKey(), randomBytes(32))) |
| 966 | .build(); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 967 | try { |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 968 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 969 | TEST_SESSION_ID, |
| 970 | /*encryptedRecoveryKey=*/ encryptedClaimResponse, |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 971 | /*applicationKeys=*/ ImmutableList.of(badApplicationKey)); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 972 | fail("should have thrown"); |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 973 | } catch (ServiceSpecificException e) { |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 974 | assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys"); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 975 | } |
| 976 | } |
| 977 | |
| 978 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 979 | public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted() |
| 980 | throws Exception { |
Bo Zhu | ae0682d | 2018-02-13 10:23:39 -0800 | [diff] [blame] | 981 | mRecoverableKeyStoreManager.startRecoverySession( |
| 982 | TEST_SESSION_ID, |
| 983 | TEST_PUBLIC_KEY, |
| 984 | TEST_VAULT_PARAMS, |
| 985 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 986 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | ae0682d | 2018-02-13 10:23:39 -0800 | [diff] [blame] | 987 | 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 Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 993 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Bo Zhu | ae0682d | 2018-02-13 10:23:39 -0800 | [diff] [blame] | 994 | TEST_SESSION_ID, |
| 995 | /*encryptedRecoveryKey=*/ encryptedClaimResponse, |
| 996 | /*applicationKeys=*/ ImmutableList.of()); |
| 997 | } |
| 998 | |
| 999 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1000 | public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1001 | mRecoverableKeyStoreManager.startRecoverySession( |
| 1002 | TEST_SESSION_ID, |
| 1003 | TEST_PUBLIC_KEY, |
| 1004 | TEST_VAULT_PARAMS, |
| 1005 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 1006 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1007 | byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1008 | .getKeyClaimant(); |
| 1009 | SecretKey recoveryKey = randomRecoveryKey(); |
| 1010 | byte[] encryptedClaimResponse = encryptClaimResponse( |
| 1011 | keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); |
Robert Berry | bd4c43c | 2017-12-22 11:35:14 +0000 | [diff] [blame] | 1012 | byte[] applicationKeyBytes = randomBytes(32); |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 1013 | WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder() |
| 1014 | .setAlias(TEST_ALIAS) |
| 1015 | .setEncryptedKeyMaterial( |
| 1016 | encryptedApplicationKey(recoveryKey, applicationKeyBytes)) |
| 1017 | .build(); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1018 | |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1019 | Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1020 | TEST_SESSION_ID, |
| 1021 | encryptedClaimResponse, |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1022 | ImmutableList.of(applicationKey)); |
Robert Berry | bd4c43c | 2017-12-22 11:35:14 +0000 | [diff] [blame] | 1023 | |
| 1024 | assertThat(recoveredKeys).hasSize(1); |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1025 | assertThat(recoveredKeys).containsKey(TEST_ALIAS); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1026 | } |
| 1027 | |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 1028 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 1029 | public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() |
| 1030 | throws Exception { |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1031 | mRecoverableKeyStoreManager.startRecoverySession( |
| 1032 | TEST_SESSION_ID, |
| 1033 | TEST_PUBLIC_KEY, |
| 1034 | TEST_VAULT_PARAMS, |
| 1035 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 1036 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1037 | 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 Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 1045 | 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 Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1056 | |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1057 | Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1058 | TEST_SESSION_ID, |
| 1059 | encryptedClaimResponse, |
| 1060 | ImmutableList.of(applicationKey1, applicationKey2)); |
| 1061 | |
| 1062 | assertThat(recoveredKeys).hasSize(1); |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1063 | assertThat(recoveredKeys).containsKey(TEST_ALIAS2); |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1064 | } |
| 1065 | |
| 1066 | @Test |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 1067 | 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 Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1072 | mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent); |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 1073 | verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class)); |
| 1074 | } |
| 1075 | |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1076 | @Test |
Robert Berry | 8f9038c | 2018-03-26 11:36:40 +0100 | [diff] [blame] | 1077 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1105 | |
| 1106 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1107 | |
Robert Berry | 8f9038c | 2018-03-26 11:36:40 +0100 | [diff] [blame] | 1108 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1119 | |
| 1120 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1121 | |
Robert Berry | 8f9038c | 2018-03-26 11:36:40 +0100 | [diff] [blame] | 1122 | mRecoverableKeyStoreManager.setServerParams(new byte[] { 2 }); |
| 1123 | |
| 1124 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); |
| 1125 | } |
| 1126 | |
| 1127 | @Test |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1128 | public void setRecoverySecretTypes_updatesSecretTypes() throws Exception { |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1129 | int[] types1 = new int[]{11, 2000}; |
| 1130 | int[] types2 = new int[]{1, 2, 3}; |
| 1131 | int[] types3 = new int[]{}; |
| 1132 | |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1133 | mRecoverableKeyStoreManager.setRecoverySecretTypes(types1); |
| 1134 | assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1135 | types1); |
| 1136 | |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1137 | mRecoverableKeyStoreManager.setRecoverySecretTypes(types2); |
| 1138 | assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1139 | types2); |
| 1140 | |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1141 | mRecoverableKeyStoreManager.setRecoverySecretTypes(types3); |
| 1142 | assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1143 | types3); |
| 1144 | } |
| 1145 | |
| 1146 | @Test |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1147 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1154 | // There were no keys. |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1155 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1166 | |
| 1167 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1168 | |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1169 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1180 | |
| 1181 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
| 1182 | |
| 1183 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1184 | |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1185 | mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 }); |
| 1186 | |
| 1187 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); |
| 1188 | } |
| 1189 | |
| 1190 | @Test |
Dmitry Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 1191 | 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 Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 1201 | public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception { |
| 1202 | int uid = Binder.getCallingUid(); |
| 1203 | int userId = UserHandle.getCallingUserId(); |
Robert Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 1204 | mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 }); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 1205 | |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1206 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1207 | |
Robert Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 1208 | mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 }); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 1209 | |
| 1210 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); |
| 1211 | } |
| 1212 | |
| 1213 | @Test |
Robert Berry | bbe02ae | 2018-02-20 19:47:43 +0000 | [diff] [blame] | 1214 | public void setRecoveryStatus() throws Exception { |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1215 | int userId = UserHandle.getCallingUserId(); |
| 1216 | int uid = Binder.getCallingUid(); |
| 1217 | int status = 100; |
| 1218 | int status2 = 200; |
| 1219 | String alias = "key1"; |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 1220 | byte[] keyMetadata = null; |
| 1221 | |
| 1222 | WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID, |
| 1223 | status); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1224 | mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); |
| 1225 | Map<String, Integer> statuses = |
Robert Berry | 56f06b4 | 2018-02-23 13:31:32 +0000 | [diff] [blame] | 1226 | mRecoverableKeyStoreManager.getRecoveryStatus(); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1227 | assertThat(statuses).hasSize(1); |
| 1228 | assertThat(statuses).containsEntry(alias, status); |
| 1229 | |
Robert Berry | bbe02ae | 2018-02-20 19:47:43 +0000 | [diff] [blame] | 1230 | mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2); |
Robert Berry | 56f06b4 | 2018-02-23 13:31:32 +0000 | [diff] [blame] | 1231 | statuses = mRecoverableKeyStoreManager.getRecoveryStatus(); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1232 | assertThat(statuses).hasSize(1); |
| 1233 | assertThat(statuses).containsEntry(alias, status2); // updated |
| 1234 | } |
| 1235 | |
Dmitry Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 1236 | @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 Berry | bd4c43c | 2017-12-22 11:35:14 +0000 | [diff] [blame] | 1246 | private static byte[] encryptedApplicationKey( |
| 1247 | SecretKey recoveryKey, byte[] applicationKey) throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1248 | return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of( |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 1249 | TEST_ALIAS, |
| 1250 | Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null) |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 1251 | )).get(TEST_ALIAS); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1252 | } |
| 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 1272 | private static byte[] getUtf8Bytes(String s) { |
| 1273 | return s.getBytes(StandardCharsets.UTF_8); |
| 1274 | } |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1275 | |
| 1276 | private static byte[] randomBytes(int n) { |
| 1277 | byte[] bytes = new byte[n]; |
| 1278 | new Random().nextBytes(bytes); |
| 1279 | return bytes; |
| 1280 | } |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 1281 | |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1282 | private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion) |
| 1283 | throws Exception{ |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 1284 | mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL); |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1285 | // Simulate key sync. |
| 1286 | mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion); |
| 1287 | mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); |
| 1288 | } |
| 1289 | |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 1290 | 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 1301 | } |