diff options
| author | 2017-12-22 16:05:15 -0800 | |
|---|---|---|
| committer | 2017-12-23 00:18:00 -0800 | |
| commit | 584b923fb7d92a735209ec30b2c5e7d4b8e673eb (patch) | |
| tree | 34b6cfb64e11bb83a9cd7495bee25eb7ab6a75d0 | |
| parent | 5a4d8a1d3cad6d5cdf56e8cae11603dfa54b6966 (diff) | |
Write the integer given by setServerParameters() into SQLite DB
Change-Id: Icd8b40154560c600757d51ed4620d39fc07e494c
Test: adb shell am instrument -w -e package com.android.server.locksettings.recoverablekeystore com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
7 files changed, 216 insertions, 50 deletions
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java index 2f372ceba408..d8fb03fb73fb 100644 --- a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java @@ -209,7 +209,7 @@ public class RecoverableKeyStoreLoader { * version. Version zero is used, if no snapshots were created for the account. * * @return Map from recovery agent accounts to snapshot versions. - * @see KeyStoreRecoveryData.getSnapshotVersion + * @see KeyStoreRecoveryData#getSnapshotVersion * @hide */ public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions() @@ -231,7 +231,7 @@ public class RecoverableKeyStoreLoader { /** * Server parameters used to generate new recovery key blobs. This value will be included in * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included - * in vaultParams {@link startRecoverySession} + * in vaultParams {@link #startRecoverySession} * * @param serverParameters included in recovery key blob. * @see #getRecoveryData diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 7fba57ccebc2..426dc8adb29c 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -184,7 +184,7 @@ public class RecoverableKeyStoreManager { public void setServerParameters(long serverParameters, int userId) throws RemoteException { checkRecoverKeyStorePermission(); - throw new UnsupportedOperationException(); + mDatabase.setServerParameters(userId, Binder.getCallingUid(), serverParameters); } /** diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java index 8d1aed221528..d213115c8ee4 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java @@ -27,7 +27,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.RecoveryServicePublicKeyEntry; +import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry; @@ -306,7 +306,7 @@ public class RecoverableKeyStoreDb { } /** - * Inserts or updates the public key of the recovery service into the database. + * Updates the public key of the recovery service into the database. * * @param userId The uid of the profile the application is running under. * @param uid The uid of the application to whom the key belongs. @@ -318,11 +318,15 @@ public class RecoverableKeyStoreDb { public long setRecoveryServicePublicKey(int userId, int uid, PublicKey publicKey) { SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); - values.put(RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID, userId); - values.put(RecoveryServicePublicKeyEntry.COLUMN_NAME_UID, uid); - values.put(RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY, publicKey.getEncoded()); - return db.replace(RecoveryServicePublicKeyEntry.TABLE_NAME, /*nullColumnHack=*/ null, - values); + values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, publicKey.getEncoded()); + String selection = + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND " + + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?"; + String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)}; + + ensureRecoveryServiceMetadataEntryExists(userId, uid); + return db.update( + RecoveryServiceMetadataEntry.TABLE_NAME, values, selection, selectionArguments); } /** @@ -337,18 +341,18 @@ public class RecoverableKeyStoreDb { SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase(); String[] projection = { - RecoveryServicePublicKeyEntry._ID, - RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID, - RecoveryServicePublicKeyEntry.COLUMN_NAME_UID, - RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY}; + RecoveryServiceMetadataEntry._ID, + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, + RecoveryServiceMetadataEntry.COLUMN_NAME_UID, + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY}; String selection = - RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + " = ? AND " - + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + " = ?"; - String[] selectionArguments = { Integer.toString(userId), Integer.toString(uid)}; + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND " + + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?"; + String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)}; try ( Cursor cursor = db.query( - RecoveryServicePublicKeyEntry.TABLE_NAME, + RecoveryServiceMetadataEntry.TABLE_NAME, projection, selection, selectionArguments, @@ -368,8 +372,12 @@ public class RecoverableKeyStoreDb { return null; } cursor.moveToFirst(); - byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow( - RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY)); + int idx = cursor.getColumnIndexOrThrow( + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY); + if (cursor.isNull(idx)) { + return null; + } + byte[] keyBytes = cursor.getBlob(idx); X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(keyBytes); try { return KeyFactory.getInstance("EC").generatePublic(pkSpec); @@ -388,6 +396,99 @@ public class RecoverableKeyStoreDb { } /** + * Updates the server parameters given by the application initializing the local recovery + * components. + * + * @param userId The uid of the profile the application is running under. + * @param uid The uid of the application. + * @param serverParameters The server parameters. + * @return The primary key of the inserted row, or -1 if failed. + * + * @hide + */ + public long setServerParameters(int userId, int uid, long serverParameters) { + SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS, serverParameters); + String selection = + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND " + + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?"; + String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)}; + + ensureRecoveryServiceMetadataEntryExists(userId, uid); + return db.update( + RecoveryServiceMetadataEntry.TABLE_NAME, values, selection, selectionArguments); + } + + /** + * Returns the server paramters that was previously set by the application who initialized the + * local recovery service components. + * + * @param userId The uid of the profile the application is running under. + * @param uid The uid of the application who initialized the local recovery components. + * @return The server parameters that were previously set, or null if there's none. + * + * @hide + */ + public Long getServerParameters(int userId, int uid) { + SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase(); + + String[] projection = { + RecoveryServiceMetadataEntry._ID, + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, + RecoveryServiceMetadataEntry.COLUMN_NAME_UID, + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS}; + String selection = + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND " + + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?"; + String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)}; + + try ( + Cursor cursor = db.query( + RecoveryServiceMetadataEntry.TABLE_NAME, + projection, + selection, + selectionArguments, + /*groupBy=*/ null, + /*having=*/ null, + /*orderBy=*/ null) + ) { + int count = cursor.getCount(); + if (count == 0) { + return null; + } + if (count > 1) { + Log.wtf(TAG, + String.format(Locale.US, + "%d deviceId entries found for userId=%d uid=%d. " + + "Should only ever be 0 or 1.", count, userId, uid)); + return null; + } + cursor.moveToFirst(); + int idx = cursor.getColumnIndexOrThrow( + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS); + if (cursor.isNull(idx)) { + return null; + } else { + return cursor.getLong(idx); + } + } + } + + /** + * Creates an empty row in the recovery service metadata table if such a row doesn't exist for + * the given userId and uid, so db.update will succeed. + */ + private void ensureRecoveryServiceMetadataEntryExists(int userId, int uid) { + SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, userId); + values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_UID, uid); + db.insertWithOnConflict(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, + values, SQLiteDatabase.CONFLICT_IGNORE); + } + + /** * Closes all open connections to the database. */ public void close() { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java index e575687edd71..a232771d241a 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java @@ -88,10 +88,10 @@ class RecoverableKeyStoreDbContract { } /** - * Table holding public keys of the recovery service. + * Table holding metadata of the recovery service. */ - static class RecoveryServicePublicKeyEntry implements BaseColumns { - static final String TABLE_NAME = "recovery_service_public_keys"; + static class RecoveryServiceMetadataEntry implements BaseColumns { + static final String TABLE_NAME = "recovery_service_metadata"; /** * The user id of the profile the application is running under. @@ -107,5 +107,10 @@ class RecoverableKeyStoreDbContract { * The public key of the recovery service. */ static final String COLUMN_NAME_PUBLIC_KEY = "public_key"; + + /** + * The server parameters of the recovery service. + */ + static final String COLUMN_NAME_SERVER_PARAMETERS = "server_parameters"; } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java index 76106d9b5b9f..80fa20ac88af 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java @@ -5,7 +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.RecoveryServicePublicKeyEntry; +import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry; /** @@ -36,14 +36,15 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)"; private static final String SQL_CREATE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY = - "CREATE TABLE " + RecoveryServicePublicKeyEntry.TABLE_NAME + " (" - + RecoveryServicePublicKeyEntry._ID + " INTEGER PRIMARY KEY," - + RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + " INTEGER," - + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + " INTEGER," - + RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB," + "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " (" + + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY," + + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER," + + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER," + + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB," + + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS + " INTEGER," + "UNIQUE(" - + RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + "," - + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + "))"; + + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + "," + + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))"; private static final String SQL_DELETE_KEYS_ENTRY = "DROP TABLE IF EXISTS " + KeysEntry.TABLE_NAME; @@ -52,7 +53,7 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { "DROP TABLE IF EXISTS " + UserMetadataEntry.TABLE_NAME; private static final String SQL_DELETE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY = - "DROP TABLE IF EXISTS " + RecoveryServicePublicKeyEntry.TABLE_NAME; + "DROP TABLE IF EXISTS " + RecoveryServiceMetadataEntry.TABLE_NAME; RecoverableKeyStoreDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java index e20f664d14cf..6f13a9873909 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.when; import android.app.KeyguardManager; import android.content.Context; -import android.content.SharedPreferences; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.support.test.InstrumentationRegistry; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java index e172c49078c3..9cde074015be 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java @@ -28,6 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import android.content.Context; +import android.content.SharedPreferences; import android.security.recoverablekeystore.RecoverableKeyStoreLoader; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -306,22 +307,60 @@ public class RecoverableKeyStoreDbTest { } @Test - public void setRecoveryServicePublicKey_allowsTwoUsersToHaveTheSameUidAndKey() - throws Exception { - int userId1 = 12; - int userId2 = 23; + public void getRecoveryServicePublicKey_returnsNullIfNoKey() throws Exception { + int userId = 12; + int uid = 10009; + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); + + long serverParams = 123456L; + mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams); + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull(); + } + + @Test + public void getRecoveryServicePublicKey_returnsInsertedKey() throws Exception { + int userId = 12; int uid = 10009; PublicKey pubkey = genRandomPublicKey(); - mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId1, uid, pubkey); - mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId2, uid, pubkey); - assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId1, uid)).isEqualTo( - pubkey); - assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId2, uid)).isEqualTo( + mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey); + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( pubkey); } @Test - public void setRecoveryServicePublicKey_allowsAUserToHaveTwoUids() throws Exception { + public void setServerParameters_replaceOldValue() throws Exception { + int userId = 12; + int uid = 10009; + long serverParams1 = 111L; + long serverParams2 = 222L; + mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams1); + mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams2); + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo( + serverParams2); + } + + @Test + public void getServerParameters_returnsNullIfNoValue() throws Exception { + int userId = 12; + int uid = 10009; + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isNull(); + + PublicKey pubkey = genRandomPublicKey(); + mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey); + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isNull(); + } + + @Test + public void getServerParameters_returnsInsertedValue() throws Exception { + int userId = 12; + int uid = 10009; + long serverParams = 123456L; + mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams); + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(serverParams); + } + + @Test + public void setRecoveryServiceMetadataEntry_allowsAUserToHaveTwoUids() throws Exception { int userId = 12; int uid1 = 10009; int uid2 = 20009; @@ -335,20 +374,41 @@ public class RecoverableKeyStoreDbTest { } @Test - public void getRecoveryServicePublicKey_returnsNullIfNoKey() throws Exception { - int userId = 12; + public void setRecoveryServiceMetadataEntry_allowsTwoUsersToHaveTheSameUid() throws Exception { + int userId1 = 12; + int userId2 = 23; int uid = 10009; - assertNull(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)); + PublicKey pubkey = genRandomPublicKey(); + mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId1, uid, pubkey); + mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId2, uid, pubkey); + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId1, uid)).isEqualTo( + pubkey); + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId2, uid)).isEqualTo( + pubkey); } @Test - public void getRecoveryServicePublicKey_returnsInsertedKey() throws Exception { + public void setRecoveryServiceMetadataEntry_updatesColumnsSeparately() throws Exception { int userId = 12; int uid = 10009; - PublicKey pubkey = genRandomPublicKey(); - mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey); + PublicKey pubkey1 = genRandomPublicKey(); + PublicKey pubkey2 = genRandomPublicKey(); + long serverParams = 123456L; + + mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey1); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( - pubkey); + pubkey1); + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isNull(); + + mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams); + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( + pubkey1); + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(serverParams); + + mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey2); + assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( + pubkey2); + assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(serverParams); } private static byte[] getUtf8Bytes(String s) { |