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; |
Dmitry Dementyev | 89f12d5 | 2019-02-28 12:26:01 -0800 | [diff] [blame] | 62 | import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 63 | import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; |
| 64 | import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage; |
Robert Berry | bd086f1 | 2017-12-27 13:29:39 +0000 | [diff] [blame] | 65 | import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 66 | |
| 67 | import com.google.common.collect.ImmutableList; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 68 | import com.google.common.collect.ImmutableMap; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 69 | |
| 70 | import org.junit.After; |
| 71 | import org.junit.Before; |
| 72 | import org.junit.Test; |
| 73 | import org.junit.runner.RunWith; |
| 74 | import org.mockito.Mock; |
| 75 | import org.mockito.MockitoAnnotations; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 76 | import org.mockito.Spy; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 77 | |
| 78 | import java.io.File; |
| 79 | import java.nio.charset.StandardCharsets; |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 80 | import java.security.cert.CertPath; |
| 81 | import java.security.cert.CertificateFactory; |
| 82 | import java.security.cert.X509Certificate; |
| 83 | import java.util.ArrayList; |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 84 | import java.util.Map; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 85 | import java.util.Random; |
Brett Chabot | a26eda9 | 2018-07-23 13:08:30 -0700 | [diff] [blame] | 86 | import java.util.concurrent.Executors; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 87 | |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 88 | import javax.crypto.KeyGenerator; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 89 | import javax.crypto.SecretKey; |
| 90 | import javax.crypto.spec.SecretKeySpec; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 91 | |
| 92 | @SmallTest |
| 93 | @RunWith(AndroidJUnit4.class) |
| 94 | public class RecoverableKeyStoreManagerTest { |
| 95 | private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; |
| 96 | |
Bo Zhu | b31ab67 | 2018-03-20 22:44:18 -0700 | [diff] [blame] | 97 | private static final String ROOT_CERTIFICATE_ALIAS = ""; |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 98 | private static final String DEFAULT_ROOT_CERT_ALIAS = |
| 99 | TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 100 | private static final String INSECURE_CERTIFICATE_ALIAS = |
| 101 | TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 102 | private static final String TEST_SESSION_ID = "karlin"; |
Bo Zhu | bd7879c | 2018-05-11 05:10:28 +0000 | [diff] [blame] | 103 | 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] | 104 | 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 Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 107 | private static final byte[] TEST_VAULT_PARAMS = new byte[] { |
| 108 | // backend_key |
Bo Zhu | bd7879c | 2018-05-11 05:10:28 +0000 | [diff] [blame] | 109 | (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 Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 119 | // 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 Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 127 | private static final int TEST_GENERATION_ID = 2; |
| 128 | private static final int TEST_USER_ID = 10009; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 129 | private static final int KEY_CLAIMANT_LENGTH_BYTES = 16; |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 130 | 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 Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 133 | private static final String TEST_ALIAS2 = "bob"; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 134 | private static final int RECOVERABLE_KEY_SIZE_BYTES = 32; |
Bo Zhu | 2c8e538 | 2018-02-26 15:54:25 -0800 | [diff] [blame] | 135 | private static final int APPLICATION_KEY_SIZE_BYTES = 32; |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 136 | 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 Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 139 | private static final byte[] KEY_METADATA_NULL = null; |
| 140 | private static final byte[] KEY_METADATA_NON_NULL = getUtf8Bytes("keymetametadata"); |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 141 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 144 | private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = ""; |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 145 | 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 Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 152 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 153 | @Mock private Context mMockContext; |
Robert Berry | 9104404 | 2017-12-27 12:05:58 +0000 | [diff] [blame] | 154 | @Mock private RecoverySnapshotListenersStorage mMockListenersStorage; |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 155 | @Mock private KeyguardManager mKeyguardManager; |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 156 | @Mock private PlatformKeyManager mPlatformKeyManager; |
Dmitry Dementyev | 29b9de5 | 2018-01-31 16:09:32 -0800 | [diff] [blame] | 157 | @Mock private ApplicationKeyStorage mApplicationKeyStorage; |
Dmitry Dementyev | 89f12d5 | 2019-02-28 12:26:01 -0800 | [diff] [blame] | 158 | @Mock private CleanupManager mCleanupManager; |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 159 | @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 160 | |
| 161 | private RecoverableKeyStoreDb mRecoverableKeyStoreDb; |
| 162 | private File mDatabaseFile; |
| 163 | private RecoverableKeyStoreManager mRecoverableKeyStoreManager; |
| 164 | private RecoverySessionStorage mRecoverySessionStorage; |
Robert Berry | bd086f1 | 2017-12-27 13:29:39 +0000 | [diff] [blame] | 165 | private RecoverySnapshotStorage mRecoverySnapshotStorage; |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 166 | private PlatformEncryptionKey mPlatformEncryptionKey; |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 167 | |
| 168 | @Before |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 169 | public void setUp() throws Exception { |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 170 | MockitoAnnotations.initMocks(this); |
| 171 | |
| 172 | Context context = InstrumentationRegistry.getTargetContext(); |
| 173 | mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME); |
| 174 | mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context); |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 175 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 176 | mRecoverySessionStorage = new RecoverySessionStorage(); |
Robert Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 177 | |
| 178 | when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager); |
| 179 | when(mMockContext.getSystemServiceName(any())).thenReturn("test"); |
Robert Berry | 3ae5bea | 2017-12-27 10:58:03 +0000 | [diff] [blame] | 180 | when(mMockContext.getApplicationContext()).thenReturn(mMockContext); |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 181 | 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 Berry | cfc990a | 2017-12-22 15:54:30 +0000 | [diff] [blame] | 186 | |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 187 | mRecoverableKeyStoreManager = new RecoverableKeyStoreManager( |
| 188 | mMockContext, |
| 189 | mRecoverableKeyStoreDb, |
Robert Berry | 4a534ec | 2017-12-21 15:44:02 +0000 | [diff] [blame] | 190 | mRecoverySessionStorage, |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 191 | Executors.newSingleThreadExecutor(), |
Robert Berry | 9104404 | 2017-12-27 12:05:58 +0000 | [diff] [blame] | 192 | mRecoverySnapshotStorage, |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 193 | mMockListenersStorage, |
Dmitry Dementyev | 29b9de5 | 2018-01-31 16:09:32 -0800 | [diff] [blame] | 194 | mPlatformKeyManager, |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 195 | mApplicationKeyStorage, |
Dmitry Dementyev | 89f12d5 | 2019-02-28 12:26:01 -0800 | [diff] [blame] | 196 | mTestOnlyInsecureCertificateHelper, |
| 197 | mCleanupManager); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | @After |
| 201 | public void tearDown() { |
| 202 | mRecoverableKeyStoreDb.close(); |
| 203 | mDatabaseFile.delete(); |
| 204 | } |
| 205 | |
| 206 | @Test |
Bo Zhu | 2c8e538 | 2018-02-26 15:54:25 -0800 | [diff] [blame] | 207 | 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 Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 234 | } catch (NullPointerException e) { |
| 235 | assertThat(e.getMessage()).contains("is null"); |
Bo Zhu | 2c8e538 | 2018-02-26 15:54:25 -0800 | [diff] [blame] | 236 | } |
| 237 | } |
| 238 | |
| 239 | @Test |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 240 | 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 Berry | 5daccec | 2018-01-06 19:16:25 +0000 | [diff] [blame] | 311 | public void removeKey_removesAKey() throws Exception { |
| 312 | int uid = Binder.getCallingUid(); |
Dmitry Dementyev | 86f5bb1 | 2018-03-27 16:58:50 -0700 | [diff] [blame] | 313 | mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); |
Robert Berry | 5daccec | 2018-01-06 19:16:25 +0000 | [diff] [blame] | 314 | |
| 315 | mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); |
| 316 | |
| 317 | assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNull(); |
| 318 | } |
| 319 | |
| 320 | @Test |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 321 | public void removeKey_updatesShouldCreateSnapshot() throws Exception { |
Dmitry Dementyev | 77183ef | 2018-01-05 15:46:00 -0800 | [diff] [blame] | 322 | int uid = Binder.getCallingUid(); |
| 323 | int userId = UserHandle.getCallingUserId(); |
Dmitry Dementyev | 86f5bb1 | 2018-03-27 16:58:50 -0700 | [diff] [blame] | 324 | mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); |
Dmitry Dementyev | 77183ef | 2018-01-05 15:46:00 -0800 | [diff] [blame] | 325 | // 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 Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 334 | 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 Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 345 | public void initRecoveryService_succeedsWithCertFile() throws Exception { |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 346 | int uid = Binder.getCallingUid(); |
| 347 | int userId = UserHandle.getCallingUserId(); |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 348 | long certSerial = 1000L; |
| 349 | mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); |
| 350 | |
| 351 | mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, |
| 352 | TestData.getCertXmlWithSerial(certSerial)); |
| 353 | |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 354 | verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) |
| 355 | .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS); |
| 356 | |
Robert Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 357 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 358 | 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 Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 362 | assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); |
| 363 | } |
| 364 | |
| 365 | @Test |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 366 | 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 Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 393 | 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 Zhu | 8d6861e | 2018-03-21 20:45:09 -0700 | [diff] [blame] | 454 | 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 Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 471 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 475 | mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, |
| 476 | modifiedCertXml); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 477 | fail("should have thrown"); |
| 478 | } catch (ServiceSpecificException e) { |
Bo Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 479 | assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 480 | } |
| 481 | } |
| 482 | |
| 483 | @Test |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 484 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 494 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, |
| 495 | DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1); |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 496 | // There were no keys. |
| 497 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 498 | } |
| 499 | |
| 500 | @Test |
Aseem Kumar | 23174b7 | 2018-04-03 11:35:51 -0700 | [diff] [blame] | 501 | public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception { |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 502 | 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 Kumar | 23174b7 | 2018-04-03 11:35:51 -0700 | [diff] [blame] | 508 | 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 Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 515 | } |
| 516 | |
| 517 | @Test |
Bo Zhu | b10ba44 | 2018-03-31 17:13:46 -0700 | [diff] [blame] | 518 | 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 Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 564 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 571 | |
| 572 | generateKeyAndSimulateSync(userId, uid, 10); |
| 573 | |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 574 | mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, |
| 575 | TestData.getCertXmlWithSerial(certSerial)); |
| 576 | |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 577 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
| 578 | } |
| 579 | |
| 580 | @Test |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 581 | public void initRecoveryService_throwsIfRawPublicKey() throws Exception { |
Bo Zhu | 14d993d | 2018-02-03 21:38:48 -0800 | [diff] [blame] | 582 | int uid = Binder.getCallingUid(); |
| 583 | int userId = UserHandle.getCallingUserId(); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 584 | mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); |
| 585 | |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 586 | 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 Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 593 | |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 594 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 595 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, |
| 596 | DEFAULT_ROOT_CERT_ALIAS)).isNull(); |
| 597 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid, |
| 598 | DEFAULT_ROOT_CERT_ALIAS)).isNull(); |
Bo Zhu | c98c843 | 2018-03-31 13:08:49 -0700 | [diff] [blame] | 599 | assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 600 | } |
| 601 | |
| 602 | @Test |
Bo Zhu | 0b8c82e | 2018-03-30 11:31:53 -0700 | [diff] [blame] | 603 | 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 Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 614 | 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 Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 622 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 623 | assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid, |
| 624 | DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 625 | assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); |
| 626 | } |
| 627 | |
| 628 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 629 | 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 Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 647 | 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 Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 653 | } catch (NullPointerException e) { |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 654 | 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 Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 665 | } catch (NullPointerException e) { |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 666 | 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 Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 678 | assertThat(e.errorCode).isEqualTo(ERROR_BAD_CERTIFICATE_FORMAT); |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 679 | } |
| 680 | } |
| 681 | |
| 682 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 683 | 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 Zhu | 41d2dd2 | 2018-03-30 12:20:06 -0700 | [diff] [blame] | 690 | assertThat(e.errorCode).isEqualTo(ERROR_INVALID_CERTIFICATE); |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 691 | } |
| 692 | } |
| 693 | |
| 694 | @Test |
Bo Zhu | 7f414d9 | 2018-02-28 09:28:19 -0800 | [diff] [blame] | 695 | 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 708 | 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 714 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 715 | |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 716 | verify(mMockContext, times(1)) |
| 717 | .enforceCallingOrSelfPermission( |
Dmitry Dementyev | ed89ea0 | 2018-01-11 13:53:52 -0800 | [diff] [blame] | 718 | eq(Manifest.permission.RECOVER_KEYSTORE), any()); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 719 | } |
| 720 | |
| 721 | @Test |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 722 | public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception { |
| 723 | mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( |
| 724 | TEST_SESSION_ID, |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 725 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 726 | RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), |
| 727 | TEST_VAULT_PARAMS, |
| 728 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 729 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 730 | |
| 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 742 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 743 | RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), |
| 744 | TEST_VAULT_PARAMS, |
| 745 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 746 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 747 | |
| 748 | verify(mMockContext, times(2)) |
| 749 | .enforceCallingOrSelfPermission( |
| 750 | eq(Manifest.permission.RECOVER_KEYSTORE), any()); |
| 751 | } |
| 752 | |
| 753 | @Test |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 754 | 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 760 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 761 | |
| 762 | assertEquals(1, mRecoverySessionStorage.size()); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 763 | RecoverySessionStorage.Entry entry = |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 764 | mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 765 | assertArrayEquals(TEST_SECRET, entry.getLskfHash()); |
| 766 | assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length); |
| 767 | } |
| 768 | |
| 769 | @Test |
Robert Berry | 2bcdad9 | 2018-01-18 12:53:29 +0000 | [diff] [blame] | 770 | 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 776 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | 2bcdad9 | 2018-01-18 12:53:29 +0000 | [diff] [blame] | 777 | |
| 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 Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 790 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | 2bcdad9 | 2018-01-18 12:53:29 +0000 | [diff] [blame] | 791 | |
| 792 | mRecoverableKeyStoreManager.closeSession("some random session"); |
| 793 | |
| 794 | assertEquals(1, mRecoverySessionStorage.size()); |
| 795 | } |
| 796 | |
| 797 | @Test |
Dmitry Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 798 | 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 808 | 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 Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 815 | ImmutableList.of()); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 816 | fail("should have thrown"); |
Dmitry Dementyev | ae6ec6d | 2018-01-18 14:29:49 -0800 | [diff] [blame] | 817 | } catch (UnsupportedOperationException e) { |
Dmitry Dementyev | 77183ef | 2018-01-05 15:46:00 -0800 | [diff] [blame] | 818 | assertThat(e.getMessage()).startsWith( |
Dmitry Dementyev | 0916e7c | 2018-01-23 13:02:08 -0800 | [diff] [blame] | 819 | "Only a single KeyChainProtectionParams is supported"); |
Robert Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 820 | } |
| 821 | } |
| 822 | |
| 823 | @Test |
Bo Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 824 | public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception { |
| 825 | byte[] vaultParams = TEST_VAULT_PARAMS.clone(); |
| 826 | vaultParams[1] ^= (byte) 1; // Flip 1 bit |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 827 | |
Bo Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 828 | try { |
| 829 | mRecoverableKeyStoreManager.startRecoverySession( |
| 830 | TEST_SESSION_ID, |
| 831 | TEST_PUBLIC_KEY, |
| 832 | vaultParams, |
| 833 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 834 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | def7ffd | 2018-01-05 14:50:52 -0800 | [diff] [blame] | 835 | fail("should have thrown"); |
| 836 | } catch (ServiceSpecificException e) { |
| 837 | assertThat(e.getMessage()).contains("do not match"); |
| 838 | } |
| 839 | } |
| 840 | |
| 841 | @Test |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 842 | public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception { |
| 843 | try { |
| 844 | mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( |
| 845 | TEST_SESSION_ID, |
Dmitry Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 846 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 847 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 865 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 866 | RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), |
| 867 | vaultParams, |
| 868 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 869 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 870 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 883 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 884 | RecoveryCertPath.createRecoveryCertPath(emptyCertPath), |
| 885 | TEST_VAULT_PARAMS, |
| 886 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 887 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 888 | fail("should have thrown"); |
| 889 | } catch (ServiceSpecificException e) { |
Bo Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 890 | 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 Dementyev | f34fc7e | 2018-03-26 17:31:29 -0700 | [diff] [blame] | 903 | TEST_DEFAULT_ROOT_CERT_ALIAS, |
Bo Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 904 | RecoveryCertPath.createRecoveryCertPath(shortCertPath), |
| 905 | TEST_VAULT_PARAMS, |
| 906 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 907 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 7ce4ea5 | 2018-02-27 23:52:19 -0800 | [diff] [blame] | 908 | fail("should have thrown"); |
| 909 | } catch (ServiceSpecificException e) { |
| 910 | // expected |
Bo Zhu | 7c1972f | 2018-02-22 21:43:52 -0800 | [diff] [blame] | 911 | } |
| 912 | } |
| 913 | |
| 914 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 915 | public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 916 | try { |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 917 | WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder() |
| 918 | .setAlias(TEST_ALIAS) |
| 919 | .setEncryptedKeyMaterial(randomBytes(32)) |
| 920 | .build(); |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 921 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 922 | TEST_SESSION_ID, |
| 923 | /*recoveryKeyBlob=*/ randomBytes(32), |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 924 | /*applicationKeys=*/ ImmutableList.of(applicationKey)); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 925 | fail("should have thrown"); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 926 | } catch (ServiceSpecificException e) { |
| 927 | // expected |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 928 | } |
| 929 | } |
| 930 | |
| 931 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 932 | public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 933 | mRecoverableKeyStoreManager.startRecoverySession( |
| 934 | TEST_SESSION_ID, |
| 935 | TEST_PUBLIC_KEY, |
| 936 | TEST_VAULT_PARAMS, |
| 937 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 938 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 939 | |
| 940 | try { |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 941 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 942 | TEST_SESSION_ID, |
| 943 | /*encryptedRecoveryKey=*/ randomBytes(60), |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 944 | /*applicationKeys=*/ ImmutableList.of()); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 945 | fail("should have thrown"); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 946 | } catch (ServiceSpecificException e) { |
| 947 | assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key"); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 948 | } |
| 949 | } |
| 950 | |
| 951 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 952 | public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() |
| 953 | throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 954 | mRecoverableKeyStoreManager.startRecoverySession( |
| 955 | TEST_SESSION_ID, |
| 956 | TEST_PUBLIC_KEY, |
| 957 | TEST_VAULT_PARAMS, |
| 958 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 959 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 960 | byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 961 | .getKeyClaimant(); |
| 962 | SecretKey recoveryKey = randomRecoveryKey(); |
| 963 | byte[] encryptedClaimResponse = encryptClaimResponse( |
| 964 | keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 965 | WrappedApplicationKey badApplicationKey = new WrappedApplicationKey.Builder() |
| 966 | .setAlias(TEST_ALIAS) |
| 967 | .setEncryptedKeyMaterial( |
| 968 | encryptedApplicationKey(randomRecoveryKey(), randomBytes(32))) |
| 969 | .build(); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 970 | try { |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 971 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 972 | TEST_SESSION_ID, |
| 973 | /*encryptedRecoveryKey=*/ encryptedClaimResponse, |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 974 | /*applicationKeys=*/ ImmutableList.of(badApplicationKey)); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 975 | fail("should have thrown"); |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 976 | } catch (ServiceSpecificException e) { |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 977 | assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys"); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 978 | } |
| 979 | } |
| 980 | |
| 981 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 982 | public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted() |
| 983 | throws Exception { |
Bo Zhu | ae0682d | 2018-02-13 10:23:39 -0800 | [diff] [blame] | 984 | mRecoverableKeyStoreManager.startRecoverySession( |
| 985 | TEST_SESSION_ID, |
| 986 | TEST_PUBLIC_KEY, |
| 987 | TEST_VAULT_PARAMS, |
| 988 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 989 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | ae0682d | 2018-02-13 10:23:39 -0800 | [diff] [blame] | 990 | 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 Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 996 | mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Bo Zhu | ae0682d | 2018-02-13 10:23:39 -0800 | [diff] [blame] | 997 | TEST_SESSION_ID, |
| 998 | /*encryptedRecoveryKey=*/ encryptedClaimResponse, |
| 999 | /*applicationKeys=*/ ImmutableList.of()); |
| 1000 | } |
| 1001 | |
| 1002 | @Test |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1003 | public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1004 | mRecoverableKeyStoreManager.startRecoverySession( |
| 1005 | TEST_SESSION_ID, |
| 1006 | TEST_PUBLIC_KEY, |
| 1007 | TEST_VAULT_PARAMS, |
| 1008 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 1009 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1010 | byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID) |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1011 | .getKeyClaimant(); |
| 1012 | SecretKey recoveryKey = randomRecoveryKey(); |
| 1013 | byte[] encryptedClaimResponse = encryptClaimResponse( |
| 1014 | keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); |
Robert Berry | bd4c43c | 2017-12-22 11:35:14 +0000 | [diff] [blame] | 1015 | byte[] applicationKeyBytes = randomBytes(32); |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 1016 | WrappedApplicationKey applicationKey = new WrappedApplicationKey.Builder() |
| 1017 | .setAlias(TEST_ALIAS) |
| 1018 | .setEncryptedKeyMaterial( |
| 1019 | encryptedApplicationKey(recoveryKey, applicationKeyBytes)) |
| 1020 | .build(); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1021 | |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1022 | Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1023 | TEST_SESSION_ID, |
| 1024 | encryptedClaimResponse, |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1025 | ImmutableList.of(applicationKey)); |
Robert Berry | bd4c43c | 2017-12-22 11:35:14 +0000 | [diff] [blame] | 1026 | |
| 1027 | assertThat(recoveredKeys).hasSize(1); |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1028 | assertThat(recoveredKeys).containsKey(TEST_ALIAS); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1029 | } |
| 1030 | |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 1031 | @Test |
Dmitry Dementyev | 57ca3da | 2018-03-28 12:36:45 -0700 | [diff] [blame] | 1032 | public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() |
| 1033 | throws Exception { |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1034 | mRecoverableKeyStoreManager.startRecoverySession( |
| 1035 | TEST_SESSION_ID, |
| 1036 | TEST_PUBLIC_KEY, |
| 1037 | TEST_VAULT_PARAMS, |
| 1038 | TEST_VAULT_CHALLENGE, |
Dmitry Dementyev | 16d9db5 | 2018-03-26 11:31:46 -0700 | [diff] [blame] | 1039 | ImmutableList.of(TEST_PROTECTION_PARAMS)); |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1040 | 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 Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 1048 | 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 Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1059 | |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1060 | Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1061 | TEST_SESSION_ID, |
| 1062 | encryptedClaimResponse, |
| 1063 | ImmutableList.of(applicationKey1, applicationKey2)); |
| 1064 | |
| 1065 | assertThat(recoveredKeys).hasSize(1); |
Robert Berry | 4a5c87d | 2018-03-19 18:00:46 +0000 | [diff] [blame] | 1066 | assertThat(recoveredKeys).containsKey(TEST_ALIAS2); |
Bo Zhu | 4857cb5 | 2018-02-06 14:34:48 -0800 | [diff] [blame] | 1067 | } |
| 1068 | |
| 1069 | @Test |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 1070 | 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 Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1075 | mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent); |
Dmitry Dementyev | 3b17c63 | 2017-12-21 17:30:48 -0800 | [diff] [blame] | 1076 | verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class)); |
| 1077 | } |
| 1078 | |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1079 | @Test |
Robert Berry | 8f9038c | 2018-03-26 11:36:40 +0100 | [diff] [blame] | 1080 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1108 | |
| 1109 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1110 | |
Robert Berry | 8f9038c | 2018-03-26 11:36:40 +0100 | [diff] [blame] | 1111 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1122 | |
| 1123 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1124 | |
Robert Berry | 8f9038c | 2018-03-26 11:36:40 +0100 | [diff] [blame] | 1125 | mRecoverableKeyStoreManager.setServerParams(new byte[] { 2 }); |
| 1126 | |
| 1127 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); |
| 1128 | } |
| 1129 | |
| 1130 | @Test |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1131 | public void setRecoverySecretTypes_updatesSecretTypes() throws Exception { |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1132 | int[] types1 = new int[]{11, 2000}; |
| 1133 | int[] types2 = new int[]{1, 2, 3}; |
| 1134 | int[] types3 = new int[]{}; |
| 1135 | |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1136 | mRecoverableKeyStoreManager.setRecoverySecretTypes(types1); |
| 1137 | assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1138 | types1); |
| 1139 | |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1140 | mRecoverableKeyStoreManager.setRecoverySecretTypes(types2); |
| 1141 | assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1142 | types2); |
| 1143 | |
Dmitry Dementyev | 1429831 | 2018-01-04 15:19:19 -0800 | [diff] [blame] | 1144 | mRecoverableKeyStoreManager.setRecoverySecretTypes(types3); |
| 1145 | assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo( |
Dmitry Dementyev | bdfdf53 | 2017-12-27 11:58:45 -0800 | [diff] [blame] | 1146 | types3); |
| 1147 | } |
| 1148 | |
| 1149 | @Test |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1150 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1157 | // There were no keys. |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1158 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1169 | |
| 1170 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1171 | |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1172 | 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 Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1183 | |
| 1184 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse(); |
| 1185 | |
| 1186 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1187 | |
Robert Berry | 5a1acefb | 2018-03-26 14:41:30 +0100 | [diff] [blame] | 1188 | mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 }); |
| 1189 | |
| 1190 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); |
| 1191 | } |
| 1192 | |
| 1193 | @Test |
Dmitry Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 1194 | 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 Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 1204 | public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception { |
| 1205 | int uid = Binder.getCallingUid(); |
| 1206 | int userId = UserHandle.getCallingUserId(); |
Robert Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 1207 | mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 }); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 1208 | |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1209 | generateKeyAndSimulateSync(userId, uid, 10); |
| 1210 | |
Robert Berry | e8edf97 | 2018-03-27 11:45:11 +0100 | [diff] [blame] | 1211 | mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 }); |
Dmitry Dementyev | 40dadb0 | 2018-01-10 18:03:37 -0800 | [diff] [blame] | 1212 | |
| 1213 | assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); |
| 1214 | } |
| 1215 | |
| 1216 | @Test |
Robert Berry | bbe02ae | 2018-02-20 19:47:43 +0000 | [diff] [blame] | 1217 | public void setRecoveryStatus() throws Exception { |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1218 | int userId = UserHandle.getCallingUserId(); |
| 1219 | int uid = Binder.getCallingUid(); |
| 1220 | int status = 100; |
| 1221 | int status2 = 200; |
| 1222 | String alias = "key1"; |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 1223 | byte[] keyMetadata = null; |
| 1224 | |
| 1225 | WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, keyMetadata, GENERATION_ID, |
| 1226 | status); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1227 | mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); |
| 1228 | Map<String, Integer> statuses = |
Robert Berry | 56f06b4 | 2018-02-23 13:31:32 +0000 | [diff] [blame] | 1229 | mRecoverableKeyStoreManager.getRecoveryStatus(); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1230 | assertThat(statuses).hasSize(1); |
| 1231 | assertThat(statuses).containsEntry(alias, status); |
| 1232 | |
Robert Berry | bbe02ae | 2018-02-20 19:47:43 +0000 | [diff] [blame] | 1233 | mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2); |
Robert Berry | 56f06b4 | 2018-02-23 13:31:32 +0000 | [diff] [blame] | 1234 | statuses = mRecoverableKeyStoreManager.getRecoveryStatus(); |
Dmitry Dementyev | ad88471 | 2017-12-20 12:38:36 -0800 | [diff] [blame] | 1235 | assertThat(statuses).hasSize(1); |
| 1236 | assertThat(statuses).containsEntry(alias, status2); // updated |
| 1237 | } |
| 1238 | |
Dmitry Dementyev | 1e6a9dc | 2018-03-21 13:52:00 -0700 | [diff] [blame] | 1239 | @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 Berry | bd4c43c | 2017-12-22 11:35:14 +0000 | [diff] [blame] | 1249 | private static byte[] encryptedApplicationKey( |
| 1250 | SecretKey recoveryKey, byte[] applicationKey) throws Exception { |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1251 | return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of( |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 1252 | TEST_ALIAS, |
| 1253 | Pair.create(new SecretKeySpec(applicationKey, "AES"), /*metadata=*/ null) |
Dmitry Dementyev | fd4ae0b | 2018-03-23 11:06:24 -0700 | [diff] [blame] | 1254 | )).get(TEST_ALIAS); |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1255 | } |
| 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 1275 | private static byte[] getUtf8Bytes(String s) { |
| 1276 | return s.getBytes(StandardCharsets.UTF_8); |
| 1277 | } |
Robert Berry | b9a220b | 2017-12-21 12:41:01 +0000 | [diff] [blame] | 1278 | |
| 1279 | private static byte[] randomBytes(int n) { |
| 1280 | byte[] bytes = new byte[n]; |
| 1281 | new Random().nextBytes(bytes); |
| 1282 | return bytes; |
| 1283 | } |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 1284 | |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1285 | private void generateKeyAndSimulateSync(int userId, int uid, int snapshotVersion) |
| 1286 | throws Exception{ |
Bo Zhu | 7ebcd66 | 2019-01-04 17:00:58 -0800 | [diff] [blame] | 1287 | mRecoverableKeyStoreManager.generateKeyWithMetadata(TEST_ALIAS, KEY_METADATA_NULL); |
Dmitry Dementyev | 925f026 | 2018-04-12 12:10:50 -0700 | [diff] [blame] | 1288 | // Simulate key sync. |
| 1289 | mRecoverableKeyStoreDb.setSnapshotVersion(userId, uid, snapshotVersion); |
| 1290 | mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); |
| 1291 | } |
| 1292 | |
Bo Zhu | 3462c83 | 2018-01-04 22:42:36 -0800 | [diff] [blame] | 1293 | 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 Berry | e16fa98 | 2017-12-20 15:59:37 +0000 | [diff] [blame] | 1304 | } |