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
17package com.android.server.locksettings.recoverablekeystore.storage;
18
19import android.content.Context;
20import android.database.sqlite.SQLiteDatabase;
21import android.database.sqlite.SQLiteOpenHelper;
22import android.util.Log;
23
24import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
25import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
26import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
27import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
28
29/**
30 * Helper for creating the recoverable key database.
31 */
32class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
33    private static final String TAG = "RecoverableKeyStoreDbHp";
34
35    static final int DATABASE_VERSION = 4;
36    private static final String DATABASE_NAME = "recoverablekeystore.db";
37
38    private static final String SQL_CREATE_KEYS_ENTRY =
39            "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
40                    + KeysEntry._ID + " INTEGER PRIMARY KEY,"
41                    + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER,"
42                    + KeysEntry.COLUMN_NAME_UID + " INTEGER,"
43                    + KeysEntry.COLUMN_NAME_ALIAS + " TEXT,"
44                    + KeysEntry.COLUMN_NAME_NONCE + " BLOB,"
45                    + KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
46                    + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
47                    + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
48                    + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
49                    + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
50                    + KeysEntry.COLUMN_NAME_ALIAS + "))";
51
52    private static final String SQL_CREATE_USER_METADATA_ENTRY =
53            "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
54                    + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
55                    + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
56                    + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
57
58    private static final String SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY =
59            "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
60                    + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
61                    + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER,"
62                    + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
63                    + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
64                    + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
65                    + RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST + " TEXT,"
66                    + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
67                    + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
68                    + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
69                    + RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
70                    + RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
71                    + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
72                    + "UNIQUE("
73                    + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID  + ","
74                    + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
75
76    private static final String SQL_CREATE_ROOT_OF_TRUST_ENTRY =
77            "CREATE TABLE " + RootOfTrustEntry.TABLE_NAME + " ("
78                    + RootOfTrustEntry._ID + " INTEGER PRIMARY KEY,"
79                    + RootOfTrustEntry.COLUMN_NAME_USER_ID + " INTEGER,"
80                    + RootOfTrustEntry.COLUMN_NAME_UID + " INTEGER,"
81                    + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " TEST,"
82                    + RootOfTrustEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
83                    + RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
84                    + "UNIQUE("
85                    + RootOfTrustEntry.COLUMN_NAME_USER_ID  + ","
86                    + RootOfTrustEntry.COLUMN_NAME_UID  + ","
87                    + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + "))";
88
89    private static final String SQL_DELETE_KEYS_ENTRY =
90            "DROP TABLE IF EXISTS " + KeysEntry.TABLE_NAME;
91
92    private static final String SQL_DELETE_USER_METADATA_ENTRY =
93            "DROP TABLE IF EXISTS " + UserMetadataEntry.TABLE_NAME;
94
95    private static final String SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY =
96            "DROP TABLE IF EXISTS " + RecoveryServiceMetadataEntry.TABLE_NAME;
97
98    private static final String SQL_DELETE_ROOT_OF_TRUST_ENTRY =
99            "DROP TABLE IF EXISTS " + RootOfTrustEntry.TABLE_NAME;
100
101    RecoverableKeyStoreDbHelper(Context context) {
102        super(context, DATABASE_NAME, null, DATABASE_VERSION);
103    }
104
105    @Override
106    public void onCreate(SQLiteDatabase db) {
107        db.execSQL(SQL_CREATE_KEYS_ENTRY);
108        db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
109        db.execSQL(SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY);
110        db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
111    }
112
113    @Override
114    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
115        Log.e(TAG, "Recreating recoverablekeystore after unexpected version downgrade.");
116        dropAllKnownTables(db); // Wipe database.
117        onCreate(db);
118    }
119
120    @Override
121    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
122        if (oldVersion < 2) {
123            dropAllKnownTables(db); // Wipe database.
124            onCreate(db);
125            return;
126        }
127
128        if (oldVersion < 3 && newVersion >= 3) {
129            upgradeDbForVersion3(db);
130            oldVersion = 3;
131        }
132
133        if (oldVersion < 4 && newVersion >= 4) {
134            upgradeDbForVersion4(db);
135            oldVersion = 4;
136        }
137
138        if (oldVersion != newVersion) {
139            Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
140        }
141    }
142
143    private void dropAllKnownTables(SQLiteDatabase db) {
144            db.execSQL(SQL_DELETE_KEYS_ENTRY);
145            db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
146            db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
147            db.execSQL(SQL_DELETE_ROOT_OF_TRUST_ENTRY);
148    }
149
150    private void upgradeDbForVersion3(SQLiteDatabase db) {
151        // Add the two columns for cert path and cert serial number
152        addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
153                RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, "BLOB", /*defaultStr=*/ null);
154        addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
155                RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, "INTEGER", /*defaultStr=*/
156                null);
157    }
158
159    private void upgradeDbForVersion4(SQLiteDatabase db) {
160        Log.d(TAG, "Updating recoverable keystore database to version 4");
161        // Add new table with two columns for cert path and cert serial number.
162        db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
163        // adds column to store root of trust currently used by the recovery agent
164        addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
165                RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, "TEXT",
166                /*defaultStr=*/ null);
167    }
168
169    private static void addColumnToTable(
170            SQLiteDatabase db, String tableName, String column, String columnType,
171            String defaultStr) {
172        Log.d(TAG, "Adding column " + column + " to " + tableName + ".");
173
174        String alterStr = "ALTER TABLE " + tableName + " ADD COLUMN " + column + " " + columnType;
175        if (defaultStr != null && !defaultStr.isEmpty()) {
176            alterStr += " DEFAULT " + defaultStr;
177        }
178
179        db.execSQL(alterStr + ";");
180    }
181}
182
183