Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e8f4f724 authored by Dmitry Dementyev's avatar Dmitry Dementyev Committed by Android (Google) Code Review
Browse files

Merge "Use new params for platform key protection."

parents a2fbe126 b5b22b19
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -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);
            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);
            // 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;
        }

+21 −29
Original line number Diff line number Diff line
@@ -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.
+16 −14
Original line number Diff line number Diff line
@@ -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) {
+114 −114

File changed.

Preview size limit exceeded, changes collapsed.

+5 −4
Original line number Diff line number Diff line
@@ -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(