blob: 35215c34d8f01b7a6c6c45c0952b03e9c9bc4c46 [file] [log] [blame]
Bo Zhu14d993d2018-02-03 21:38:48 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.locksettings.recoverablekeystore.storage;
18
19import static com.google.common.truth.Truth.assertThat;
20
Brett Chabota26eda92018-07-23 13:08:30 -070021import android.content.ContentValues;
22import android.content.Context;
23import android.database.sqlite.SQLiteDatabase;
24
25import androidx.test.InstrumentationRegistry;
26import androidx.test.filters.SmallTest;
27import androidx.test.runner.AndroidJUnit4;
28
29import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
30import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
31import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
32import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
Bo Zhu14d993d2018-02-03 21:38:48 -080033
34import org.junit.After;
35import org.junit.Before;
36import org.junit.Test;
37import org.junit.runner.RunWith;
38
Brett Chabota26eda92018-07-23 13:08:30 -070039import static java.nio.charset.StandardCharsets.UTF_8;
Bo Zhu14d993d2018-02-03 21:38:48 -080040
41@SmallTest
42@RunWith(AndroidJUnit4.class)
43public class RecoverableKeyStoreDbHelperTest {
44
45 private static final long TEST_USER_ID = 10L;
46 private static final long TEST_UID = 60001L;
47 private static final String TEST_ALIAS = "test-alias";
48 private static final byte[] TEST_NONCE = "test-nonce".getBytes(UTF_8);
49 private static final byte[] TEST_WRAPPED_KEY = "test-wrapped-key".getBytes(UTF_8);
50 private static final long TEST_GENERATION_ID = 13L;
51 private static final long TEST_LAST_SYNCED_AT = 1517990732000L;
52 private static final int TEST_RECOVERY_STATUS = 3;
53 private static final int TEST_PLATFORM_KEY_GENERATION_ID = 11;
54 private static final int TEST_SNAPSHOT_VERSION = 31;
55 private static final int TEST_SHOULD_CREATE_SNAPSHOT = 1;
56 private static final byte[] TEST_PUBLIC_KEY = "test-public-key".getBytes(UTF_8);
57 private static final String TEST_SECRET_TYPES = "test-secret-types";
58 private static final long TEST_COUNTER_ID = -3981205205038476415L;
59 private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8);
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070060 private static final String TEST_ROOT_ALIAS = "root_cert_alias";
Bo Zhu14d993d2018-02-03 21:38:48 -080061 private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
62 private static final long TEST_CERT_SERIAL = 1000L;
Bo Zhu7ebcd662019-01-04 17:00:58 -080063 private static final byte[] TEST_KEY_METADATA = "test-key-metadata".getBytes(UTF_8);
Bo Zhu14d993d2018-02-03 21:38:48 -080064
65 private static final String SQL_CREATE_V2_TABLE_KEYS =
66 "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
67 + KeysEntry._ID + " INTEGER PRIMARY KEY,"
68 + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER,"
69 + KeysEntry.COLUMN_NAME_UID + " INTEGER,"
70 + KeysEntry.COLUMN_NAME_ALIAS + " TEXT,"
71 + KeysEntry.COLUMN_NAME_NONCE + " BLOB,"
72 + KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
73 + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
74 + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
75 + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
76 + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
77 + KeysEntry.COLUMN_NAME_ALIAS + "))";
78
79 private static final String SQL_CREATE_V2_TABLE_USER_METADATA =
80 "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
81 + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
82 + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
83 + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
84
85 private static final String SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA =
86 "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
87 + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
88 + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER,"
89 + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
90 + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
91 + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
92 + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
93 + RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
94 + RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
95 + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
96 + "UNIQUE("
97 + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + ","
98 + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
99
100 private SQLiteDatabase mDatabase;
101 private RecoverableKeyStoreDbHelper mDatabaseHelper;
102
103 @Before
104 public void setUp() throws Exception {
105 Context context = InstrumentationRegistry.getTargetContext();
106 mDatabaseHelper = new RecoverableKeyStoreDbHelper(context);
107 mDatabase = SQLiteDatabase.create(null);
108 }
109
110 @After
111 public void tearDown() throws Exception {
112 mDatabase.close();
113 }
114
115 private void createV2Tables() throws Exception {
116 mDatabase.execSQL(SQL_CREATE_V2_TABLE_KEYS);
117 mDatabase.execSQL(SQL_CREATE_V2_TABLE_USER_METADATA);
118 mDatabase.execSQL(SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA);
119 }
120
121 @Test
122 public void onCreate() throws Exception {
123 mDatabaseHelper.onCreate(mDatabase);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800124 checkAllColumns_latest();
Bo Zhu14d993d2018-02-03 21:38:48 -0800125 }
126
127 @Test
128 public void onUpgrade_beforeV2() throws Exception {
129 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
130 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800131 checkAllColumns_latest();
Bo Zhu14d993d2018-02-03 21:38:48 -0800132 }
133
134 @Test
135 public void onUpgrade_fromV2() throws Exception {
136 createV2Tables();
137 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
138 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800139 checkAllColumns_latest();
Bo Zhu14d993d2018-02-03 21:38:48 -0800140 }
141
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700142 @Test
Bo Zhu7ebcd662019-01-04 17:00:58 -0800143 public void onUpgrade_v2_to_v3_to_v4_to_latest() throws Exception {
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700144 createV2Tables();
145
146 assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table;
147
148 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, /*newVersion=*/ 3);
149
150 assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table;
151
Bo Zhu7ebcd662019-01-04 17:00:58 -0800152 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, /*newVersion=*/ 4);
153 checkAllColumns_v4();
154
155 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700156 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800157 checkAllColumns_latest();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700158 }
159
160 private boolean isRootOfTrustTableAvailable() {
161 ContentValues values = new ContentValues();
162 values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
163 values.put(RootOfTrustEntry.COLUMN_NAME_UID, TEST_UID);
164 values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS);
165 values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
166 values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800167 return mDatabase.replace(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700168 > -1;
169 }
170
Bo Zhu7ebcd662019-01-04 17:00:58 -0800171 private void checkAllColumns_v4() throws Exception {
Bo Zhu14d993d2018-02-03 21:38:48 -0800172 // Check the table containing encrypted application keys
173 ContentValues values = new ContentValues();
174 values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
175 values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
176 values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
177 values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
178 values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
179 values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
180 values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
181 values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800182 assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
Bo Zhu14d993d2018-02-03 21:38:48 -0800183 .isGreaterThan(-1L);
184
185 // Check the table about user metadata
186 values = new ContentValues();
187 values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
188 values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID,
189 TEST_PLATFORM_KEY_GENERATION_ID);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800190 assertThat(
191 mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
Bo Zhu14d993d2018-02-03 21:38:48 -0800192 .isGreaterThan(-1L);
193
194 // Check the table about recovery service metadata
195 values = new ContentValues();
196 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
197 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_UID, TEST_UID);
198 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION,
199 TEST_SNAPSHOT_VERSION);
200 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT,
201 TEST_SHOULD_CREATE_SNAPSHOT);
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700202 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, TEST_ROOT_ALIAS);
Bo Zhu14d993d2018-02-03 21:38:48 -0800203 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY);
204 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES);
205 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID);
206 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS, TEST_SERVER_PARAMS);
207 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
208 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
209 assertThat(
Bo Zhu7ebcd662019-01-04 17:00:58 -0800210 mDatabase.replace(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
Bo Zhu14d993d2018-02-03 21:38:48 -0800211 values))
212 .isGreaterThan(-1L);
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700213
214 // Check the table about recovery service and root of trust data introduced in V4
215 assertThat(isRootOfTrustTableAvailable()).isTrue();
Bo Zhu14d993d2018-02-03 21:38:48 -0800216 }
Bo Zhu7ebcd662019-01-04 17:00:58 -0800217
218 private void checkAllColumns_latest() throws Exception {
219 // Check all columns of the previous version first.
220 checkAllColumns_v4();
221
222 ContentValues values = new ContentValues();
223 values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
224 values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
225 values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
226 values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
227 values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
228 values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
229 values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
230 values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
231
232 // This column is added when upgrading from v4 to v5
233 values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, TEST_KEY_METADATA);
234
235 assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
236 .isGreaterThan(-1L);
237 }
Bo Zhu14d993d2018-02-03 21:38:48 -0800238}