Add storage for platform key IDs to SQLite db
Also fix UNIQUE constraint for keys table and add test. Test: adb shell am instrument -w -e package com.android.server.locksettings.recoverablekeystore com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner Change-Id: I868cc4385b6557135ef1d40b39f23c0383453ca3
This commit is contained in:
@@ -25,6 +25,7 @@ import android.util.Log;
|
||||
|
||||
import com.android.server.locksettings.recoverablekeystore.WrappedKey;
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
@@ -175,6 +176,52 @@ public class RecoverableKeyStoreDb {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code generationId} of the platform key for the account owned by {@code userId}.
|
||||
*
|
||||
* @return The primary key ID of the relation.
|
||||
*/
|
||||
public long setPlatformKeyGenerationId(int userId, int generationId) {
|
||||
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
|
||||
values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, generationId);
|
||||
return db.replace(
|
||||
UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generation ID associated with the platform key of the user with {@code userId}.
|
||||
*/
|
||||
public int getPlatformKeyGenerationId(int userId) {
|
||||
SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
|
||||
String[] projection = {
|
||||
UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID};
|
||||
String selection =
|
||||
UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
|
||||
String[] selectionArguments = {
|
||||
Integer.toString(userId)};
|
||||
|
||||
try (
|
||||
Cursor cursor = db.query(
|
||||
UserMetadataEntry.TABLE_NAME,
|
||||
projection,
|
||||
selection,
|
||||
selectionArguments,
|
||||
/*groupBy=*/ null,
|
||||
/*having=*/ null,
|
||||
/*orderBy=*/ null)
|
||||
) {
|
||||
if (cursor.getCount() == 0) {
|
||||
return -1;
|
||||
}
|
||||
cursor.moveToFirst();
|
||||
return cursor.getInt(
|
||||
cursor.getColumnIndexOrThrow(
|
||||
UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all open connections to the database.
|
||||
*/
|
||||
|
||||
@@ -58,4 +58,22 @@ class RecoverableKeyStoreDbContract {
|
||||
*/
|
||||
static final String COLUMN_NAME_LAST_SYNCED_AT = "last_synced_at";
|
||||
}
|
||||
|
||||
/**
|
||||
* Recoverable KeyStore metadata for a specific user profile.
|
||||
*/
|
||||
static class UserMetadataEntry implements BaseColumns {
|
||||
static final String TABLE_NAME = "user_metadata";
|
||||
|
||||
/**
|
||||
* User ID of the profile.
|
||||
*/
|
||||
static final String COLUMN_NAME_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* Every time a new platform key is generated for a user, this increments. The platform key
|
||||
* is used to wrap recoverable keys on disk.
|
||||
*/
|
||||
static final String COLUMN_NAME_PLATFORM_KEY_GENERATION_ID = "platform_key_generation_id";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
|
||||
|
||||
/**
|
||||
* Helper for creating the recoverable key database.
|
||||
@@ -13,31 +14,44 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final String DATABASE_NAME = "recoverablekeystore.db";
|
||||
|
||||
private static final String SQL_CREATE_ENTRIES =
|
||||
private static final String SQL_CREATE_KEYS_ENTRY =
|
||||
"CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
|
||||
+ KeysEntry._ID + " INTEGER PRIMARY KEY,"
|
||||
+ KeysEntry.COLUMN_NAME_UID + " INTEGER UNIQUE,"
|
||||
+ KeysEntry.COLUMN_NAME_ALIAS + " TEXT UNIQUE,"
|
||||
+ KeysEntry.COLUMN_NAME_UID + " INTEGER,"
|
||||
+ KeysEntry.COLUMN_NAME_ALIAS + " TEXT,"
|
||||
+ KeysEntry.COLUMN_NAME_NONCE + " BLOB,"
|
||||
+ KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
|
||||
+ KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
|
||||
+ KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER)";
|
||||
+ KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
|
||||
+ "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
|
||||
+ KeysEntry.COLUMN_NAME_ALIAS + "))";
|
||||
|
||||
private static final String SQL_DELETE_ENTRIES =
|
||||
private static final String SQL_CREATE_USER_METADATA_ENTRY =
|
||||
"CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
|
||||
+ UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
|
||||
+ UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
|
||||
+ UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
|
||||
|
||||
private static final String SQL_DELETE_KEYS_ENTRY =
|
||||
"DROP TABLE IF EXISTS " + KeysEntry.TABLE_NAME;
|
||||
|
||||
private static final String SQL_DELETE_USER_METADATA_ENTRY =
|
||||
"DROP TABLE IF EXISTS " + UserMetadataEntry.TABLE_NAME;
|
||||
|
||||
RecoverableKeyStoreDbHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(SQL_CREATE_ENTRIES);
|
||||
db.execSQL(SQL_CREATE_KEYS_ENTRY);
|
||||
db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
db.execSQL(SQL_DELETE_ENTRIES);
|
||||
db.execSQL(SQL_DELETE_KEYS_ENTRY);
|
||||
db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.locksettings.recoverablekeystore.WrappedKey;
|
||||
|
||||
import java.io.File;
|
||||
@@ -65,7 +64,7 @@ public class RecoverableKeyStoreDbTest {
|
||||
WrappedKey oldWrappedKey = new WrappedKey(
|
||||
getUtf8Bytes("nonce1"),
|
||||
getUtf8Bytes("keymaterial1"),
|
||||
/*platformKeyGenerationId=*/1);
|
||||
/*platformKeyGenerationId=*/ 1);
|
||||
mRecoverableKeyStoreDb.insertKey(
|
||||
userId, alias, oldWrappedKey);
|
||||
byte[] nonce = getUtf8Bytes("nonce2");
|
||||
@@ -82,6 +81,29 @@ public class RecoverableKeyStoreDbTest {
|
||||
assertEquals(2, retrievedKey.getPlatformKeyGenerationId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertKey_allowsTwoUidsToHaveSameAlias() {
|
||||
String alias = "pcoulton";
|
||||
WrappedKey key1 = new WrappedKey(
|
||||
getUtf8Bytes("nonce1"),
|
||||
getUtf8Bytes("key1"),
|
||||
/*platformKeyGenerationId=*/ 1);
|
||||
WrappedKey key2 = new WrappedKey(
|
||||
getUtf8Bytes("nonce2"),
|
||||
getUtf8Bytes("key2"),
|
||||
/*platformKeyGenerationId=*/ 1);
|
||||
|
||||
mRecoverableKeyStoreDb.insertKey(/*uid=*/ 1, alias, key1);
|
||||
mRecoverableKeyStoreDb.insertKey(/*uid=*/ 2, alias, key2);
|
||||
|
||||
assertArrayEquals(
|
||||
getUtf8Bytes("nonce1"),
|
||||
mRecoverableKeyStoreDb.getKey(1, alias).getNonce());
|
||||
assertArrayEquals(
|
||||
getUtf8Bytes("nonce2"),
|
||||
mRecoverableKeyStoreDb.getKey(2, alias).getNonce());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getKey_returnsNullIfNoKey() {
|
||||
WrappedKey key = mRecoverableKeyStoreDb.getKey(
|
||||
@@ -157,6 +179,29 @@ public class RecoverableKeyStoreDbTest {
|
||||
assertTrue(keys.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPlatformKeyGenerationId_returnsGenerationId() {
|
||||
int userId = 42;
|
||||
int generationId = 24;
|
||||
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
|
||||
|
||||
assertEquals(generationId, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPlatformKeyGenerationId_returnsMinusOneIfNoEntry() {
|
||||
assertEquals(-1, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(42));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPlatformKeyGenerationId_replacesOldEntry() {
|
||||
int userId = 42;
|
||||
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, 1);
|
||||
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, 2);
|
||||
|
||||
assertEquals(2, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
|
||||
}
|
||||
|
||||
private static byte[] getUtf8Bytes(String s) {
|
||||
return s.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user