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

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

Merge changes I4a6c5bae,If47d54f2,I7083e1b8,Ia2165338,Ic2b0b220, ... into main

* changes:
  Zeroize intermediate secrets in unlockLskfBasedProtector()
  Zeroize intermediate key in transformUnderWeaverSecret()
  Zeroize intermediate key in stretchedLskfToWeaverKey()
  Zeroize password metrics key in savePasswordMetrics()
  Zeroize CE storage key in setCeStorageProtection()
  Zeroize keystore password in unlockKeystore()
parents 916220dd a4a10406
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -1514,7 +1514,12 @@ public class LockSettingsService extends ILockSettings.Stub {
    }

    private void unlockKeystore(int userId, SyntheticPassword sp) {
        mKeyStoreAuthorization.onDeviceUnlocked(userId, sp.deriveKeyStorePassword());
        final byte[] password = sp.deriveKeyStorePassword();
        try {
            mKeyStoreAuthorization.onDeviceUnlocked(userId, password);
        } finally {
            ArrayUtils.zeroize(password);
        }
    }

    @VisibleForTesting /** Note: this method is overridden in unit tests */
@@ -2193,6 +2198,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            throw new IllegalStateException("Failed to protect CE key for user " + userId, e);
        } finally {
            Binder.restoreCallingIdentity(callingId);
            ArrayUtils.zeroize(secret);
        }
    }

