Merge "Use new params for platform key protection."

This commit is contained in:
Dmitry Dementyev
2020-01-10 21:14:10 +00:00
committed by Android (Google) Code Review
5 changed files with 174 additions and 166 deletions

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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(