blob: 2658af68f78b4e05ff16d44d764cc349fd16f91e [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;
Dmitry Dementyev89f12d52019-02-28 12:26:01 -080054 private static final int TEST_USER_SERIAL_NUMBER = 15;
Bo Zhu14d993d2018-02-03 21:38:48 -080055 private static final int TEST_SNAPSHOT_VERSION = 31;
56 private static final int TEST_SHOULD_CREATE_SNAPSHOT = 1;
57 private static final byte[] TEST_PUBLIC_KEY = "test-public-key".getBytes(UTF_8);
58 private static final String TEST_SECRET_TYPES = "test-secret-types";
59 private static final long TEST_COUNTER_ID = -3981205205038476415L;
60 private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8);
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -070061 private static final String TEST_ROOT_ALIAS = "root_cert_alias";
Bo Zhu14d993d2018-02-03 21:38:48 -080062 private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
63 private static final long TEST_CERT_SERIAL = 1000L;
Bo Zhu7ebcd662019-01-04 17:00:58 -080064 private static final byte[] TEST_KEY_METADATA = "test-key-metadata".getBytes(UTF_8);
Bo Zhu14d993d2018-02-03 21:38:48 -080065
66 private static final String SQL_CREATE_V2_TABLE_KEYS =
67 "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
68 + KeysEntry._ID + " INTEGER PRIMARY KEY,"
69 + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER,"
70 + KeysEntry.COLUMN_NAME_UID + " INTEGER,"
71 + KeysEntry.COLUMN_NAME_ALIAS + " TEXT,"
72 + KeysEntry.COLUMN_NAME_NONCE + " BLOB,"
73 + KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
74 + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
75 + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
76 + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
77 + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
78 + KeysEntry.COLUMN_NAME_ALIAS + "))";
79
80 private static final String SQL_CREATE_V2_TABLE_USER_METADATA =
81 "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
82 + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
83 + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
84 + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
85
86 private static final String SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA =
87 "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
88 + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
89 + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER,"
90 + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
91 + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
92 + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
93 + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
94 + RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
95 + RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
96 + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
97 + "UNIQUE("
98 + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + ","
99 + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
100
101 private SQLiteDatabase mDatabase;
102 private RecoverableKeyStoreDbHelper mDatabaseHelper;
103
104 @Before
105 public void setUp() throws Exception {
106 Context context = InstrumentationRegistry.getTargetContext();
107 mDatabaseHelper = new RecoverableKeyStoreDbHelper(context);
108 mDatabase = SQLiteDatabase.create(null);
109 }
110
111 @After
112 public void tearDown() throws Exception {
113 mDatabase.close();
114 }
115
116 private void createV2Tables() throws Exception {
117 mDatabase.execSQL(SQL_CREATE_V2_TABLE_KEYS);
118 mDatabase.execSQL(SQL_CREATE_V2_TABLE_USER_METADATA);
119 mDatabase.execSQL(SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA);
120 }
121
122 @Test
123 public void onCreate() throws Exception {
124 mDatabaseHelper.onCreate(mDatabase);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800125 checkAllColumns_latest();
Bo Zhu14d993d2018-02-03 21:38:48 -0800126 }
127
128 @Test
129 public void onUpgrade_beforeV2() throws Exception {
130 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
131 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800132 checkAllColumns_latest();
Bo Zhu14d993d2018-02-03 21:38:48 -0800133 }
134
135 @Test
136 public void onUpgrade_fromV2() throws Exception {
137 createV2Tables();
138 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
139 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800140 checkAllColumns_latest();
Bo Zhu14d993d2018-02-03 21:38:48 -0800141 }
142
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700143 @Test
Bo Zhu7ebcd662019-01-04 17:00:58 -0800144 public void onUpgrade_v2_to_v3_to_v4_to_latest() throws Exception {
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700145 createV2Tables();
146
147 assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table;
148
149 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, /*newVersion=*/ 3);
150
151 assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table;
152
Bo Zhu7ebcd662019-01-04 17:00:58 -0800153 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, /*newVersion=*/ 4);
154 checkAllColumns_v4();
155
156 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 4,
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700157 RecoverableKeyStoreDbHelper.DATABASE_VERSION);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800158 checkAllColumns_latest();
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700159 }
160
161 private boolean isRootOfTrustTableAvailable() {
162 ContentValues values = new ContentValues();
163 values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
164 values.put(RootOfTrustEntry.COLUMN_NAME_UID, TEST_UID);
165 values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS);
166 values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
167 values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800168 return mDatabase.replace(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700169 > -1;
170 }
171
Bo Zhu7ebcd662019-01-04 17:00:58 -0800172 private void checkAllColumns_v4() throws Exception {
Bo Zhu14d993d2018-02-03 21:38:48 -0800173 // Check the table containing encrypted application keys
174 ContentValues values = new ContentValues();
175 values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
176 values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
177 values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
178 values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
179 values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
180 values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
181 values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
182 values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800183 assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
Bo Zhu14d993d2018-02-03 21:38:48 -0800184 .isGreaterThan(-1L);
185
186 // Check the table about user metadata
187 values = new ContentValues();
188 values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
189 values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID,
190 TEST_PLATFORM_KEY_GENERATION_ID);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800191 assertThat(
192 mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
Bo Zhu14d993d2018-02-03 21:38:48 -0800193 .isGreaterThan(-1L);
194
195 // Check the table about recovery service metadata
196 values = new ContentValues();
197 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
198 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_UID, TEST_UID);
199 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION,
200 TEST_SNAPSHOT_VERSION);
201 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT,
202 TEST_SHOULD_CREATE_SNAPSHOT);
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700203 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, TEST_ROOT_ALIAS);
Bo Zhu14d993d2018-02-03 21:38:48 -0800204 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY);
205 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES);
206 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID);
207 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS, TEST_SERVER_PARAMS);
208 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
209 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
210 assertThat(
Bo Zhu7ebcd662019-01-04 17:00:58 -0800211 mDatabase.replace(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
Bo Zhu14d993d2018-02-03 21:38:48 -0800212 values))
213 .isGreaterThan(-1L);
Dmitry Dementyevf34fc7e2018-03-26 17:31:29 -0700214
215 // Check the table about recovery service and root of trust data introduced in V4
216 assertThat(isRootOfTrustTableAvailable()).isTrue();
Bo Zhu14d993d2018-02-03 21:38:48 -0800217 }
Bo Zhu7ebcd662019-01-04 17:00:58 -0800218
219 private void checkAllColumns_latest() throws Exception {
220 // Check all columns of the previous version first.
221 checkAllColumns_v4();
222
223 ContentValues values = new ContentValues();
224 values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
225 values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
226 values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
227 values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
228 values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
229 values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
230 values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
231 values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
232
233 // This column is added when upgrading from v4 to v5
234 values.put(KeysEntry.COLUMN_NAME_KEY_METADATA, TEST_KEY_METADATA);
235
236 assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
237 .isGreaterThan(-1L);
Dmitry Dementyev89f12d52019-02-28 12:26:01 -0800238
239 // User serial number column was added when upgrading from v5 to v6
240 values = new ContentValues();
241 values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
242 values.put(UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER, TEST_USER_SERIAL_NUMBER);
243 assertThat(
244 mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
245 .isGreaterThan(-1L);
Bo Zhu7ebcd662019-01-04 17:00:58 -0800246 }
Dmitry Dementyev89f12d52019-02-28 12:26:01 -0800247
Bo Zhu14d993d2018-02-03 21:38:48 -0800248}