From 3462c832d1acccca3cdd541dea86442eb81536ab Mon Sep 17 00:00:00 2001 From: Bo Zhu Date: Thu, 4 Jan 2018 22:42:36 -0800 Subject: [PATCH] Refactor PlatformKeyManger to remove mUserId to fix two tests that fail 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 --- .../recoverablekeystore/KeySyncTask.java | 4 +- .../PlatformKeyManager.java | 122 ++++++++++-------- .../RecoverableKeyStoreManager.java | 31 +++-- .../recoverablekeystore/KeySyncTaskTest.java | 2 +- .../PlatformKeyManagerTest.java | 70 +++++----- .../RecoverableKeyStoreManagerTest.java | 48 +++++-- 6 files changed, 161 insertions(+), 116 deletions(-) diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index e385833bac636..6dafd0bf0e620 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -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 wrappedKeys = mRecoverableKeyStoreDb.getAllKeys( mUserId, decryptKey.getGenerationId()); return WrappedKey.unwrapKeys(decryptKey, wrappedKeys); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java index a8b8361b4f03d..7005de5a6dd55 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -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 { *

These IDs look as follows: * {@code com.security.recoverablekeystore/platform///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 { *

These IDs look as follows: * {@code com.security.recoverablekeystore/platform///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(); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 84d472275b0c1..3379f83d52c8d 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -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); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index 1aac8ce0a1f60..a0dcfdc5f808d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -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)); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java index 97fbca2403bf1..b1bff70422d4f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java @@ -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 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"), diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index 445fbdeab6f62..26f27b47e3872 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -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(); + } }