OSDN Git Service

Refactor PlatformKeyManger to remove mUserId to fix two tests that fail
authorBo Zhu <bozhu@google.com>
Fri, 5 Jan 2018 06:42:36 +0000 (22:42 -0800)
committerBo Zhu <bozhu@google.com>
Fri, 5 Jan 2018 21:09:52 +0000 (13:09 -0800)
on clean devices without secure screenlocks

The mUserId is removed to allow a mocked PlatformKeyManager to be passed
into RecoverableKeyStoreManager for tests.

Test: adb shell am instrument -w -e package
com.android.server.locksettings.recoverablekeystore
com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner

Change-Id: I9b1fda3c7d869b683cd71cb81ea88da5d995ace9

services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java

index e385833..6dafd0b 100644 (file)
@@ -89,7 +89,7 @@ public class KeySyncTask implements Runnable {
                 userId,
                 credentialType,
                 credential,
-                () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb, userId));
+                () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb));
     }
 
     /**
@@ -246,7 +246,7 @@ public class KeySyncTask implements Runnable {
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
             NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
         PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
-        PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey();
+        PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
         Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
                 mUserId, decryptKey.getGenerationId());
         return WrappedKey.unwrapKeys(decryptKey, wrappedKeys);
index a8b8361..7005de5 100644 (file)
@@ -71,7 +71,6 @@ public class PlatformKeyManager {
     private final Context mContext;
     private final KeyStoreProxy mKeyStore;
     private final RecoverableKeyStoreDb mDatabase;
-    private final int mUserId;
 
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
 
@@ -80,34 +79,25 @@ public class PlatformKeyManager {
      * defined by {@code context}.
      *
      * @param context This should be the context of the RecoverableKeyStoreLoader service.
-     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws KeyStoreException if failed to initialize AndroidKeyStore.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
-     * @throws InsecureUserException if the user does not have a lock screen set.
      * @throws SecurityException if the caller does not have permission to write to /data/system.
      *
      * @hide
      */
-    public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database,
-            int userId)
-            throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
-        context = context.getApplicationContext();
-        PlatformKeyManager keyManager = new PlatformKeyManager(
-                userId,
-                context,
+    public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database)
+            throws KeyStoreException, NoSuchAlgorithmException {
+        return new PlatformKeyManager(
+                context.getApplicationContext(),
                 new KeyStoreProxyImpl(getAndLoadAndroidKeyStore()),
                 database);
-        keyManager.init();
-        return keyManager;
     }
 
     @VisibleForTesting
     PlatformKeyManager(
-            int userId,
             Context context,
             KeyStoreProxy keyStore,
             RecoverableKeyStoreDb database) {
-        mUserId = userId;
         mKeyStore = keyStore;
         mContext = context;
         mDatabase = database;
@@ -118,67 +108,91 @@ public class PlatformKeyManager {
      * key has to be replaced. (e.g., because the user has removed and then re-added their lock
      * screen). Returns -1 if no key has been generated yet.
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
+     *
      * @hide
      */
-    public int getGenerationId() {
-        return mDatabase.getPlatformKeyGenerationId(mUserId);
+    public int getGenerationId(int userId) {
+        return mDatabase.getPlatformKeyGenerationId(userId);
     }
 
     /**
      * Returns {@code true} if the platform key is available. A platform key won't be available if
      * the user has not set up a lock screen.
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
+     *
      * @hide
      */
-    public boolean isAvailable() {
-        return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(mUserId);
+    public boolean isAvailable(int userId) {
+        return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId);
     }
 
     /**
      * Generates a new key and increments the generation ID. Should be invoked if the platform key
      * is corrupted and needs to be rotated.
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
      * @throws KeyStoreException if there is an error in AndroidKeyStore.
+     * @throws InsecureUserException if the user does not have a lock screen set.
      *
      * @hide
      */
-    public void regenerate() throws NoSuchAlgorithmException, KeyStoreException {
-        int nextId = getGenerationId() + 1;
-        generateAndLoadKey(nextId);
+    public void regenerate(int userId)
+            throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException {
+        if (!isAvailable(userId)) {
+            throw new InsecureUserException(String.format(
+                    Locale.US, "%d does not have a lock screen set.", userId));
+        }
+
+        int generationId = getGenerationId(userId);
+        int nextId;
+        if (generationId == -1) {
+            nextId = 1;
+        } else {
+            nextId = generationId + 1;
+        }
+        generateAndLoadKey(userId, nextId);
     }
 
     /**
      * Returns the platform key used for encryption.
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws KeyStoreException if there was an AndroidKeyStore error.
      * @throws UnrecoverableKeyException if the key could not be recovered.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+     * @throws InsecureUserException if the user does not have a lock screen set.
      *
      * @hide
      */
-    public PlatformEncryptionKey getEncryptKey()
-            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
-        int generationId = getGenerationId();
+    public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException,
+           UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+        init(userId);
+        int generationId = getGenerationId(userId);
         AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
-                getEncryptAlias(generationId), /*password=*/ null);
+                getEncryptAlias(userId, generationId), /*password=*/ null);
         return new PlatformEncryptionKey(generationId, key);
     }
 
     /**
      * Returns the platform key used for decryption. Only works after a recent screen unlock.
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws KeyStoreException if there was an AndroidKeyStore error.
      * @throws UnrecoverableKeyException if the key could not be recovered.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+     * @throws InsecureUserException if the user does not have a lock screen set.
      *
      * @hide
      */
-    public PlatformDecryptionKey getDecryptKey()
-            throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
-        int generationId = getGenerationId();
+    public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
+           UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+        init(userId);
+        int generationId = getGenerationId(userId);
         AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
-                getDecryptAlias(generationId), /*password=*/ null);
+                getDecryptAlias(userId, generationId), /*password=*/ null);
         return new PlatformDecryptionKey(generationId, key);
     }
 