+127 −101
Original line number Diff line number Diff line
@@ -1409,9 +1409,12 @@ class SyntheticPasswordManager {
            return result;
        }

        byte[] stretchedLskf = stretchLskf(credential, pwd);

        final byte[] protectorSecret;
        byte[] stretchedLskf = null;
        byte[] weaverKey = null;
        byte[] gkPassword = null;
        byte[] protectorSecret = null;
        try {
            stretchedLskf = stretchLskf(credential, pwd);
            long sid = GateKeeper.INVALID_SECURE_USER_ID;
            int weaverSlot = loadWeaverSlot(protectorId, userId);
            if (weaverSlot != INVALID_WEAVER_SLOT) {
@@ -1423,16 +1426,17 @@ class SyntheticPasswordManager {
                    result.gkResponse = VerifyCredentialResponse.ERROR;
                    return result;
                }
            result.gkResponse = weaverVerify(weaver, weaverSlot,
                    stretchedLskfToWeaverKey(stretchedLskf));
                weaverKey = stretchedLskfToWeaverKey(stretchedLskf);
                result.gkResponse = weaverVerify(weaver, weaverSlot, weaverKey);
                if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
                    return result;
                }
                protectorSecret = transformUnderWeaverSecret(stretchedLskf,
                        result.gkResponse.getGatekeeperHAT());
            } else {
            // Weaver is unavailable, so the protector uses Gatekeeper to verify the LSKF, unless
            // the LSKF is empty in which case Gatekeeper might not have been used at all.
                // Weaver is unavailable, so the protector uses Gatekeeper to verify the LSKF,
                // unless the LSKF is empty in which case Gatekeeper might not have been used at
                // all.
                if (pwd == null || pwd.passwordHandle == null) {
                    if (!credential.isNone()) {
                        Slog.e(TAG, "Missing Gatekeeper password handle for nonempty LSKF");
@@ -1440,7 +1444,7 @@ class SyntheticPasswordManager {
                        return result;
                    }
                } else {
                byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
                    gkPassword = stretchedLskfToGkPassword(stretchedLskf);
                    GateKeeperResponse response;
                    try {
                        response = gatekeeper.verifyChallenge(fakeUserId(userId), 0L,
@@ -1463,7 +1467,8 @@ class SyntheticPasswordManager {
                                reenrollResponse = GateKeeperResponse.ERROR;
                                // continue the flow anyway
                            }
                        if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                            if (reenrollResponse.getResponseCode()
                                    == GateKeeperResponse.RESPONSE_OK) {
                                pwd.passwordHandle = reenrollResponse.getPayload();
                                // Use the reenrollment opportunity to update credential type
                                // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
@@ -1477,7 +1482,8 @@ class SyntheticPasswordManager {
                            }
                        }
                    } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
                    result.gkResponse = VerifyCredentialResponse.fromTimeout(response.getTimeout());
                        result.gkResponse = VerifyCredentialResponse.fromTimeout(
                                response.getTimeout());
                        return result;
                    } else  {
                        result.gkResponse = VerifyCredentialResponse.ERROR;
@@ -1493,8 +1499,8 @@ class SyntheticPasswordManager {
                }
                protectorSecret = transformUnderSecdiscardable(stretchedLskf, secdiscardable);
            }
        // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
        // Notify the callback so the keyguard UI can proceed immediately.
            // Supplied credential passes first stage weaver/gatekeeper check so it should be
            // correct.  Notify the callback so the keyguard UI can proceed immediately.
            if (progressCallback != null) {
                try {
                    progressCallback.onCredentialVerified();
@@ -1508,12 +1514,19 @@ class SyntheticPasswordManager {
            // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
            result.gkResponse = verifyChallenge(gatekeeper, result.syntheticPassword, 0L, userId);

        // Upgrade case: store the metrics if the device did not have stored metrics before, should
        // only happen once on old protectors.
            // Upgrade case: store the metrics if the device did not have stored metrics before,
            // should only happen once on old protectors.
            if (result.syntheticPassword != null && !credential.isNone()
                    && !hasPasswordMetrics(protectorId, userId)) {
                savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId);
            syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe.
                // Not strictly needed as the upgrade can be re-done, but be safe.
                syncState(userId);
            }
        } finally {
            ArrayUtils.zeroize(stretchedLskf);
            ArrayUtils.zeroize(weaverKey);
            ArrayUtils.zeroize(gkPassword);
            ArrayUtils.zeroize(protectorSecret);
        }
        return result;
    }
@@ -1789,9 +1802,13 @@ class SyntheticPasswordManager {
    }

    private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
        byte[] weaverSecret = SyntheticPasswordCrypto.personalizedHash(
        final byte[] weaverSecret = SyntheticPasswordCrypto.personalizedHash(
                PERSONALIZATION_WEAVER_PASSWORD, secret);
        try {
            return ArrayUtils.concat(data, weaverSecret);
        } finally {
            ArrayUtils.zeroize(weaverSecret);
        }
    }

    private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
@@ -1865,10 +1882,15 @@ class SyntheticPasswordManager {
     */
    private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp,
            long protectorId, int userId) {
        final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(),
        final byte[] metricsKey = sp.deriveMetricsKey();
        try {
            final byte[] encrypted = SyntheticPasswordCrypto.encrypt(metricsKey,
                    /* personalization= */ new byte[0],
                    new VersionedPasswordMetrics(credential).serialize());
            saveState(PASSWORD_METRICS_NAME, encrypted, protectorId, userId);
        } finally {
            ArrayUtils.zeroize(metricsKey);
        }
    }

    @VisibleForTesting
@@ -1967,12 +1989,16 @@ class SyntheticPasswordManager {
    }

    private byte[] stretchedLskfToWeaverKey(byte[] stretchedLskf) {
        byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY,
        final byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY,
                stretchedLskf);
        try {
            if (key.length < mWeaverConfig.keySize) {
                throw new IllegalArgumentException("weaver key length too small");
            }
            return Arrays.copyOf(key, mWeaverConfig.keySize);
        } finally {
            ArrayUtils.zeroize(key);
        }
    }

    @VisibleForTesting
+3 −1
Original line number Diff line number Diff line
@@ -20,13 +20,15 @@ import static com.google.common.truth.Truth.assertThat;

import android.util.ArrayMap;

import java.util.Arrays;

public class FakeStorageManager {

    private final ArrayMap<Integer, byte[]> mUserSecrets = new ArrayMap<>();

    public void setCeStorageProtection(int userId, byte[] secret) {
        assertThat(mUserSecrets).doesNotContainKey(userId);
        mUserSecrets.put(userId, secret);
        mUserSecrets.put(userId, Arrays.copyOf(secret, secret.length));
    }

    public byte[] getUserUnlockToken(int userId) {