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

Commit fc7a26cd authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "LockSettingsService: migrate to its own keystore namespace" am: b8e54262

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1675900

Change-Id: Ibc8bdbcf832b90e3a5628829a6206374c9504374
parents bce6d733 b8e54262
Loading
Loading
Loading
Loading
+79 −29
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_DECRYPT;
import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
@@ -99,6 +101,7 @@ import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.security.keystore2.AndroidKeyStoreProvider;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
@@ -153,6 +156,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -225,6 +229,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    private final SyntheticPasswordManager mSpManager;

    private final KeyStore mKeyStore;
    private final java.security.KeyStore mJavaKeyStore;
    private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
    private ManagedProfilePasswordCache mManagedProfilePasswordCache;

@@ -543,16 +548,22 @@ public class LockSettingsService extends ILockSettings.Stub {
            return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId);
        }

        public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache() {
        public java.security.KeyStore getJavaKeyStore() {
            try {
                java.security.KeyStore ks = java.security.KeyStore.getInstance(
                        SyntheticPasswordCrypto.androidKeystoreProviderName());
                ks.load(null);
                return new ManagedProfilePasswordCache(ks, getUserManager());
                ks.load(new AndroidKeyStoreLoadStoreParameter(
                        SyntheticPasswordCrypto.keyNamespace()));
                return ks;
            } catch (Exception e) {
                throw new IllegalStateException("Cannot load keystore", e);
            }
        }

        public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache(
                java.security.KeyStore ks) {
            return new ManagedProfilePasswordCache(ks, getUserManager());
        }
    }

    public LockSettingsService(Context context) {
@@ -564,6 +575,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        mInjector = injector;
        mContext = injector.getContext();
        mKeyStore = injector.getKeyStore();
        mJavaKeyStore = injector.getJavaKeyStore();
        mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
        mHandler = injector.getHandler(injector.getServiceThread());
        mStrongAuth = injector.getStrongAuth();
@@ -586,7 +598,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        mStrongAuthTracker.register(mStrongAuth);

        mSpManager = injector.getSyntheticPasswordManager(mStorage);
        mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
        mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);

        mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
                mStorage);