@@ -186,38 +200,36 @@ public class PlatformKeyManager {
      * Initializes the class. If there is no current platform key, and the user has a lock screen
      * set, will create the platform key and set the generation ID.
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws KeyStoreException if there was an error in AndroidKeyStore.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
      *
      * @hide
      */
-    public void init() throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
-        if (!isAvailable()) {
+    void init(int userId)
+            throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
+        if (!isAvailable(userId)) {
             throw new InsecureUserException(String.format(
-                    Locale.US, "%d does not have a lock screen set.", mUserId));
+                    Locale.US, "%d does not have a lock screen set.", userId));
         }
 
-        int generationId = getGenerationId();
-        if (isKeyLoaded(generationId)) {
+        int generationId = getGenerationId(userId);
+        if (isKeyLoaded(userId, generationId)) {
             Log.i(TAG, String.format(
                     Locale.US, "Platform key generation %d exists already.", generationId));
             return;
         }
         if (generationId == -1) {
             Log.i(TAG, "Generating initial platform ID.");
+            generationId = 1;
         } else {
             Log.w(TAG, String.format(Locale.US, "Platform generation ID was %d but no "
                     + "entry was present in AndroidKeyStore. Generating fresh key.", generationId));
-        }
-
-        if (generationId == -1) {
-            generationId = 1;
-        } else {
             // Had to generate a fresh key, bump the generation id
             generationId++;
         }
 
-        generateAndLoadKey(generationId);
+        generateAndLoadKey(userId, generationId);
     }
 
     /**
@@ -227,11 +239,12 @@ public class PlatformKeyManager {
      * <p>These IDs look as follows:
      * {@code com.security.recoverablekeystore/platform/<user id>/<generation id>/encrypt}
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @param generationId The generation ID.
      * @return The alias.
      */
