From b7c06ea06a7d18d02becb100958d47c9d96369b5 Mon Sep 17 00:00:00 2001 From: Robert Berry Date: Thu, 21 Dec 2017 13:37:23 +0000 Subject: [PATCH] Add userId to RecoverableKeyStoreDb We need to store the userId (i.e., the uid of the *profile*, not the app), as well as the app uid. This is because when the screen is unlocked, the unlock is associated with a specific user profile, not a specific app. So at that point we need to look up all keys that are pending sync for that *user*, and upload them to the remote storage. Test: adb shell am instrument -w -e package com.android.server.locksettings.recoverablekeystore com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner Change-Id: I6c456cca8974f5e1a15dfde6e9dd4e6bf4c16065 --- .../RecoverableKeyGenerator.java | 6 +++-- .../storage/RecoverableKeyStoreDb.java | 17 ++++++++------ .../storage/RecoverableKeyStoreDbContract.java | 5 +++++ .../storage/RecoverableKeyStoreDbHelper.java | 1 + .../RecoverableKeyGeneratorTest.java | 13 +++++++---- .../storage/RecoverableKeyStoreDbTest.java | 26 +++++++++++++--------- 6 files changed, 45 insertions(+), 23 deletions(-) diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java index d50a736501ec..bb40fd8c2134 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java @@ -87,6 +87,7 @@ public class RecoverableKeyGenerator { * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding. * * @param platformKey The user's platform key, with which to wrap the generated key. + * @param userId The user ID of the profile to which the calling app belongs. * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in AndroidKeyStore. * @throws RecoverableKeyStorageException if there is some error persisting the key either to @@ -96,7 +97,8 @@ public class RecoverableKeyGenerator { * * @hide */ - public void generateAndStoreKey(PlatformEncryptionKey platformKey, int uid, String alias) + public void generateAndStoreKey( + PlatformEncryptionKey platformKey, int userId, int uid, String alias) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { mKeyGenerator.init(KEY_SIZE_BITS); SecretKey key = mKeyGenerator.generateKey(); @@ -134,7 +136,7 @@ public class RecoverableKeyGenerator { } catch (DestroyFailedException e) { Log.w(TAG, "Could not destroy SecretKey."); } - long result = mDatabase.insertKey(uid, alias, wrappedKey); + long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { // Attempt to clean up 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 3644d368a7f3..ed570c3b660c 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 @@ -39,6 +39,7 @@ import java.util.Map; public class RecoverableKeyStoreDb { private static final String TAG = "RecoverableKeyStoreDb"; private static final int IDLE_TIMEOUT_SECONDS = 30; + private static final int LAST_SYNCED_AT_UNSYNCED = -1; private final RecoverableKeyStoreDbHelper mKeyStoreDbHelper; @@ -61,6 +62,7 @@ public class RecoverableKeyStoreDb { /** * Inserts a key into the database. * + * @param userId The uid of the profile the application is running under. * @param uid Uid of the application to whom the key belongs. * @param alias The alias of the key in the AndroidKeyStore. * @param wrappedKey The wrapped key. @@ -68,14 +70,15 @@ public class RecoverableKeyStoreDb { * * @hide */ - public long insertKey(int uid, String alias, WrappedKey wrappedKey) { + public long insertKey(int userId, int uid, String alias, WrappedKey wrappedKey) { SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); + values.put(KeysEntry.COLUMN_NAME_USER_ID, userId); values.put(KeysEntry.COLUMN_NAME_UID, uid); values.put(KeysEntry.COLUMN_NAME_ALIAS, alias); values.put(KeysEntry.COLUMN_NAME_NONCE, wrappedKey.getNonce()); values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, wrappedKey.getKeyMaterial()); - values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, -1); + values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, LAST_SYNCED_AT_UNSYNCED); values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId()); return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values); } @@ -130,16 +133,16 @@ public class RecoverableKeyStoreDb { } /** - * Returns all keys for the given {@code uid} and {@code platformKeyGenerationId}. + * Returns all keys for the given {@code userId} and {@code platformKeyGenerationId}. * - * @param uid User id of the profile to which all the keys are associated. + * @param userId User id of the profile to which all the keys are associated. * @param platformKeyGenerationId The generation ID of the platform key that wrapped these keys. * (i.e., this should be the most recent generation ID, as older platform keys are not * usable.) * * @hide */ - public Map getAllKeys(int uid, int platformKeyGenerationId) { + public Map getAllKeys(int userId, int platformKeyGenerationId) { SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase(); String[] projection = { KeysEntry._ID, @@ -147,10 +150,10 @@ public class RecoverableKeyStoreDb { KeysEntry.COLUMN_NAME_WRAPPED_KEY, KeysEntry.COLUMN_NAME_ALIAS}; String selection = - KeysEntry.COLUMN_NAME_UID + " = ? AND " + KeysEntry.COLUMN_NAME_USER_ID + " = ? AND " + KeysEntry.COLUMN_NAME_GENERATION_ID + " = ?"; String[] selectionArguments = { - Integer.toString(uid), Integer.toString(platformKeyGenerationId) }; + Integer.toString(userId), Integer.toString(platformKeyGenerationId) }; try ( Cursor cursor = db.query( 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 b6c168f41078..dcd1827d8576 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 @@ -29,6 +29,11 @@ class RecoverableKeyStoreDbContract { static final String TABLE_NAME = "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 generated the key. */ static final String COLUMN_NAME_UID = "uid"; 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 686820323241..b017fbb59a65 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 @@ -17,6 +17,7 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { private static final String SQL_CREATE_KEYS_ENTRY = "CREATE TABLE " + KeysEntry.TABLE_NAME + "( " + KeysEntry._ID + " INTEGER PRIMARY KEY," + + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER," + KeysEntry.COLUMN_NAME_UID + " INTEGER," + KeysEntry.COLUMN_NAME_ALIAS + " TEXT," + KeysEntry.COLUMN_NAME_NONCE + " BLOB," diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java index 3012931a7167..b3dbf93b6e7f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java @@ -61,6 +61,7 @@ public class RecoverableKeyGeneratorTest { private static final String UNSUPPORTED_CIPHER_ALGORITHM = "AES/CTR/NoPadding"; private static final String TEST_ALIAS = "karlin"; private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey"; + private static final int TEST_USER_ID = 1000; private static final int KEYSTORE_UID_SELF = -1; private static final int GCM_TAG_LENGTH_BITS = 128; private static final int GCM_NONCE_LENGTH_BYTES = 12; @@ -95,7 +96,8 @@ public class RecoverableKeyGeneratorTest { @Test public void generateAndStoreKey_setsKeyInKeyStore() throws Exception { - mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS); + mRecoverableKeyGenerator.generateAndStoreKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF); assertTrue(keyStore.containsAlias(TEST_ALIAS)); @@ -104,7 +106,8 @@ public class RecoverableKeyGeneratorTest { @Test public void generateAndStoreKey_storesKeyEnabledForAesGcmNoPaddingEncryptDecrypt() throws Exception { - mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS); + mRecoverableKeyGenerator.generateAndStoreKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF); SecretKey key = (SecretKey) keyStore.getKey(TEST_ALIAS, /*password=*/ null); @@ -117,7 +120,8 @@ public class RecoverableKeyGeneratorTest { @Test public void generateAndStoreKey_storesKeyDisabledForOtherModes() throws Exception { - mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS); + mRecoverableKeyGenerator.generateAndStoreKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF); SecretKey key = (SecretKey) keyStore.getKey(TEST_ALIAS, /*password=*/ null); @@ -133,7 +137,8 @@ public class RecoverableKeyGeneratorTest { @Test public void generateAndStoreKey_storesWrappedKey() throws Exception { - mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS); + mRecoverableKeyGenerator.generateAndStoreKey( + mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS); KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF); SecretKey key = (SecretKey) keyStore.getKey(TEST_ALIAS, /*password=*/ null); 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 d5ad9597bf1c..76cbea8e5d66 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 @@ -60,22 +60,23 @@ public class RecoverableKeyStoreDbTest { @Test public void insertKey_replacesOldKey() { int userId = 12; + int uid = 10009; String alias = "test"; WrappedKey oldWrappedKey = new WrappedKey( getUtf8Bytes("nonce1"), getUtf8Bytes("keymaterial1"), /*platformKeyGenerationId=*/ 1); mRecoverableKeyStoreDb.insertKey( - userId, alias, oldWrappedKey); + userId, uid, alias, oldWrappedKey); byte[] nonce = getUtf8Bytes("nonce2"); byte[] keyMaterial = getUtf8Bytes("keymaterial2"); WrappedKey newWrappedKey = new WrappedKey( nonce, keyMaterial, /*platformKeyGenerationId=*/2); mRecoverableKeyStoreDb.insertKey( - userId, alias, newWrappedKey); + userId, uid, alias, newWrappedKey); - WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(userId, alias); + WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); assertArrayEquals(nonce, retrievedKey.getNonce()); assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial()); assertEquals(2, retrievedKey.getPlatformKeyGenerationId()); @@ -83,6 +84,7 @@ public class RecoverableKeyStoreDbTest { @Test public void insertKey_allowsTwoUidsToHaveSameAlias() { + int userId = 6; String alias = "pcoulton"; WrappedKey key1 = new WrappedKey( getUtf8Bytes("nonce1"), @@ -93,8 +95,8 @@ public class RecoverableKeyStoreDbTest { getUtf8Bytes("key2"), /*platformKeyGenerationId=*/ 1); - mRecoverableKeyStoreDb.insertKey(/*uid=*/ 1, alias, key1); - mRecoverableKeyStoreDb.insertKey(/*uid=*/ 2, alias, key2); + mRecoverableKeyStoreDb.insertKey(userId, /*uid=*/ 1, alias, key1); + mRecoverableKeyStoreDb.insertKey(userId, /*uid=*/ 2, alias, key2); assertArrayEquals( getUtf8Bytes("nonce1"), @@ -115,14 +117,15 @@ public class RecoverableKeyStoreDbTest { @Test public void getKey_returnsInsertedKey() { int userId = 12; + int uid = 1009; int generationId = 6; String alias = "test"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId); - mRecoverableKeyStoreDb.insertKey(userId, alias, wrappedKey); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); - WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(userId, alias); + WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); assertArrayEquals(nonce, retrievedKey.getNonce()); assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial()); @@ -132,12 +135,13 @@ public class RecoverableKeyStoreDbTest { @Test public void getAllKeys_getsKeysWithUserIdAndGenerationId() { int userId = 12; + int uid = 1009; int generationId = 6; String alias = "test"; byte[] nonce = getUtf8Bytes("nonce"); byte[] keyMaterial = getUtf8Bytes("keymaterial"); WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId); - mRecoverableKeyStoreDb.insertKey(userId, alias, wrappedKey); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); Map keys = mRecoverableKeyStoreDb.getAllKeys(userId, generationId); @@ -152,12 +156,13 @@ public class RecoverableKeyStoreDbTest { @Test public void getAllKeys_doesNotReturnKeysWithBadGenerationId() { int userId = 12; + int uid = 6000; WrappedKey wrappedKey = new WrappedKey( getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), /*platformKeyGenerationId=*/ 5); mRecoverableKeyStoreDb.insertKey( - userId, /*alias=*/ "test", wrappedKey); + userId, uid, /*alias=*/ "test", wrappedKey); Map keys = mRecoverableKeyStoreDb.getAllKeys( userId, /*generationId=*/ 7); @@ -168,10 +173,11 @@ public class RecoverableKeyStoreDbTest { @Test public void getAllKeys_doesNotReturnKeysWithBadUserId() { int generationId = 12; + int uid = 10009; WrappedKey wrappedKey = new WrappedKey( getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), generationId); mRecoverableKeyStoreDb.insertKey( - /*userId=*/ 1, /*alias=*/ "test", wrappedKey); + /*userId=*/ 1, uid, /*alias=*/ "test", wrappedKey); Map keys = mRecoverableKeyStoreDb.getAllKeys( /*userId=*/ 2, generationId); -- 2.11.0