Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +17 −3 Original line number Diff line number Diff line Loading @@ -38,10 +38,12 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessi import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.UnrecoverableKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.HashMap; import java.util.List; Loading Loading @@ -112,12 +114,24 @@ public class RecoverableKeyStoreManager { } } public int initRecoveryService( public void initRecoveryService( @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList, int userId) throws RemoteException { checkRecoverKeyStorePermission(); // TODO open /system/etc/security/... cert file throw new UnsupportedOperationException(); // TODO: open /system/etc/security/... cert file, and check the signature on the public keys PublicKey publicKey; try { KeyFactory kf = KeyFactory.getInstance("EC"); // TODO: Randomly choose a key from the list -- right now we just use the whole input X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList); publicKey = kf.generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); } catch (InvalidKeySpecException e) { throw new RemoteException("Invalid public key for the recovery service"); } mDatabase.setRecoveryServicePublicKey(userId, Binder.getCallingUid(), publicKey); } /** Loading services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +92 −0 Original line number Diff line number Diff line Loading @@ -27,9 +27,19 @@ 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.UserMetadataEntry; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; Loading Loading @@ -295,6 +305,88 @@ public class RecoverableKeyStoreDb { } } /** * Inserts or 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. * @param publicKey The public key of the recovery service. * @return The primary key of the inserted row, or -1 if failed. * * @hide */ 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); } /** * Returns the public key of the recovery service. * * @param userId The uid of the profile the application is running under. * @param uid The uid of the application who initializes the local recovery components. * * @hide */ public PublicKey getRecoveryServicePublicKey(int userId, int uid) { SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase(); String[] projection = { RecoveryServicePublicKeyEntry._ID, RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID, RecoveryServicePublicKeyEntry.COLUMN_NAME_UID, RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY}; String selection = RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + " = ? AND " + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + " = ?"; String[] selectionArguments = { Integer.toString(userId), Integer.toString(uid)}; try ( Cursor cursor = db.query( RecoveryServicePublicKeyEntry.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 PublicKey entries found for userId=%d uid=%d. " + "Should only ever be 0 or 1.", count, userId, uid)); return null; } cursor.moveToFirst(); byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow( RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY)); X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(keyBytes); try { return KeyFactory.getInstance("EC").generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); } catch (InvalidKeySpecException e) { Log.wtf(TAG, String.format(Locale.US, "Recovery service public key entry cannot be decoded for " + "userId=%d uid=%d.", userId, uid)); return null; } } } /** * Closes all open connections to the database. */ Loading services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java +22 −0 Original line number Diff line number Diff line Loading @@ -86,4 +86,26 @@ class RecoverableKeyStoreDbContract { */ static final String COLUMN_NAME_PLATFORM_KEY_GENERATION_ID = "platform_key_generation_id"; } /** * Table holding public keys of the recovery service. */ static class RecoveryServicePublicKeyEntry implements BaseColumns { static final String TABLE_NAME = "recovery_service_public_keys"; /** * The user id of the profile the application is running under. */ static final String COLUMN_NAME_USER_ID = "user_id"; /** * The uid of the application that initializes the local recovery components. */ static final String COLUMN_NAME_UID = "uid"; /** * The public key of the recovery service. */ static final String COLUMN_NAME_PUBLIC_KEY = "public_key"; } } services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java +16 −0 Original line number Diff line number Diff line Loading @@ -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.RecoveryServicePublicKeyEntry; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry; /** Loading Loading @@ -34,12 +35,25 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE," + 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," + "UNIQUE(" + RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + "," + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + "))"; 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; private static final String SQL_DELETE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY = "DROP TABLE IF EXISTS " + RecoveryServicePublicKeyEntry.TABLE_NAME; RecoverableKeyStoreDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } Loading @@ -48,12 +62,14 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_KEYS_ENTRY); db.execSQL(SQL_CREATE_USER_METADATA_ENTRY); db.execSQL(SQL_CREATE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DELETE_KEYS_ENTRY); db.execSQL(SQL_DELETE_USER_METADATA_ENTRY); db.execSQL(SQL_DELETE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY); onCreate(db); } } services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +67 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,9 @@ import com.android.server.locksettings.recoverablekeystore.WrappedKey; import java.io.File; import java.nio.charset.StandardCharsets; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.spec.ECGenParameterSpec; import java.util.Map; @SmallTest Loading Loading @@ -290,7 +293,71 @@ public class RecoverableKeyStoreDbTest { assertThat(statuses).hasSize(0); } @Test public void setRecoveryServicePublicKey_replaceOldKey() throws Exception { int userId = 12; int uid = 10009; PublicKey pubkey1 = genRandomPublicKey(); PublicKey pubkey2 = genRandomPublicKey(); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey1); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey2); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( pubkey2); } @Test public void setRecoveryServicePublicKey_allowsTwoUsersToHaveTheSameUidAndKey() throws Exception { int userId1 = 12; int userId2 = 23; 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( pubkey); } @Test public void setRecoveryServicePublicKey_allowsAUserToHaveTwoUids() throws Exception { int userId = 12; int uid1 = 10009; int uid2 = 20009; PublicKey pubkey = genRandomPublicKey(); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid1, pubkey); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid2, pubkey); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid1)).isEqualTo( pubkey); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid2)).isEqualTo( pubkey); } @Test public void getRecoveryServicePublicKey_returnsNullIfNoKey() throws Exception { int userId = 12; int uid = 10009; assertNull(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)); } @Test public void getRecoveryServicePublicKey_returnsInsertedKey() throws Exception { int userId = 12; int uid = 10009; PublicKey pubkey = genRandomPublicKey(); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( pubkey); } private static byte[] getUtf8Bytes(String s) { return s.getBytes(StandardCharsets.UTF_8); } private static PublicKey genRandomPublicKey() throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); keyPairGenerator.initialize(new ECGenParameterSpec("secp256r1")); return keyPairGenerator.generateKeyPair().getPublic(); } } Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +17 −3 Original line number Diff line number Diff line Loading @@ -38,10 +38,12 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessi import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.UnrecoverableKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.HashMap; import java.util.List; Loading Loading @@ -112,12 +114,24 @@ public class RecoverableKeyStoreManager { } } public int initRecoveryService( public void initRecoveryService( @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList, int userId) throws RemoteException { checkRecoverKeyStorePermission(); // TODO open /system/etc/security/... cert file throw new UnsupportedOperationException(); // TODO: open /system/etc/security/... cert file, and check the signature on the public keys PublicKey publicKey; try { KeyFactory kf = KeyFactory.getInstance("EC"); // TODO: Randomly choose a key from the list -- right now we just use the whole input X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList); publicKey = kf.generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); } catch (InvalidKeySpecException e) { throw new RemoteException("Invalid public key for the recovery service"); } mDatabase.setRecoveryServicePublicKey(userId, Binder.getCallingUid(), publicKey); } /** Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +92 −0 Original line number Diff line number Diff line Loading @@ -27,9 +27,19 @@ 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.UserMetadataEntry; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; Loading Loading @@ -295,6 +305,88 @@ public class RecoverableKeyStoreDb { } } /** * Inserts or 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. * @param publicKey The public key of the recovery service. * @return The primary key of the inserted row, or -1 if failed. * * @hide */ 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); } /** * Returns the public key of the recovery service. * * @param userId The uid of the profile the application is running under. * @param uid The uid of the application who initializes the local recovery components. * * @hide */ public PublicKey getRecoveryServicePublicKey(int userId, int uid) { SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase(); String[] projection = { RecoveryServicePublicKeyEntry._ID, RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID, RecoveryServicePublicKeyEntry.COLUMN_NAME_UID, RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY}; String selection = RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + " = ? AND " + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + " = ?"; String[] selectionArguments = { Integer.toString(userId), Integer.toString(uid)}; try ( Cursor cursor = db.query( RecoveryServicePublicKeyEntry.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 PublicKey entries found for userId=%d uid=%d. " + "Should only ever be 0 or 1.", count, userId, uid)); return null; } cursor.moveToFirst(); byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow( RecoveryServicePublicKeyEntry.COLUMN_NAME_PUBLIC_KEY)); X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(keyBytes); try { return KeyFactory.getInstance("EC").generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); } catch (InvalidKeySpecException e) { Log.wtf(TAG, String.format(Locale.US, "Recovery service public key entry cannot be decoded for " + "userId=%d uid=%d.", userId, uid)); return null; } } } /** * Closes all open connections to the database. */ Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java +22 −0 Original line number Diff line number Diff line Loading @@ -86,4 +86,26 @@ class RecoverableKeyStoreDbContract { */ static final String COLUMN_NAME_PLATFORM_KEY_GENERATION_ID = "platform_key_generation_id"; } /** * Table holding public keys of the recovery service. */ static class RecoveryServicePublicKeyEntry implements BaseColumns { static final String TABLE_NAME = "recovery_service_public_keys"; /** * The user id of the profile the application is running under. */ static final String COLUMN_NAME_USER_ID = "user_id"; /** * The uid of the application that initializes the local recovery components. */ static final String COLUMN_NAME_UID = "uid"; /** * The public key of the recovery service. */ static final String COLUMN_NAME_PUBLIC_KEY = "public_key"; } }
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java +16 −0 Original line number Diff line number Diff line Loading @@ -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.RecoveryServicePublicKeyEntry; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry; /** Loading Loading @@ -34,12 +35,25 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE," + 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," + "UNIQUE(" + RecoveryServicePublicKeyEntry.COLUMN_NAME_USER_ID + "," + RecoveryServicePublicKeyEntry.COLUMN_NAME_UID + "))"; 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; private static final String SQL_DELETE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY = "DROP TABLE IF EXISTS " + RecoveryServicePublicKeyEntry.TABLE_NAME; RecoverableKeyStoreDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } Loading @@ -48,12 +62,14 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_KEYS_ENTRY); db.execSQL(SQL_CREATE_USER_METADATA_ENTRY); db.execSQL(SQL_CREATE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DELETE_KEYS_ENTRY); db.execSQL(SQL_DELETE_USER_METADATA_ENTRY); db.execSQL(SQL_DELETE_RECOVERY_SERVICE_PUBLIC_KEY_ENTRY); onCreate(db); } }
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +67 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,9 @@ import com.android.server.locksettings.recoverablekeystore.WrappedKey; import java.io.File; import java.nio.charset.StandardCharsets; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.spec.ECGenParameterSpec; import java.util.Map; @SmallTest Loading Loading @@ -290,7 +293,71 @@ public class RecoverableKeyStoreDbTest { assertThat(statuses).hasSize(0); } @Test public void setRecoveryServicePublicKey_replaceOldKey() throws Exception { int userId = 12; int uid = 10009; PublicKey pubkey1 = genRandomPublicKey(); PublicKey pubkey2 = genRandomPublicKey(); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey1); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey2); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( pubkey2); } @Test public void setRecoveryServicePublicKey_allowsTwoUsersToHaveTheSameUidAndKey() throws Exception { int userId1 = 12; int userId2 = 23; 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( pubkey); } @Test public void setRecoveryServicePublicKey_allowsAUserToHaveTwoUids() throws Exception { int userId = 12; int uid1 = 10009; int uid2 = 20009; PublicKey pubkey = genRandomPublicKey(); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid1, pubkey); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid2, pubkey); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid1)).isEqualTo( pubkey); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid2)).isEqualTo( pubkey); } @Test public void getRecoveryServicePublicKey_returnsNullIfNoKey() throws Exception { int userId = 12; int uid = 10009; assertNull(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)); } @Test public void getRecoveryServicePublicKey_returnsInsertedKey() throws Exception { int userId = 12; int uid = 10009; PublicKey pubkey = genRandomPublicKey(); mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey); assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo( pubkey); } private static byte[] getUtf8Bytes(String s) { return s.getBytes(StandardCharsets.UTF_8); } private static PublicKey genRandomPublicKey() throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); keyPairGenerator.initialize(new ECGenParameterSpec("secp256r1")); return keyPairGenerator.generateKeyPair().getPublic(); } }