-    private String getEncryptAlias(int generationId) {
-        return KEY_ALIAS_PREFIX + mUserId + "/" + generationId + "/" + ENCRYPT_KEY_ALIAS_SUFFIX;
+    private String getEncryptAlias(int userId, int generationId) {
+        return KEY_ALIAS_PREFIX + userId + "/" + generationId + "/" + ENCRYPT_KEY_ALIAS_SUFFIX;
     }
 
     /**
@@ -241,18 +254,19 @@ public class PlatformKeyManager {
      * <p>These IDs look as follows:
      * {@code com.security.recoverablekeystore/platform/<user id>/<generation id>/decrypt}
      *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @param generationId The generation ID.
      * @return The alias.
      */
-    private String getDecryptAlias(int generationId) {
-        return KEY_ALIAS_PREFIX + mUserId + "/" + generationId + "/" + DECRYPT_KEY_ALIAS_SUFFIX;
+    private String getDecryptAlias(int userId, int generationId) {
+        return KEY_ALIAS_PREFIX + userId + "/" + generationId + "/" + DECRYPT_KEY_ALIAS_SUFFIX;
     }
 
     /**
      * Sets the current generation ID to {@code generationId}.
      */
-    private void setGenerationId(int generationId) {
-        mDatabase.setPlatformKeyGenerationId(mUserId, generationId);
+    private void setGenerationId(int userId, int generationId) {
+        mDatabase.setPlatformKeyGenerationId(userId, generationId);
     }
 
     /**
@@ -261,9 +275,9 @@ public class PlatformKeyManager {
      *
      * @throws KeyStoreException if there was an error checking AndroidKeyStore.
      */
-    private boolean isKeyLoaded(int generationId) throws KeyStoreException {
-        return mKeyStore.containsAlias(getEncryptAlias(generationId))
-                && mKeyStore.containsAlias(getDecryptAlias(generationId));
+    private boolean isKeyLoaded(int userId, int generationId) throws KeyStoreException {
+        return mKeyStore.containsAlias(getEncryptAlias(userId, generationId))
+                && mKeyStore.containsAlias(getDecryptAlias(userId, generationId));
     }
 
     /**
@@ -274,10 +288,10 @@ public class PlatformKeyManager {
      *     available since API version 1.
      * @throws KeyStoreException if there was an issue loading the keys into AndroidKeyStore.
      */
-    private void generateAndLoadKey(int generationId)
+    private void generateAndLoadKey(int userId, int generationId)
             throws NoSuchAlgorithmException, KeyStoreException {
-        String encryptAlias = getEncryptAlias(generationId);
-        String decryptAlias = getDecryptAlias(generationId);
+        String encryptAlias = getEncryptAlias(userId, generationId);
+        String decryptAlias = getDecryptAlias(userId, generationId);
         SecretKey secretKey = generateAesKey();
 
         mKeyStore.setEntry(
@@ -296,10 +310,10 @@ public class PlatformKeyManager {
                             USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
-                    .setBoundToSpecificSecureUserId(mUserId)
+                    .setBoundToSpecificSecureUserId(userId)
                     .build());
 
-        setGenerationId(generationId);
+        setGenerationId(userId, generationId);
 
         try {
             secretKey.destroy();
index 84d4722..3379f83 100644 (file)
@@ -85,22 +85,34 @@ public class RecoverableKeyStoreManager {
     private final RecoverySnapshotListenersStorage mListenersStorage;
     private final RecoverableKeyGenerator mRecoverableKeyGenerator;
     private final RecoverySnapshotStorage mSnapshotStorage;
+    private final PlatformKeyManager mPlatformKeyManager;
 
     /**
      * Returns a new or existing instance.
      *
      * @hide
      */
-    public static synchronized RecoverableKeyStoreManager getInstance(Context mContext) {
+    public static synchronized RecoverableKeyStoreManager getInstance(Context context) {
         if (mInstance == null) {
-            RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(mContext);
+            RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
+            PlatformKeyManager platformKeyManager;
+            try {
+                platformKeyManager = PlatformKeyManager.getInstance(context, db);
+            } catch (NoSuchAlgorithmException e) {
+                // Impossible: all algorithms must be supported by AOSP
+                throw new RuntimeException(e);
+            } catch (KeyStoreException e) {
+                throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
+            }
+
             mInstance = new RecoverableKeyStoreManager(
-                    mContext.getApplicationContext(),
+                    context.getApplicationContext(),
                     db,
                     new RecoverySessionStorage(),
                     Executors.newSingleThreadExecutor(),
                     new RecoverySnapshotStorage(),
-                    new RecoverySnapshotListenersStorage());
+                    new RecoverySnapshotListenersStorage(),
+                    platformKeyManager);
         }
         return mInstance;
     }
@@ -112,13 +124,16 @@ public class RecoverableKeyStoreManager {
             RecoverySessionStorage recoverySessionStorage,
             ExecutorService executorService,
             RecoverySnapshotStorage snapshotStorage,
-            RecoverySnapshotListenersStorage listenersStorage) {
+            RecoverySnapshotListenersStorage listenersStorage,
+            PlatformKeyManager platformKeyManager) {
         mContext = context;
         mDatabase = recoverableKeyStoreDb;
         mRecoverySessionStorage = recoverySessionStorage;
         mExecutorService = executorService;
         mListenersStorage = listenersStorage;
         mSnapshotStorage = snapshotStorage;
+        mPlatformKeyManager = platformKeyManager;
+
         try {
             mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
         } catch (NoSuchAlgorithmException e) {
@@ -380,12 +395,10 @@ public class RecoverableKeyStoreManager {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
-        PlatformEncryptionKey encryptionKey;
 
+        PlatformEncryptionKey encryptionKey;
         try {
-            PlatformKeyManager platformKeyManager = PlatformKeyManager.getInstance(
-                    mContext, mDatabase, userId);
-            encryptionKey = platformKeyManager.getEncryptKey();
+            encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
         } catch (NoSuchAlgorithmException e) {
             // Impossible: all algorithms must be supported by AOSP
             throw new RuntimeException(e);
index 1aac8ce..a0dcfdc 100644 (file)
@@ -114,7 +114,7 @@ public class KeySyncTaskTest {
 
         mWrappingKey = generateAndroidKeyStoreKey();
         mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
-        when(mPlatformKeyManager.getDecryptKey()).thenReturn(
+        when(mPlatformKeyManager.getDecryptKey(TEST_USER_ID)).thenReturn(
                 new PlatformDecryptionKey(TEST_GENERATION_ID, mWrappingKey));
     }
 
index 97fbca2..b1bff70 100644 (file)
@@ -78,7 +78,7 @@ public class PlatformKeyManagerTest {
         mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
         mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
         mPlatformKeyManager = new PlatformKeyManager(
-                USER_ID_FIXTURE, mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
+                mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
 
         when(mContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
         when(mContext.getSystemServiceName(any())).thenReturn("test");
@@ -93,7 +93,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsEncryptKeyWithCorrectAlias() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy).setEntry(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
@@ -103,14 +103,14 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsEncryptKeyWithCorrectPurposes() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertEquals(KeyProperties.PURPOSE_ENCRYPT, getEncryptKeyProtection().getPurposes());
     }
 
     @Test
     public void init_createsEncryptKeyWithCorrectPaddings() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertArrayEquals(
                 new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
@@ -119,7 +119,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsEncryptKeyWithCorrectBlockModes() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertArrayEquals(
                 new String[] { KeyProperties.BLOCK_MODE_GCM },
@@ -128,14 +128,14 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsEncryptKeyWithoutAuthenticationRequired() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertFalse(getEncryptKeyProtection().isUserAuthenticationRequired());
     }
 
     @Test
     public void init_createsDecryptKeyWithCorrectAlias() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy).setEntry(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
@@ -145,14 +145,14 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsDecryptKeyWithCorrectPurposes() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertEquals(KeyProperties.PURPOSE_DECRYPT, getDecryptKeyProtection().getPurposes());
     }
 
     @Test
     public void init_createsDecryptKeyWithCorrectPaddings() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertArrayEquals(
                 new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
@@ -161,7 +161,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsDecryptKeyWithCorrectBlockModes() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertArrayEquals(
                 new String[] { KeyProperties.BLOCK_MODE_GCM },
@@ -170,14 +170,14 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsDecryptKeyWithAuthenticationRequired() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired());
     }
 
     @Test
     public void init_createsDecryptKeyWithAuthenticationValidFor15Seconds() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertEquals(
                 USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS,
@@ -186,7 +186,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsDecryptKeyBoundToTheUsersAuthentication() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertEquals(
                 USER_ID_FIXTURE,
@@ -195,7 +195,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_createsBothKeysWithSameMaterial() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy, times(2)).setEntry(any(), mEntryArgumentCaptor.capture(), any());
         List<KeyStore.Entry> entries = mEntryArgumentCaptor.getAllValues();
@@ -206,7 +206,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_savesGenerationIdToDatabase() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
         assertEquals(1,
                 mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE));
@@ -214,23 +214,23 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void init_setsGenerationIdTo1() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        assertEquals(1, mPlatformKeyManager.getGenerationId());
+        assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
     }
 
     @Test
     public void init_incrementsGenerationIdIfKeyIsUnavailable() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        assertEquals(2, mPlatformKeyManager.getGenerationId());
+        assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
     }
 
     @Test
     public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
         when(mKeyStoreProxy
                 .containsAlias("com.android.server.locksettings.recoverablekeystore/"
                         + "platform/42/1/decrypt")).thenReturn(true);
@@ -238,21 +238,19 @@ public class PlatformKeyManagerTest {
                 .containsAlias("com.android.server.locksettings.recoverablekeystore/"
                         + "platform/42/1/encrypt")).thenReturn(true);
 
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        assertEquals(1, mPlatformKeyManager.getGenerationId());
+        assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
     }
 
     @Test
     public void getGenerationId_returnsMinusOneIfNotInitialized() throws Exception {
-        assertEquals(-1, mPlatformKeyManager.getGenerationId());
+        assertEquals(-1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
     }
 
     @Test
     public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
-        mPlatformKeyManager.init();
-
-        mPlatformKeyManager.getDecryptKey();
+        mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy).getKey(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
@@ -261,9 +259,7 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
-        mPlatformKeyManager.init();
-
-        mPlatformKeyManager.getEncryptKey();
+        mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy).getKey(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
@@ -272,18 +268,18 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void regenerate_incrementsTheGenerationId() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        mPlatformKeyManager.regenerate();
+        mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
 
-        assertEquals(2, mPlatformKeyManager.getGenerationId());
+        assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
     }
 
     @Test
     public void regenerate_generatesANewEncryptKeyWithTheCorrectAlias() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        mPlatformKeyManager.regenerate();
+        mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy).setEntry(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
@@ -293,9 +289,9 @@ public class PlatformKeyManagerTest {
 
     @Test
     public void regenerate_generatesANewDecryptKeyWithTheCorrectAlias() throws Exception {
-        mPlatformKeyManager.init();
+        mPlatformKeyManager.init(USER_ID_FIXTURE);
 
-        mPlatformKeyManager.regenerate();
+        mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
 
         verify(mKeyStoreProxy).setEntry(
                 eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
index 445fbde..26f27b4 100644 (file)
@@ -36,9 +36,11 @@ import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
-import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
 import android.security.recoverablekeystore.KeyDerivationParameters;
 import android.security.recoverablekeystore.KeyEntryRecoveryData;
 import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
@@ -67,9 +69,8 @@ import java.util.concurrent.Executors;
 import java.util.Map;
 import java.util.Random;
 
-import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 @SmallTest
@@ -77,7 +78,6 @@ import javax.crypto.spec.SecretKeySpec;
 public class RecoverableKeyStoreManagerTest {
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
 
-    private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
     private static final String TEST_SESSION_ID = "karlin";
     private static final byte[] TEST_PUBLIC_KEY = new byte[] {
         (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -97,6 +97,8 @@ public class RecoverableKeyStoreManagerTest {
     private static final byte[] TEST_SECRET = getUtf8Bytes("password1234");
     private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge");
     private static final byte[] TEST_VAULT_PARAMS = getUtf8Bytes("vault_params");
+    private static final int TEST_GENERATION_ID = 2;
+    private static final int TEST_USER_ID = 10009;
     private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
     private static final byte[] RECOVERY_RESPONSE_HEADER =
             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
@@ -105,20 +107,24 @@ public class RecoverableKeyStoreManagerTest {
     private static final int GENERATION_ID = 1;
     private static final byte[] NONCE = getUtf8Bytes("nonce");
     private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
-    private static final int GCM_TAG_SIZE_BITS = 128;
+    private static final String KEY_ALGORITHM = "AES";
+    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+    private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
 
     @Mock private Context mMockContext;
     @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
     @Mock private KeyguardManager mKeyguardManager;
+    @Mock private PlatformKeyManager mPlatformKeyManager;
 
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
     private File mDatabaseFile;
     private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
     private RecoverySessionStorage mRecoverySessionStorage;
     private RecoverySnapshotStorage mRecoverySnapshotStorage;
+    private PlatformEncryptionKey mPlatformEncryptionKey;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         Context context = InstrumentationRegistry.getTargetContext();
@@ -130,7 +136,11 @@ public class RecoverableKeyStoreManagerTest {
         when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
         when(mMockContext.getSystemServiceName(any())).thenReturn("test");
         when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
-        when(mKeyguardManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mKeyguardManager.isDeviceSecure(TEST_USER_ID)).thenReturn(true);
+
+        mPlatformEncryptionKey =
+                new PlatformEncryptionKey(TEST_GENERATION_ID, generateAndroidKeyStoreKey());
+        when(mPlatformKeyManager.getEncryptKey(anyInt())).thenReturn(mPlatformEncryptionKey);
 
         mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
                 mMockContext,
@@ -138,7 +148,8 @@ public class RecoverableKeyStoreManagerTest {
                 mRecoverySessionStorage,
                 Executors.newSingleThreadExecutor(),
                 mRecoverySnapshotStorage,
-                mMockListenersStorage);
+                mMockListenersStorage,
+                mPlatformKeyManager);
     }
 
     @After
@@ -212,7 +223,7 @@ public class RecoverableKeyStoreManagerTest {
                     TEST_VAULT_CHALLENGE,
                     ImmutableList.of());
             fail("should have thrown");
-        } catch (RemoteException e) {
+        } catch (ServiceSpecificException e) {
             assertEquals("Only a single KeyStoreRecoveryMetadata is supported", e.getMessage());
         }
     }
@@ -232,7 +243,7 @@ public class RecoverableKeyStoreManagerTest {
                                     KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
                                     TEST_SECRET)));
             fail("should have thrown");
-        } catch (RemoteException e) {
+        } catch (ServiceSpecificException e) {
             assertEquals("Not a valid X509 key", e.getMessage());
         }
     }
@@ -273,7 +284,6 @@ public class RecoverableKeyStoreManagerTest {
             fail("should have thrown");
         } catch (ServiceSpecificException e) {
             assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key");
-            //assertEquals("Failed to decrypt recovery key", e.getMessage());
         }
     }
 
@@ -304,8 +314,8 @@ public class RecoverableKeyStoreManagerTest {
                     /*encryptedRecoveryKey=*/ encryptedClaimResponse,
                     /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
             fail("should have thrown");
-        } catch (RemoteException e) {
-            assertEquals("Failed to recover key with alias 'nick'", e.getMessage());
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).startsWith("Failed to recover key with alias 'nick'");
         }
     }
 
@@ -466,4 +476,16 @@ public class RecoverableKeyStoreManagerTest {
         new Random().nextBytes(bytes);
         return bytes;
     }
+
+    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                KEY_ALGORITHM,
+                ANDROID_KEY_STORE_PROVIDER);
+        keyGenerator.init(new KeyGenParameterSpec.Builder(
+                WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .build());
+        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+    }
 }