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.storage; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static java.nio.charset.StandardCharsets.UTF_8; 22 23 import org.junit.After; 24 import org.junit.Before; 25 import org.junit.Test; 26 import org.junit.runner.RunWith; 27 28 import android.content.ContentValues; 29 import android.content.Context; 30 import android.database.sqlite.SQLiteDatabase; 31 import android.support.test.InstrumentationRegistry; 32 import android.support.test.filters.SmallTest; 33 import android.support.test.runner.AndroidJUnit4; 34 35 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry; 36 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry; 37 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry; 38 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry; 39 40 @SmallTest 41 @RunWith(AndroidJUnit4.class) 42 public class RecoverableKeyStoreDbHelperTest { 43 44 private static final long TEST_USER_ID = 10L; 45 private static final long TEST_UID = 60001L; 46 private static final String TEST_ALIAS = "test-alias"; 47 private static final byte[] TEST_NONCE = "test-nonce".getBytes(UTF_8); 48 private static final byte[] TEST_WRAPPED_KEY = "test-wrapped-key".getBytes(UTF_8); 49 private static final long TEST_GENERATION_ID = 13L; 50 private static final long TEST_LAST_SYNCED_AT = 1517990732000L; 51 private static final int TEST_RECOVERY_STATUS = 3; 52 private static final int TEST_PLATFORM_KEY_GENERATION_ID = 11; 53 private static final int TEST_SNAPSHOT_VERSION = 31; 54 private static final int TEST_SHOULD_CREATE_SNAPSHOT = 1; 55 private static final byte[] TEST_PUBLIC_KEY = "test-public-key".getBytes(UTF_8); 56 private static final String TEST_SECRET_TYPES = "test-secret-types"; 57 private static final long TEST_COUNTER_ID = -3981205205038476415L; 58 private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8); 59 private static final String TEST_ROOT_ALIAS = "root_cert_alias"; 60 private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8); 61 private static final long TEST_CERT_SERIAL = 1000L; 62 63 private static final String SQL_CREATE_V2_TABLE_KEYS = 64 "CREATE TABLE " + KeysEntry.TABLE_NAME + "( " 65 + KeysEntry._ID + " INTEGER PRIMARY KEY," 66 + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER," 67 + KeysEntry.COLUMN_NAME_UID + " INTEGER," 68 + KeysEntry.COLUMN_NAME_ALIAS + " TEXT," 69 + KeysEntry.COLUMN_NAME_NONCE + " BLOB," 70 + KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB," 71 + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER," 72 + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER," 73 + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER," 74 + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + "," 75 + KeysEntry.COLUMN_NAME_ALIAS + "))"; 76 77 private static final String SQL_CREATE_V2_TABLE_USER_METADATA = 78 "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( " 79 + UserMetadataEntry._ID + " INTEGER PRIMARY KEY," 80 + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE," 81 + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)"; 82 83 private static final String SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA = 84 "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " (" 85 + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY," 86 + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER," 87 + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER," 88 + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER," 89 + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER," 90 + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB," 91 + RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT," 92 + RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER," 93 + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB," 94 + "UNIQUE(" 95 + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + "," 96 + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))"; 97 98 private SQLiteDatabase mDatabase; 99 private RecoverableKeyStoreDbHelper mDatabaseHelper; 100 101 @Before 102 public void setUp() throws Exception { 103 Context context = InstrumentationRegistry.getTargetContext(); 104 mDatabaseHelper = new RecoverableKeyStoreDbHelper(context); 105 mDatabase = SQLiteDatabase.create(null); 106 } 107 108 @After 109 public void tearDown() throws Exception { 110 mDatabase.close(); 111 } 112 113 private void createV2Tables() throws Exception { 114 mDatabase.execSQL(SQL_CREATE_V2_TABLE_KEYS); 115 mDatabase.execSQL(SQL_CREATE_V2_TABLE_USER_METADATA); 116 mDatabase.execSQL(SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA); 117 } 118 119 @Test 120 public void onCreate() throws Exception { 121 mDatabaseHelper.onCreate(mDatabase); 122 checkAllColumns(); 123 } 124 125 @Test 126 public void onUpgrade_beforeV2() throws Exception { 127 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1, 128 RecoverableKeyStoreDbHelper.DATABASE_VERSION); 129 checkAllColumns(); 130 } 131 132 @Test 133 public void onUpgrade_fromV2() throws Exception { 134 createV2Tables(); 135 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, 136 RecoverableKeyStoreDbHelper.DATABASE_VERSION); 137 checkAllColumns(); 138 } 139 140 @Test 141 public void onUpgrade_v2_to_v3_to_v4() throws Exception { 142 createV2Tables(); 143 144 assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table; 145 146 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, /*newVersion=*/ 3); 147 148 assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table; 149 150 mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3, 151 RecoverableKeyStoreDbHelper.DATABASE_VERSION); 152 checkAllColumns(); 153 } 154 155 private boolean isRootOfTrustTableAvailable() { 156 ContentValues values = new ContentValues(); 157 values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); 158 values.put(RootOfTrustEntry.COLUMN_NAME_UID, TEST_UID); 159 values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS); 160 values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH); 161 values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL); 162 return mDatabase.insert(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values) 163 > -1; 164 } 165 166 private void checkAllColumns() throws Exception { 167 // Check the table containing encrypted application keys 168 ContentValues values = new ContentValues(); 169 values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); 170 values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID); 171 values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS); 172 values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE); 173 values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY); 174 values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID); 175 values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT); 176 values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS); 177 assertThat(mDatabase.insert(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) 178 .isGreaterThan(-1L); 179 180 // Check the table about user metadata 181 values = new ContentValues(); 182 values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); 183 values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, 184 TEST_PLATFORM_KEY_GENERATION_ID); 185 assertThat(mDatabase.insert(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)) 186 .isGreaterThan(-1L); 187 188 // Check the table about recovery service metadata 189 values = new ContentValues(); 190 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID); 191 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_UID, TEST_UID); 192 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION, 193 TEST_SNAPSHOT_VERSION); 194 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT, 195 TEST_SHOULD_CREATE_SNAPSHOT); 196 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, TEST_ROOT_ALIAS); 197 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY); 198 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES); 199 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID); 200 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS, TEST_SERVER_PARAMS); 201 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH); 202 values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL); 203 assertThat( 204 mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, 205 values)) 206 .isGreaterThan(-1L); 207 208 // Check the table about recovery service and root of trust data introduced in V4 209 assertThat(isRootOfTrustTableAvailable()).isTrue(); 210 } 211 } 212