Skip to content
Snippets Groups Projects
Commit 26f7222e authored by Dmitry Dementyev's avatar Dmitry Dementyev
Browse files

Throw InsecureUserException when LSKF is not set in recoverablekeystore.

Without fix RecoveryController.generateKey and importKey throw generic ServiceSpecificException.

Bug: 283534188
Test: atest com.android.server.locksettings.recoverablekeystore
Change-Id: I8604d3c771e37ca3322d3301037b7443d0a3928b
parent 3a317d93
No related branches found
No related tags found
No related merge requests found
......@@ -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);
}
}
......@@ -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 {
......
......@@ -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;
......@@ -392,6 +394,18 @@ public class PlatformKeyManagerTest {
any());
}
@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(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment