diff options
3 files changed, 51 insertions, 17 deletions
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 7009a41726e2..ce3fb850b517 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -166,6 +166,7 @@ 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}. * @@ -174,7 +175,7 @@ public class PlatformKeyManager { @VisibleForTesting void regenerate(int userId) throws NoSuchAlgorithmException, KeyStoreException, IOException, - RemoteException { + RemoteException, InsecureUserException { int generationId = getGenerationId(userId); int nextId; if (generationId == -1) { @@ -195,13 +196,14 @@ public class PlatformKeyManager { * @throws UnrecoverableKeyException if the key could not be recovered. * @throws NoSuchAlgorithmException if AES is unavailable - should never occur. * @throws IOException if there was an issue with local database update. + * @throws InsecureUserException if the user does not have a lock screen set. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * * @hide */ public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, - IOException, RemoteException { + IOException, RemoteException, InsecureUserException { init(userId); try { // Try to see if the decryption key is still accessible before using the encryption key. @@ -254,7 +256,7 @@ public class PlatformKeyManager { */ public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, - IOException, RemoteException { + IOException, InsecureUserException, RemoteException { init(userId); try { PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId); @@ -328,7 +330,7 @@ public class PlatformKeyManager { */ void init(int userId) throws KeyStoreException, NoSuchAlgorithmException, IOException, - RemoteException { + RemoteException, InsecureUserException { int generationId = getGenerationId(userId); if (isKeyLoaded(userId, generationId)) { Log.i(TAG, String.format( @@ -414,7 +416,8 @@ public class PlatformKeyManager { * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. */ private void generateAndLoadKey(int userId, int generationId) - throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException { + throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException, + InsecureUserException { String encryptAlias = getEncryptAlias(userId, generationId); String decryptAlias = getDecryptAlias(userId, generationId); // SecretKey implementation doesn't provide reliable way to destroy the secret @@ -427,23 +430,31 @@ public class PlatformKeyManager { .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); // Skip UserAuthenticationRequired for main user if (userId == UserHandle.USER_SYSTEM) { + // attempt to store key will fail if screenlock is not set. decryptionKeyProtection.setUnlockedDeviceRequired(true); } else { // Don't set protection params to prevent losing key. } // Store decryption key first since it is more likely to fail. - mKeyStore.setEntry( - decryptAlias, - new KeyStore.SecretKeyEntry(secretKey), - decryptionKeyProtection.build()); - mKeyStore.setEntry( - encryptAlias, - new KeyStore.SecretKeyEntry(secretKey), - new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .build()); - + try { + mKeyStore.setEntry( + decryptAlias, + new KeyStore.SecretKeyEntry(secretKey), + decryptionKeyProtection.build()); + mKeyStore.setEntry( + encryptAlias, + new KeyStore.SecretKeyEntry(secretKey), + new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + } catch (KeyStoreException e) { + if (!isDeviceSecure(userId)) { + throw new InsecureUserException("Screenlock is not set"); + } else { + throw e; + } + } setGenerationId(userId, generationId); } @@ -477,4 +488,8 @@ public class PlatformKeyManager { return keyStore; } + private boolean isDeviceSecure(int userId) { + return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId); + } + } 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 24dbce49eace..0cfdaf26cc1c 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -19,6 +19,7 @@ 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; @@ -750,6 +751,8 @@ 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 { @@ -817,6 +820,8 @@ 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 { 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 c546a741e76a..c09e09c8404f 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 @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; import android.app.KeyguardManager; import android.content.Context; @@ -54,6 +55,7 @@ import org.mockito.MockitoAnnotations; import java.io.File; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.UnrecoverableKeyException; import java.util.List; @@ -393,6 +395,18 @@ public class PlatformKeyManagerTest { } @Test + public void getEncryptKey_noScreenlock() throws Exception { + when(mKeyguardManager.isDeviceSecure(USER_ID_FIXTURE)).thenReturn(false); + doThrow(new KeyStoreException()).when(mKeyStoreProxy).setEntry( + anyString(), + any(), + any()); + + assertThrows(InsecureUserException.class, + () -> mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE)); + } + + @Test public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( eq(DECRYPTION_KEY_ALIAS_1), |