Merge "Add storage for platform key IDs to SQLite db"

This commit is contained in:
Robert Berry
2017-12-20 10:49:09 +00:00
committed by Android (Google) Code Review
4 changed files with 133 additions and 9 deletions

View File

@@ -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.
*/

View File

@@ -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";
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}