OSDN Git Service

Write the integer given by setServerParameters() into SQLite DB
authorBo Zhu <bozhu@google.com>
Sat, 23 Dec 2017 00:05:15 +0000 (16:05 -0800)
committerBo Zhu <bozhu@google.com>
Sat, 23 Dec 2017 08:18:00 +0000 (00:18 -0800)
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

core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java

index 2f372ce..d8fb03f 100644 (file)
@@ -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
index 7fba57c..426dc8a 100644 (file)
@@ -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);
     }
 
     /**
index 8d1aed2..d213115 100644 (file)
@@ -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() {
index e575687..a232771 100644 (file)
@@ -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";
     }
 }
index 76106d9..80fa20a 100644 (file)
@@ -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);
index e20f664..6f13a98 100644 (file)
@@ -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;
index e172c49..9cde074 100644 (file)
@@ -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) {