From b5b22b19f95139dc0617d478828f1a0dc183b6ad Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Fri, 27 Dec 2019 12:34:06 -0800 Subject: [PATCH] Use new params for platform key protection. UnlockedDeviceRequired=true keys survive LSKF removal. Generation id for keys created with new param will start from 1000000. New parameter only works for primary user. Added 2 seconds delay between phone unlock and running sync task. Test: atest com.android.server.locksettings.recoverablekeystore Bug: 111561213 Change-Id: Ifc48ef823a72fdbdff1a09563de0d0be0b004f79 --- .../recoverablekeystore/KeySyncTask.java | 21 +- .../PlatformKeyManager.java | 50 ++-- .../RecoverableKeyStoreManager.java | 30 +-- .../PlatformKeyManagerTest.java | 230 +++++++++--------- .../RecoverableKeyStoreManagerTest.java | 9 +- 5 files changed, 174 insertions(+), 166 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 29338ba06dc27..52750f392d5bd 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -20,6 +20,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_L import android.content.Context; import android.os.RemoteException; +import android.os.UserHandle; import android.security.Scrypt; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; @@ -163,16 +164,28 @@ public class KeySyncTask implements Runnable { } private void syncKeys() throws RemoteException { + int generation = mPlatformKeyManager.getGenerationId(mUserId); if (mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { // Application keys for the user will not be available for sync. Log.w(TAG, "Credentials are not set for user " + mUserId); - int generation = mPlatformKeyManager.getGenerationId(mUserId); - mPlatformKeyManager.invalidatePlatformKey(mUserId, generation); + if (generation < PlatformKeyManager.MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED + || mUserId != UserHandle.USER_SYSTEM) { + // Only invalidate keys with legacy protection param. + mPlatformKeyManager.invalidatePlatformKey(mUserId, generation); + } return; } if (isCustomLockScreen()) { - Log.w(TAG, "Unsupported credential type " + mCredentialType + "for user " + mUserId); - mRecoverableKeyStoreDb.invalidateKeysForUserIdOnCustomScreenLock(mUserId); + Log.w(TAG, "Unsupported credential type " + mCredentialType + " for user " + mUserId); + // Keys will be synced when user starts using non custom screen lock. + if (generation < PlatformKeyManager.MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED + || mUserId != UserHandle.USER_SYSTEM) { + mRecoverableKeyStoreDb.invalidateKeysForUserIdOnCustomScreenLock(mUserId); + } + return; + } + if (mPlatformKeyManager.isDeviceLocked(mUserId) && mUserId == UserHandle.USER_SYSTEM) { + Log.w(TAG, "Can't sync keys for locked user " + mUserId); return; } 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 0ad6c2a695568..0761cde825b66 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -67,8 +67,9 @@ import javax.crypto.spec.GCMParameterSpec; * @hide */ public class PlatformKeyManager { - private static final String TAG = "PlatformKeyManager"; + static final int MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED = 1000000; + private static final String TAG = "PlatformKeyManager"; private static final String KEY_ALGORITHM = "AES"; private static final int KEY_SIZE_BITS = 256; private static final String KEY_ALIAS_PREFIX = @@ -131,14 +132,14 @@ public class PlatformKeyManager { /** * 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. + * device is locked. * * @param userId The ID of the user to whose lock screen the platform key must be bound. * * @hide */ - public boolean isAvailable(int userId) { - return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId); + public boolean isDeviceLocked(int userId) { + return mContext.getSystemService(KeyguardManager.class).isDeviceLocked(userId); } /** @@ -169,7 +170,6 @@ public class PlatformKeyManager { * @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. * @throws IOException if there was an issue with local database update. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * @@ -177,13 +177,8 @@ public class PlatformKeyManager { */ @VisibleForTesting void regenerate(int userId) - throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException, + throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException { - 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) { @@ -192,6 +187,7 @@ public class PlatformKeyManager { invalidatePlatformKey(userId, generationId); nextId = generationId + 1; } + generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED); generateAndLoadKey(userId, nextId); } @@ -203,7 +199,6 @@ public class PlatformKeyManager { * @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. * @throws IOException if there was an issue with local database update. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * @@ -211,7 +206,7 @@ public class PlatformKeyManager { */ public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, - InsecureUserException, IOException, RemoteException { + IOException, RemoteException { init(userId); try { // Try to see if the decryption key is still accessible before using the encryption key. @@ -234,12 +229,11 @@ public class PlatformKeyManager { * @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 */ private PlatformEncryptionKey getEncryptKeyInternal(int userId) throws KeyStoreException, - UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException { + UnrecoverableKeyException, NoSuchAlgorithmException { int generationId = getGenerationId(userId); String alias = getEncryptAlias(userId, generationId); if (!isKeyLoaded(userId, generationId)) { @@ -258,7 +252,6 @@ public class PlatformKeyManager { * @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. * @throws IOException if there was an issue with local database update. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * @@ -266,7 +259,7 @@ public class PlatformKeyManager { */ public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, - InsecureUserException, IOException, RemoteException { + IOException, RemoteException { init(userId); try { PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId); @@ -288,12 +281,11 @@ public class PlatformKeyManager { * @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 */ private PlatformDecryptionKey getDecryptKeyInternal(int userId) throws KeyStoreException, - UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException { + UnrecoverableKeyException, NoSuchAlgorithmException { int generationId = getGenerationId(userId); String alias = getDecryptAlias(userId, generationId); if (!isKeyLoaded(userId, generationId)) { @@ -340,13 +332,8 @@ public class PlatformKeyManager { * @hide */ void init(int userId) - throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException, + throws KeyStoreException, NoSuchAlgorithmException, IOException, RemoteException { - if (!isAvailable(userId)) { - throw new InsecureUserException(String.format( - Locale.US, "%d does not have a lock screen set.", userId)); - } - int generationId = getGenerationId(userId); if (isKeyLoaded(userId, generationId)) { Log.i(TAG, String.format( @@ -363,6 +350,7 @@ public class PlatformKeyManager { generationId++; } + generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED); generateAndLoadKey(userId, generationId); } @@ -440,12 +428,16 @@ public class PlatformKeyManager { KeyProtection.Builder decryptionKeyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) - .setUserAuthenticationRequired(true) - .setUserAuthenticationValidityDurationSeconds( - USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); - if (userId != UserHandle.USER_SYSTEM) { + // Skip UserAuthenticationRequired for main user + if (userId == UserHandle.USER_SYSTEM) { + decryptionKeyProtection.setUnlockedDeviceRequired(true); + } else { + // With setUnlockedDeviceRequired, KeyStore thinks that device is locked . + decryptionKeyProtection.setUserAuthenticationRequired(true); + decryptionKeyProtection.setUserAuthenticationValidityDurationSeconds( + USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS); // Bind decryption key to secondary profile lock screen secret. long secureUserId = getGateKeeperService().getSecureUserId(userId); // TODO(b/124095438): Propagate this failure instead of silently failing. 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 383d5cf326c06..6d97ed7a69a7e 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -19,7 +19,6 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT; import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED; import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE; -import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT; import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING; @@ -46,7 +45,6 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; -import com.android.internal.util.Preconditions; import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException; import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils; import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException; @@ -76,8 +74,9 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.crypto.AEADBadTagException; @@ -89,13 +88,14 @@ import javax.crypto.AEADBadTagException; */ public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; + private static final long SYNC_DELAY_MILLIS = 2000; private static RecoverableKeyStoreManager mInstance; private final Context mContext; private final RecoverableKeyStoreDb mDatabase; private final RecoverySessionStorage mRecoverySessionStorage; - private final ExecutorService mExecutorService; + private final ScheduledExecutorService mExecutorService; private final RecoverySnapshotListenersStorage mListenersStorage; private final RecoverableKeyGenerator mRecoverableKeyGenerator; private final RecoverySnapshotStorage mSnapshotStorage; @@ -136,7 +136,7 @@ public class RecoverableKeyStoreManager { context.getApplicationContext(), db, new RecoverySessionStorage(), - Executors.newSingleThreadExecutor(), + Executors.newScheduledThreadPool(1), snapshotStorage, new RecoverySnapshotListenersStorage(), platformKeyManager, @@ -152,7 +152,7 @@ public class RecoverableKeyStoreManager { Context context, RecoverableKeyStoreDb recoverableKeyStoreDb, RecoverySessionStorage recoverySessionStorage, - ExecutorService executorService, + ScheduledExecutorService executorService, RecoverySnapshotStorage snapshotStorage, RecoverySnapshotListenersStorage listenersStorage, PlatformKeyManager platformKeyManager, @@ -724,8 +724,6 @@ public class RecoverableKeyStoreManager { throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException | IOException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } catch (InsecureUserException e) { - throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { @@ -793,8 +791,6 @@ public class RecoverableKeyStoreManager { throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException | IOException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } catch (InsecureUserException e) { - throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { @@ -915,7 +911,7 @@ public class RecoverableKeyStoreManager { int storedHashType, @NonNull byte[] credential, int userId) { // So as not to block the critical path unlocking the phone, defer to another thread. try { - mExecutorService.execute(KeySyncTask.newInstance( + mExecutorService.schedule(KeySyncTask.newInstance( mContext, mDatabase, mSnapshotStorage, @@ -923,7 +919,10 @@ public class RecoverableKeyStoreManager { userId, storedHashType, credential, - /*credentialUpdated=*/ false)); + /*credentialUpdated=*/ false), + SYNC_DELAY_MILLIS, + TimeUnit.MILLISECONDS + ); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e); } catch (KeyStoreException e) { @@ -947,7 +946,7 @@ public class RecoverableKeyStoreManager { int userId) { // So as not to block the critical path unlocking the phone, defer to another thread. try { - mExecutorService.execute(KeySyncTask.newInstance( + mExecutorService.schedule(KeySyncTask.newInstance( mContext, mDatabase, mSnapshotStorage, @@ -955,7 +954,10 @@ public class RecoverableKeyStoreManager { userId, storedHashType, credential, - /*credentialUpdated=*/ true)); + /*credentialUpdated=*/ true), + SYNC_DELAY_MILLIS, + TimeUnit.MILLISECONDS + ); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e); } catch (KeyStoreException e) { 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 821d97acc2302..670bd8107bed9 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 @@ -67,13 +67,25 @@ import javax.crypto.KeyGenerator; public class PlatformKeyManagerTest { private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; - private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15; + private static final int MIN_GENERATION_ID = 1000000; + private static final int PRIMARY_USER_ID_FIXTURE = 0; private static final int USER_ID_FIXTURE = 42; private static final long USER_SID = 4200L; private static final String KEY_ALGORITHM = "AES"; private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private static final String TESTING_KEYSTORE_KEY_ALIAS = "testing-key-store-key-alias"; + private static final String ENCRYPTION_KEY_ALIAS_1 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000000/encrypt"; + private static final String DECRYPTION_KEY_ALIAS_1 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000000/decrypt"; + private static final String DECRYPTION_KEY_FOR_ALIAS_PRIMARY_USER_1 = + "com.android.server.locksettings.recoverablekeystore/platform/0/1000000/decrypt"; + private static final String ENCRYPTION_KEY_ALIAS_2 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000001/encrypt"; + private static final String DECRYPTION_KEY_ALIAS_2 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000001/decrypt"; + @Mock private Context mContext; @Mock private KeyStoreProxy mKeyStoreProxy; @Mock private KeyguardManager mKeyguardManager; @@ -114,7 +126,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any(), any()); } @@ -156,7 +168,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any(), any()); } @@ -187,19 +199,33 @@ public class PlatformKeyManagerTest { } @Test - public void init_createsDecryptKeyWithAuthenticationRequired() throws Exception { - mPlatformKeyManager.init(USER_ID_FIXTURE); + public void init_primaryUser_createsDecryptKeyWithUnlockedDeviceRequired() throws Exception { + mPlatformKeyManager.init(PRIMARY_USER_ID_FIXTURE); - assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired()); + assertTrue(getDecryptKeyProtectionForPrimaryUser().isUnlockedDeviceRequired()); } @Test - public void init_createsDecryptKeyWithAuthenticationValidFor15Seconds() throws Exception { + public void init_primaryUser_createsDecryptKeyWithoutAuthenticationRequired() throws Exception { + mPlatformKeyManager.init(PRIMARY_USER_ID_FIXTURE); + + assertFalse(getDecryptKeyProtectionForPrimaryUser().isUserAuthenticationRequired()); + } + + @Test + public void init_secondaryUser_createsDecryptKeyWithoutUnlockedDeviceRequired() + throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals( - USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS, - getDecryptKeyProtection().getUserAuthenticationValidityDurationSeconds()); + assertFalse(getDecryptKeyProtection().isUnlockedDeviceRequired()); + } + + @Test + public void init_secondaryUserUser_createsDecryptKeyWithAuthenticationRequired() + throws Exception { + mPlatformKeyManager.init(USER_ID_FIXTURE); + + assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired()); } @Test @@ -219,7 +245,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); verify(mKeyStoreProxy, never()).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any(), any()); } @@ -231,7 +257,7 @@ public class PlatformKeyManagerTest { expectThrows(RemoteException.class, () -> mPlatformKeyManager.init(USER_ID_FIXTURE)); verify(mKeyStoreProxy, never()).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any(), any()); } @@ -251,15 +277,15 @@ public class PlatformKeyManagerTest { public void init_savesGenerationIdToDatabase() throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(1, + assertEquals(MIN_GENERATION_ID, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE)); } @Test - public void init_setsGenerationIdTo1() throws Exception { + public void init_setsGenerationId() throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test @@ -268,22 +294,20 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID + 1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test @@ -294,226 +318,194 @@ public class PlatformKeyManagerTest { @Test public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy.getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any())).thenReturn(generateAndroidKeyStoreKey()); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any()); } @Test public void getDecryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(false); // was removed + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); // new version is available + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy.getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any())).thenReturn(generateAndroidKeyStoreKey()); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt")); + eq(DECRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any()); } @Test public void getDecryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(false); // was removed + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any()); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any()); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any()); when(mKeyStoreProxy.getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any())).thenReturn(generateAndroidKeyStoreKey()); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt")); + eq(DECRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfDecryptKeyIsUnrecoverable() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any()); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(false); // was removed + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); // new version is available + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(false); // was removed + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); // new version is available + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(false); // was removed + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_getsEncryptKeyWithCorrectAlias() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any()); } @@ -523,7 +515,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); - assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID + 1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test @@ -533,17 +525,17 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt")); + eq(DECRYPTION_KEY_ALIAS_1)); mPlatformKeyManager.regenerate(USER_ID_FIXTURE); // Removes second generation keys. verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_2)); verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt")); + eq(DECRYPTION_KEY_ALIAS_2)); } @Test @@ -553,7 +545,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any(), any()); } @@ -565,14 +557,14 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any(), any()); } private KeyProtection getEncryptKeyProtection() throws Exception { verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any(), mProtectionParameterCaptor.capture()); return (KeyProtection) mProtectionParameterCaptor.getValue(); @@ -580,7 +572,15 @@ public class PlatformKeyManagerTest { private KeyProtection getDecryptKeyProtection() throws Exception { verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), + any(), + mProtectionParameterCaptor.capture()); + return (KeyProtection) mProtectionParameterCaptor.getValue(); + } + + private KeyProtection getDecryptKeyProtectionForPrimaryUser() throws Exception { + verify(mKeyStoreProxy).setEntry( + eq(DECRYPTION_KEY_FOR_ALIAS_PRIMARY_USER_1), any(), mProtectionParameterCaptor.capture()); return (KeyProtection) mProtectionParameterCaptor.getValue(); 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 68900175cc8f6..ac7447006444b 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 @@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; @@ -84,7 +85,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Map; import java.util.Random; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -157,7 +158,7 @@ public class RecoverableKeyStoreManagerTest { @Mock private PlatformKeyManager mPlatformKeyManager; @Mock private ApplicationKeyStorage mApplicationKeyStorage; @Mock private CleanupManager mCleanupManager; - @Mock private ExecutorService mExecutorService; + @Mock private ScheduledExecutorService mExecutorService; @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; private RecoverableKeyStoreDb mRecoverableKeyStoreDb; @@ -1253,7 +1254,7 @@ public class RecoverableKeyStoreManagerTest { mRecoverableKeyStoreManager.lockScreenSecretAvailable( LockPatternUtils.CREDENTIAL_TYPE_PATTERN, "password".getBytes(), 11); - verify(mExecutorService).execute(any()); + verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any()); } @Test @@ -1263,7 +1264,7 @@ public class RecoverableKeyStoreManagerTest { "password".getBytes(), 11); - verify(mExecutorService).execute(any()); + verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any()); } private static byte[] encryptedApplicationKey(