@@ -959,6 +971,21 @@ public class LockSettingsService extends ILockSettings.Stub {
            setString("migrated_wear_lockscreen_disabled", "true", 0);
            Slog.i(TAG, "Migrated lockscreen_disabled for Wear devices");
        }

        if (getString("migrated_keystore_namespace", null, 0) == null) {
            boolean success = true;
            synchronized (mSpManager) {
                success &= mSpManager.migrateKeyNamespace();
            }
            success &= migrateProfileLockKeys();
            if (success) {
                setString("migrated_keystore_namespace", "true", 0);
                Slog.i(TAG, "Migrated keys to LSS namespace");
            } else {
                Slog.w(TAG, "Failed to migrate keys to LSS namespace");
            }
        }

    }

    private void migrateOldDataAfterSystemReady() {
@@ -999,6 +1026,22 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
    }

    private boolean migrateProfileLockKeys() {
        boolean success = true;
        final List<UserInfo> users = mUserManager.getUsers();
        final int userCount = users.size();
        for (int i = 0; i < userCount; i++) {
            UserInfo user = users.get(i);
            if (user.isManagedProfile() && !getSeparateProfileChallengeEnabledInternal(user.id)) {
                success &= SyntheticPasswordCrypto.migrateLockSettingsKey(
                        PROFILE_KEY_NAME_ENCRYPT + user.id);
                success &= SyntheticPasswordCrypto.migrateLockSettingsKey(
                        PROFILE_KEY_NAME_DECRYPT + user.id);
            }
        }
        return success;
    }

    /**
     * Returns the lowest password quality that still presents the same UI for entering it.
     *
@@ -1284,11 +1327,8 @@ public class LockSettingsService extends ILockSettings.Stub {
        byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
                storedData.length);
        byte[] decryptionResult;
        java.security.KeyStore keyStore = java.security.KeyStore.getInstance(
                SyntheticPasswordCrypto.androidKeystoreProviderName());
        keyStore.load(null);
        SecretKey decryptionKey = (SecretKey) keyStore.getKey(
                LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, null);
        SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey(
                PROFILE_KEY_NAME_DECRYPT + userId, null);

        Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
@@ -1744,30 +1784,26 @@ public class LockSettingsService extends ILockSettings.Stub {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
            keyGenerator.init(new SecureRandom());
            SecretKey secretKey = keyGenerator.generateKey();
            java.security.KeyStore keyStore = java.security.KeyStore.getInstance(
                    SyntheticPasswordCrypto.androidKeystoreProviderName());
            keyStore.load(null);
            try {
                keyStore.setEntry(
                        LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId,
                mJavaKeyStore.setEntry(
                        PROFILE_KEY_NAME_ENCRYPT + userId,
                        new java.security.KeyStore.SecretKeyEntry(secretKey),
                        new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
                                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                                .build());
                keyStore.setEntry(
                        LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId,
                mJavaKeyStore.setEntry(
                        PROFILE_KEY_NAME_DECRYPT + userId,
                        new java.security.KeyStore.SecretKeyEntry(secretKey),
                        new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                                .setUserAuthenticationRequired(true)
                                .setUserAuthenticationValidityDurationSeconds(30)
                                .setCriticalToDeviceEncryption(true)
                                .build());
                // Key imported, obtain a reference to it.
                SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
                        LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null);
                SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
                        PROFILE_KEY_NAME_ENCRYPT + userId, null);
                Cipher cipher = Cipher.getInstance(
                        KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
                                + KeyProperties.ENCRYPTION_PADDING_NONE);
@@ -1776,10 +1812,10 @@ public class LockSettingsService extends ILockSettings.Stub {
                iv = cipher.getIV();
            } finally {
                // The original key can now be discarded.
                keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId);
                mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + userId);
            }
        } catch (CertificateException | UnrecoverableKeyException
                | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
        } catch (UnrecoverableKeyException
                | BadPaddingException | IllegalBlockSizeException | KeyStoreException
                | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
            throw new IllegalStateException("Failed to encrypt key", e);
        }
@@ -2300,13 +2336,9 @@ public class LockSettingsService extends ILockSettings.Stub {
    private void removeKeystoreProfileKey(int targetUserId) {
        Slog.i(TAG, "Remove keystore profile key for user: " + targetUserId);
        try {
            java.security.KeyStore keyStore = java.security.KeyStore.getInstance(
                    SyntheticPasswordCrypto.androidKeystoreProviderName());
            keyStore.load(null);
            keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + targetUserId);
            keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + targetUserId);
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
                | IOException e) {
            mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
            mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
        } catch (KeyStoreException e) {
            // We have tried our best to remove all keys
            Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
        }
@@ -3257,6 +3289,12 @@ public class LockSettingsService extends ILockSettings.Stub {
        pw.println();
        pw.decreaseIndent();

        pw.println("Keys in namespace:");
        pw.increaseIndent();
        dumpKeystoreKeys(pw);
        pw.println();
        pw.decreaseIndent();

        pw.println("Storage:");
        pw.increaseIndent();
        mStorage.dump(pw);
@@ -3276,6 +3314,18 @@ public class LockSettingsService extends ILockSettings.Stub {
        pw.decreaseIndent();
    }

    private void dumpKeystoreKeys(IndentingPrintWriter pw) {
        try {
            final Enumeration<String> aliases = mJavaKeyStore.aliases();
            while (aliases.hasMoreElements()) {
                pw.println(aliases.nextElement());
            }
        } catch (KeyStoreException e) {
            pw.println("Unable to get keys: " + e.toString());
            Slog.d(TAG, "Dump error", e);
        }
    }

    /**
     * Cryptographically disable escrow token support for the current user, if the user is not
     * managed (either user has a profile owner, or if device is managed). Do not disable
+2 −2
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.os.UserManager;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.security.keystore2.AndroidKeyStoreSpi;
import android.util.Slog;
import android.util.SparseArray;

@@ -95,11 +94,12 @@ public class ManagedProfilePasswordCache {
            SecretKey key;
            try {
                generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
                        AndroidKeyStoreSpi.NAME);
                        mKeyStore.getProvider());
                generator.init(new KeyGenParameterSpec.Builder(
                        keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                        .setKeySize(KEY_LENGTH)
                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                        .setNamespace(SyntheticPasswordCrypto.keyNamespace())
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                        // Generate auth-bound key to user 0 (since we the caller is user 0)
                        .setUserAuthenticationRequired(true)
+47 −9
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package com.android.server.locksettings;

import android.security.AndroidKeyStoreMaintenance;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import android.util.Slog;

import java.io.ByteArrayOutputStream;
@@ -125,9 +129,7 @@ public class SyntheticPasswordCrypto {

    public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
        try {
            KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
            keyStore.load(null);

            KeyStore keyStore = getKeyStore();
            SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (decryptionKey == null) {
                throw new IllegalStateException("SP key is missing: " + keyAlias);
@@ -144,10 +146,20 @@ public class SyntheticPasswordCrypto {
        return "AndroidKeyStore";
    }

    static int keyNamespace() {
        return KeyProperties.NAMESPACE_LOCKSETTINGS;
    }

    private static KeyStore getKeyStore()
            throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
        KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
        keyStore.load(new AndroidKeyStoreLoadStoreParameter(keyNamespace()));
        return keyStore;
    }

    public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
        try {
            KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
            keyStore.load(null);
            final KeyStore keyStore = getKeyStore();

            SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (decryptionKey == null) {
@@ -170,8 +182,7 @@ public class SyntheticPasswordCrypto {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
            keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom());
            SecretKey secretKey = keyGenerator.generateKey();
            KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
            keyStore.load(null);
            final KeyStore keyStore = getKeyStore();
            KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
@@ -200,8 +211,7 @@ public class SyntheticPasswordCrypto {
    public static void destroyBlobKey(String keyAlias) {
        KeyStore keyStore;
        try {
            keyStore = KeyStore.getInstance(androidKeystoreProviderName());
            keyStore.load(null);
            keyStore = getKeyStore();
            keyStore.deleteEntry(keyAlias);
            Slog.i(TAG, "SP key deleted: " + keyAlias);
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
@@ -229,4 +239,32 @@ public class SyntheticPasswordCrypto {
            throw new IllegalStateException("NoSuchAlgorithmException for SHA-512", e);
        }
    }

    static boolean migrateLockSettingsKey(String alias) {
        final KeyDescriptor legacyKey = new KeyDescriptor();
        legacyKey.domain = Domain.APP;
        legacyKey.nspace = KeyProperties.NAMESPACE_APPLICATION;
        legacyKey.alias = alias;

        final KeyDescriptor newKey = new KeyDescriptor();
        newKey.domain = Domain.SELINUX;
        newKey.nspace = SyntheticPasswordCrypto.keyNamespace();
        newKey.alias = alias;
        Slog.i(TAG, "Migrating key " + alias);
        int err = AndroidKeyStoreMaintenance.migrateKeyNamespace(legacyKey, newKey);
        if (err == 0) {
            return true;
        } else if (err == AndroidKeyStoreMaintenance.KEY_NOT_FOUND) {
            Slog.i(TAG, "Key does not exist");
            // Treat this as a success so we don't migrate again.
            return true;
        } else if (err == AndroidKeyStoreMaintenance.INVALID_ARGUMENT) {
            Slog.i(TAG, "Key already exists");
            // Treat this as a success so we don't migrate again.
            return true;
        } else {
            Slog.e(TAG, String.format("Failed to migrate key: %d", err));
            return false;
        }
    }
}
+21 −6
Original line number Diff line number Diff line
@@ -519,7 +519,7 @@ public class SyntheticPasswordManager {
    public void removeUser(int userId) {
        for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
            destroyWeaverSlot(handle, userId);
            destroySPBlobKey(getHandleName(handle));
            destroySPBlobKey(getKeyName(handle));
        }
    }

@@ -955,7 +955,7 @@ public class SyntheticPasswordManager {
        } else {
            secret = authToken.getSyntheticPassword();
        }
        byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
        byte[] content = createSPBlob(getKeyName(handle), secret, applicationId, sid);
        byte[] blob = new byte[content.length + 1 + 1];
        /*
         * We can upgrade from v1 to v2 because that's just a change in the way that
@@ -1137,10 +1137,10 @@ public class SyntheticPasswordManager {
        }
        final byte[] secret;
        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
            secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
            secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle),
                    Arrays.copyOfRange(blob, 2, blob.length), applicationId);
        } else {
            secret = decryptSPBlob(getHandleName(handle),
            secret = decryptSPBlob(getKeyName(handle),
                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
        }
        if (secret == null) {
@@ -1235,7 +1235,7 @@ public class SyntheticPasswordManager {

    private void destroySyntheticPassword(long handle, int userId) {
        destroyState(SP_BLOB_NAME, handle, userId);
        destroySPBlobKey(getHandleName(handle));
        destroySPBlobKey(getKeyName(handle));
        if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
            destroyWeaverSlot(handle, userId);
        }
@@ -1351,7 +1351,7 @@ public class SyntheticPasswordManager {
        }
    }

    private String getHandleName(long handle) {
    private String getKeyName(long handle) {
        return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
    }

@@ -1412,4 +1412,19 @@ public class SyntheticPasswordManager {
        }
        return hexBytes;
    }

    /**
     * Migrate all existing SP keystore keys from uid 1000 app domain to LSS selinux domain
     */
    public boolean migrateKeyNamespace() {
        boolean success = true;
        final Map<Integer, List<Long>> allHandles =
                mStorage.listSyntheticPasswordHandlesForAllUsers(SP_BLOB_NAME);
        for (List<Long> userHandles : allHandles.values()) {
            for (long handle : userHandles) {
                success &= SyntheticPasswordCrypto.migrateLockSettingsKey(getKeyName(handle));
            }
        }
        return success;
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -155,7 +155,8 @@ public class LockSettingsServiceTestable extends LockSettingsService {
        }

        @Override
        public ManagedProfilePasswordCache getManagedProfilePasswordCache() {
        public ManagedProfilePasswordCache getManagedProfilePasswordCache(
                java.security.KeyStore ks) {
            return mock(ManagedProfilePasswordCache.class);
        }