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

Commit 4fdb5d00 authored by Eric Biggers's avatar Eric Biggers Committed by Android (Google) Code Review
Browse files

Merge "Clean up protector key terminology"

parents faea9ff3 995c7539
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -166,10 +166,6 @@ public class LockPatternUtils {
    private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
    private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";

    public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
    public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
    public static final String SYNTHETIC_PASSWORD_KEY_PREFIX = "synthetic_password_";

    public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
    public static final String PASSWORD_HISTORY_DELIMITER = ",";

+3 −2
Original line number Diff line number Diff line
@@ -35,8 +35,6 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
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.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -211,6 +209,9 @@ public class LockSettingsService extends ILockSettings.Stub {
    // user's credential must be presented again, e.g. via ConfirmLockPattern/ConfirmLockPassword.
    private static final int GK_PW_HANDLE_STORE_DURATION_MS = 10 * 60 * 1000; // 10 minutes

    private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
    private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";

    // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
    // Do not call into ActivityManager while holding mSpManager lock.
    private final Object mSeparateChallengeLock = new Object();
+27 −22
Original line number Diff line number Diff line
@@ -127,18 +127,20 @@ public class SyntheticPasswordCrypto {
    }

    /**
     * Decrypt a legacy SP blob which did the Keystore and software encryption layers in the wrong
     * Decrypts a legacy SP blob which did the Keystore and software encryption layers in the wrong
     * order.
     */
    public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] protectorSecret) {
    public static byte[] decryptBlobV1(String protectorKeyAlias, byte[] blob,
            byte[] protectorSecret) {
        try {
            KeyStore keyStore = getKeyStore();
            SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (keyStoreKey == null) {
                throw new IllegalStateException("SP key is missing: " + keyAlias);
            SecretKey protectorKey = (SecretKey) keyStore.getKey(protectorKeyAlias, null);
            if (protectorKey == null) {
                throw new IllegalStateException("SP protector key is missing: "
                        + protectorKeyAlias);
            }
            byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob);
            return decrypt(keyStoreKey, intermediate);
            return decrypt(protectorKey, intermediate);
        } catch (Exception e) {
            Slog.e(TAG, "Failed to decrypt V1 blob", e);
            throw new IllegalStateException("Failed to decrypt blob", e);
@@ -163,15 +165,17 @@ public class SyntheticPasswordCrypto {
    /**
     * Decrypts an SP blob that was created by {@link #createBlob}.
     */
    public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) {
    public static byte[] decryptBlob(String protectorKeyAlias, byte[] blob,
            byte[] protectorSecret) {
        try {
            final KeyStore keyStore = getKeyStore();

            SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (keyStoreKey == null) {
                throw new IllegalStateException("SP key is missing: " + keyAlias);
            SecretKey protectorKey = (SecretKey) keyStore.getKey(protectorKeyAlias, null);
            if (protectorKey == null) {
                throw new IllegalStateException("SP protector key is missing: "
                        + protectorKeyAlias);
            }
            byte[] intermediate = decrypt(keyStoreKey, blob);
            byte[] intermediate = decrypt(protectorKey, blob);
            return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate);
        } catch (CertificateException | IOException | BadPaddingException
                | IllegalBlockSizeException
@@ -185,20 +189,21 @@ public class SyntheticPasswordCrypto {

    /**
     * Creates a new SP blob by encrypting the given data.  Two encryption layers are applied: an
     * inner layer using a hash of protectorSecret as the key, and an outer layer using a new
     * Keystore key with the given alias and optionally bound to a SID.
     * inner layer using a hash of protectorSecret as the key, and an outer layer using the
     * protector key, which is a Keystore key that is optionally bound to a SID.  This method
     * creates the protector key and stores it under protectorKeyAlias.
     *
     * The reason we use a layer of software encryption, instead of using protectorSecret as the
     * applicationId of the Keystore key, is to work around buggy KeyMint implementations that don't
     * cryptographically bind the applicationId to the key.  The Keystore layer has to be the outer
     * layer, so that LSKF verification is ratelimited by Gatekeeper when Weaver is unavailable.
     */
    public static byte[] createBlob(String keyAlias, byte[] data, byte[] protectorSecret,
    public static byte[] createBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret,
            long sid) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
            keyGenerator.init(AES_GCM_KEY_SIZE * 8, new SecureRandom());
            SecretKey keyStoreKey = keyGenerator.generateKey();
            SecretKey protectorKey = keyGenerator.generateKey();
            final KeyStore keyStore = getKeyStore();
            KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -212,9 +217,9 @@ public class SyntheticPasswordCrypto {
            final KeyProtection protNonRollbackResistant = builder.build();
            builder.setRollbackResistant(true);
            final KeyProtection protRollbackResistant = builder.build();
            final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(keyStoreKey);
            final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(protectorKey);
            try {
                keyStore.setEntry(keyAlias, entry, protRollbackResistant);
                keyStore.setEntry(protectorKeyAlias, entry, protRollbackResistant);
                Slog.i(TAG, "Using rollback-resistant key");
            } catch (KeyStoreException e) {
                if (!(e.getCause() instanceof android.security.KeyStoreException)) {
@@ -226,11 +231,11 @@ public class SyntheticPasswordCrypto {
                }
                Slog.w(TAG, "Rollback-resistant keys unavailable.  Falling back to "
                        + "non-rollback-resistant key");
                keyStore.setEntry(keyAlias, entry, protNonRollbackResistant);
                keyStore.setEntry(protectorKeyAlias, entry, protNonRollbackResistant);
            }

            byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data);
            return encrypt(keyStoreKey, intermediate);
            return encrypt(protectorKey, intermediate);
        } catch (CertificateException | IOException | BadPaddingException
                | IllegalBlockSizeException
                | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
@@ -241,15 +246,15 @@ public class SyntheticPasswordCrypto {
        }
    }

    public static void destroyBlobKey(String keyAlias) {
    public static void destroyProtectorKey(String keyAlias) {
        KeyStore keyStore;
        try {
            keyStore = getKeyStore();
            keyStore.deleteEntry(keyAlias);
            Slog.i(TAG, "SP key deleted: " + keyAlias);
            Slog.i(TAG, "Deleted SP protector key " + keyAlias);
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
                | IOException e) {
            Slog.e(TAG, "Failed to destroy blob", e);
            Slog.e(TAG, "Failed to delete SP protector key " + keyAlias, e);
        }
    }

+24 −16
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ public class SyntheticPasswordManager {
    private static final byte PROTECTOR_TYPE_STRONG_TOKEN_BASED = 1;
    private static final byte PROTECTOR_TYPE_WEAK_TOKEN_BASED = 2;

    private static final String PROTECTOR_KEY_ALIAS_PREFIX = "synthetic_password_";

    // The security strength of the synthetic password, in bytes
    private static final int SYNTHETIC_PASSWORD_SECURITY_STRENGTH = 256 / 8;

@@ -583,7 +585,7 @@ public class SyntheticPasswordManager {
        for (long protectorId : mStorage.listSyntheticPasswordProtectorsForUser(SP_BLOB_NAME,
                    userId)) {
            destroyWeaverSlot(protectorId, userId);
            destroySPBlobKey(getKeyName(protectorId));
            destroyProtectorKey(getProtectorKeyAlias(protectorId));
        }
        // Remove potential persistent state (in RPMB), to prevent them from accumulating and
        // causing problems.
@@ -999,7 +1001,8 @@ public class SyntheticPasswordManager {
        } else {
            spSecret = sp.getSyntheticPassword();
        }
        byte[] content = createSPBlob(getKeyName(protectorId), spSecret, protectorSecret, sid);
        byte[] content = createSpBlob(getProtectorKeyAlias(protectorId), spSecret, protectorSecret,
                sid);
        /*
         * We can upgrade from v1 to v2 because that's just a change in the way that
         * the SP is stored. However, we can't upgrade to v3 because that is a change
@@ -1210,10 +1213,11 @@ public class SyntheticPasswordManager {
        }
        final byte[] spSecret;
        if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) {
            spSecret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(protectorId), blob.mContent,
                    protectorSecret);
            spSecret = SyntheticPasswordCrypto.decryptBlobV1(getProtectorKeyAlias(protectorId),
                    blob.mContent, protectorSecret);
        } else {
            spSecret = decryptSPBlob(getKeyName(protectorId), blob.mContent, protectorSecret);
            spSecret = decryptSpBlob(getProtectorKeyAlias(protectorId), blob.mContent,
                    protectorSecret);
        }
        if (spSecret == null) {
            Slog.e(TAG, "Fail to decrypt SP for user " + userId);
@@ -1337,7 +1341,7 @@ public class SyntheticPasswordManager {

    private void destroyProtectorCommon(long protectorId, int userId) {
        destroyState(SP_BLOB_NAME, protectorId, userId);
        destroySPBlobKey(getKeyName(protectorId));
        destroyProtectorKey(getProtectorKeyAlias(protectorId));
        destroyState(SECDISCARDABLE_NAME, protectorId, userId);
        if (hasState(WEAVER_SLOT_NAME, protectorId, userId)) {
            destroyWeaverSlot(protectorId, userId);
@@ -1422,17 +1426,20 @@ public class SyntheticPasswordManager {
        mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName);
    }

    protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) {
        return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, protectorSecret);
    @VisibleForTesting
    protected byte[] decryptSpBlob(String protectorKeyAlias, byte[] blob, byte[] protectorSecret) {
        return SyntheticPasswordCrypto.decryptBlob(protectorKeyAlias, blob, protectorSecret);
    }

    protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret,
    @VisibleForTesting
    protected byte[] createSpBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret,
            long sid) {
        return SyntheticPasswordCrypto.createBlob(blobKeyName, data, protectorSecret, sid);
        return SyntheticPasswordCrypto.createBlob(protectorKeyAlias, data, protectorSecret, sid);
    }

    protected void destroySPBlobKey(String keyAlias) {
        SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
    @VisibleForTesting
    protected void destroyProtectorKey(String keyAlias) {
        SyntheticPasswordCrypto.destroyProtectorKey(keyAlias);
    }

    public static long generateProtectorId() {
@@ -1457,8 +1464,8 @@ public class SyntheticPasswordManager {
        }
    }

    private String getKeyName(long protectorId) {
        return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, protectorId);
    private String getProtectorKeyAlias(long protectorId) {
        return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
    }

    private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
@@ -1511,7 +1518,7 @@ public class SyntheticPasswordManager {
    }

    /**
     * Migrate all existing SP keystore keys from uid 1000 app domain to LSS selinux domain
     * Migrates all existing SP protector keys from uid 1000 app domain to LSS selinux domain.
     */
    public boolean migrateKeyNamespace() {
        boolean success = true;
@@ -1519,7 +1526,8 @@ public class SyntheticPasswordManager {
            mStorage.listSyntheticPasswordProtectorsForAllUsers(SP_BLOB_NAME);
        for (List<Long> userProtectors : allProtectors.values()) {
            for (long protectorId : userProtectors) {
                success &= SyntheticPasswordCrypto.migrateLockSettingsKey(getKeyName(protectorId));
                success &= SyntheticPasswordCrypto.migrateLockSettingsKey(
                        getProtectorKeyAlias(protectorId));
            }
        }
        return success;
+9 −7
Original line number Diff line number Diff line
@@ -44,12 +44,14 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
        mGateKeeper = gatekeeper;
    }

    private ArrayMap<String, byte[]> mBlobs = new ArrayMap<>();
    private final ArrayMap<String, byte[]> mBlobs = new ArrayMap<>();

    @Override
    protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) {
        if (mBlobs.containsKey(blobKeyName) && !Arrays.equals(mBlobs.get(blobKeyName), blob)) {
            throw new AssertionFailedError("blobKeyName content is overwritten: " + blobKeyName);
    protected byte[] decryptSpBlob(String protectorKeyAlias, byte[] blob, byte[] protectorSecret) {
        if (mBlobs.containsKey(protectorKeyAlias) &&
                !Arrays.equals(mBlobs.get(protectorKeyAlias), blob)) {
            throw new AssertionFailedError("Blob was overwritten; protectorKeyAlias="
                    + protectorKeyAlias);
        }
        ByteBuffer buffer = ByteBuffer.allocate(blob.length);
        buffer.put(blob, 0, blob.length);
@@ -72,7 +74,7 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
    }

    @Override
    protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret,
    protected byte[] createSpBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret,
            long sid) {
        ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + data.length + Integer.BYTES
                + protectorSecret.length + Long.BYTES);
@@ -82,12 +84,12 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
        buffer.put(protectorSecret);
        buffer.putLong(sid);
        byte[] result = buffer.array();
        mBlobs.put(blobKeyName, result);
        mBlobs.put(protectorKeyAlias, result);
        return result;
    }

    @Override
    protected void destroySPBlobKey(String keyAlias) {
    protected void destroyProtectorKey(String keyAlias) {
    }

    @Override