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

Commit a758727c authored by Rubin Xu's avatar Rubin Xu
Browse files

Remove support of legacy credential hash

CDD started to require hardware-based lockscreen authentication
(gatekeeper) since Android N. Support of legacy credential hash
(lockscreen password hashed directly on device) is now totally
obsolete and can be removed safely, provided that there is no
need to support an OTA from M (or before) straight to R.

Bug: 63619579
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/locksettings
Change-Id: Ie71fec3eb0eaba8ec4c8e33e791a3d553d9355f9
parent b93dc48c
Loading
Loading
Loading
Loading
+1 −41
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    @IntDef({CHALLENGE_NONE,
            CHALLENGE_FROM_CALLER,
            CHALLENGE_INTERNAL})
    @interface ChallengeType {};
    @interface ChallengeType {}

    // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
    // Do not call into ActivityManager while holding mSpManager lock.
@@ -1937,46 +1937,6 @@ public class LockSettingsService extends ILockSettings.Stub {
        // of unlocking the user, so yell if calling from the main thread.
        StrictMode.noteDiskRead();

        if (storedHash.version == CredentialHash.VERSION_LEGACY) {
            final byte[] hash;
            if (storedHash.type == CREDENTIAL_TYPE_PATTERN) {
                hash = LockPatternUtils.patternToHash(
                        LockPatternUtils.byteArrayToPattern(credential));
            } else {
                hash = mLockPatternUtils.legacyPasswordToHash(credential, userId).getBytes();
            }
            if (Arrays.equals(hash, storedHash.hash)) {
                if (storedHash.type == CREDENTIAL_TYPE_PATTERN) {
                    unlockKeystore(LockPatternUtils.patternByteArrayToBaseZero(credential), userId);
                } else {
                    unlockKeystore(credential, userId);
                }
                // Users with legacy credentials don't have credential-backed
                // FBE keys, so just pass through a fake token/secret
                Slog.i(TAG, "Unlocking user with fake token: " + userId);
                final byte[] fakeToken = String.valueOf(userId).getBytes();
                unlockUser(userId, fakeToken, fakeToken);

                // migrate credential to GateKeeper
                setLockCredentialInternal(credential, storedHash.type, null,
                        storedHash.type == CREDENTIAL_TYPE_PATTERN
                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                                /* TODO(roosa): keep the same password quality */,
                        userId, false, /* isLockTiedToParent= */ false);
                if (challengeType == CHALLENGE_NONE) {
                    notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
                    // Use credentials to create recoverable keystore snapshot.
                    sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
                    return VerifyCredentialResponse.OK;
                }
                // Fall through to get the auth token. Technically this should never happen,
                // as a user that had a legacy credential would have to unlock their device
                // before getting to a flow with a challenge, but supporting for consistency.
            } else {
                return VerifyCredentialResponse.ERROR;
            }
        }
        GateKeeperResponse gateKeeperResponse = getGateKeeperService()
                .verifyChallenge(userId, challenge, storedHash.hash, credential);
        VerifyCredentialResponse response = convertResponse(gateKeeperResponse);
+21 −56
Original line number Diff line number Diff line
@@ -78,9 +78,7 @@ class LockSettingsStorage {
    private static final String SYSTEM_DIRECTORY = "/system/";
    private static final String LOCK_PATTERN_FILE = "gatekeeper.pattern.key";
    private static final String BASE_ZERO_LOCK_PATTERN_FILE = "gatekeeper.gesture.key";
    private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key";
    private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
    private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key";
    private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";

    private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
@@ -96,15 +94,15 @@ class LockSettingsStorage {

    @VisibleForTesting
    public static class CredentialHash {
        static final int VERSION_LEGACY = 0;
        static final int VERSION_GATEKEEPER = 1;
        /** Deprecated private static final int VERSION_LEGACY = 0; */
        private static final int VERSION_GATEKEEPER = 1;

        private CredentialHash(byte[] hash, @CredentialType int type, int version) {
            this(hash, type, version, false /* isBaseZeroPattern */);
        private CredentialHash(byte[] hash, @CredentialType int type) {
            this(hash, type, false /* isBaseZeroPattern */);
        }

        private CredentialHash(
                byte[] hash, @CredentialType int type, int version, boolean isBaseZeroPattern) {
                byte[] hash, @CredentialType int type, boolean isBaseZeroPattern) {
            if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
                if (hash == null) {
                    throw new RuntimeException("Empty hash for CredentialHash");
@@ -116,30 +114,27 @@ class LockSettingsStorage {
            }
            this.hash = hash;
            this.type = type;
            this.version = version;
            this.isBaseZeroPattern = isBaseZeroPattern;
        }

        private static CredentialHash createBaseZeroPattern(byte[] hash) {
            return new CredentialHash(hash, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
                    VERSION_GATEKEEPER, true /* isBaseZeroPattern */);
                    true /* isBaseZeroPattern */);
        }

        static CredentialHash create(byte[] hash, int type) {
            if (type == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
                throw new RuntimeException("Bad type for CredentialHash");
            }
            return new CredentialHash(hash, type, VERSION_GATEKEEPER);
            return new CredentialHash(hash, type);
        }

        static CredentialHash createEmptyHash() {
            return new CredentialHash(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
                    VERSION_GATEKEEPER);
            return new CredentialHash(null, LockPatternUtils.CREDENTIAL_TYPE_NONE);
        }

        byte[] hash;
        @CredentialType int type;
        int version;
        boolean isBaseZeroPattern;

        public byte[] toBytes() {
@@ -148,7 +143,7 @@ class LockSettingsStorage {
            try {
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(os);
                dos.write(version);
                dos.write(VERSION_GATEKEEPER);
                dos.write(type);
                if (hash != null && hash.length > 0) {
                    dos.writeInt(hash.length);
@@ -166,7 +161,7 @@ class LockSettingsStorage {
        public static CredentialHash fromBytes(byte[] bytes) {
            try {
                DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
                int version = is.read();
                /* int version = */ is.read();
                int type = is.read();
                int hashSize = is.readInt();
                byte[] hash = null;
@@ -174,7 +169,7 @@ class LockSettingsStorage {
                    hash = new byte[hashSize];
                    is.readFully(hash);
                }
                return new CredentialHash(hash, type, version);
                return new CredentialHash(hash, type);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
@@ -269,14 +264,7 @@ class LockSettingsStorage {
    private CredentialHash readPasswordHashIfExists(int userId) {
        byte[] stored = readFile(getLockPasswordFilename(userId));
        if (!ArrayUtils.isEmpty(stored)) {
            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
                    CredentialHash.VERSION_GATEKEEPER);
        }

        stored = readFile(getLegacyLockPasswordFilename(userId));
        if (!ArrayUtils.isEmpty(stored)) {
            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
                    CredentialHash.VERSION_LEGACY);
            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        }
        return null;
    }
@@ -284,8 +272,7 @@ class LockSettingsStorage {
    private CredentialHash readPatternHashIfExists(int userId) {
        byte[] stored = readFile(getLockPatternFilename(userId));
        if (!ArrayUtils.isEmpty(stored)) {
            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
                    CredentialHash.VERSION_GATEKEEPER);
            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
        }

        stored = readFile(getBaseZeroLockPatternFilename(userId));
@@ -293,30 +280,20 @@ class LockSettingsStorage {
            return CredentialHash.createBaseZeroPattern(stored);
        }

        stored = readFile(getLegacyLockPatternFilename(userId));
        if (!ArrayUtils.isEmpty(stored)) {
            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
                    CredentialHash.VERSION_LEGACY);
        }
        return null;
    }

    public CredentialHash readCredentialHash(int userId) {
        CredentialHash passwordHash = readPasswordHashIfExists(userId);
        CredentialHash patternHash = readPatternHashIfExists(userId);
        if (passwordHash != null && patternHash != null) {
            if (passwordHash.version == CredentialHash.VERSION_GATEKEEPER) {
        if (passwordHash != null) {
            return passwordHash;
            } else {
                return patternHash;
        }
        } else if (passwordHash != null) {
            return passwordHash;
        } else if (patternHash != null) {

        CredentialHash patternHash = readPatternHashIfExists(userId);
        if (patternHash != null) {
            return patternHash;
        } else {
            return CredentialHash.createEmptyHash();
        }
        return CredentialHash.createEmptyHash();
    }

    public void removeChildProfileLock(int userId) {
@@ -342,14 +319,12 @@ class LockSettingsStorage {
    }

    public boolean hasPassword(int userId) {
        return hasFile(getLockPasswordFilename(userId)) ||
            hasFile(getLegacyLockPasswordFilename(userId));
        return hasFile(getLockPasswordFilename(userId));
    }

    public boolean hasPattern(int userId) {
        return hasFile(getLockPatternFilename(userId)) ||
            hasFile(getBaseZeroLockPatternFilename(userId)) ||
            hasFile(getLegacyLockPatternFilename(userId));
            hasFile(getBaseZeroLockPatternFilename(userId));
    }

    public boolean hasCredential(int userId) {
@@ -469,16 +444,6 @@ class LockSettingsStorage {
        return getLockCredentialFilePathForUser(userId, LOCK_PASSWORD_FILE);
    }

    @VisibleForTesting
    String getLegacyLockPatternFilename(int userId) {
        return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PATTERN_FILE);
    }

    @VisibleForTesting
    String getLegacyLockPasswordFilename(int userId) {
        return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PASSWORD_FILE);
    }

    private String getBaseZeroLockPatternFilename(int userId) {
        return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE);
    }
+0 −4
Original line number Diff line number Diff line
@@ -311,8 +311,6 @@ public class LockSettingsStorageTests extends AndroidTestCase {
    public void testFileLocation_Owner() {
        LockSettingsStorage storage = new LockSettingsStorage(getContext());

        assertEquals("/data/system/gesture.key", storage.getLegacyLockPatternFilename(0));
        assertEquals("/data/system/password.key", storage.getLegacyLockPasswordFilename(0));
        assertEquals("/data/system/gatekeeper.pattern.key", storage.getLockPatternFilename(0));
        assertEquals("/data/system/gatekeeper.password.key", storage.getLockPasswordFilename(0));
    }
@@ -436,7 +434,6 @@ public class LockSettingsStorageTests extends AndroidTestCase {
                PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes();
        CredentialHash deserialized = CredentialHash.fromBytes(serialized);

        assertEquals(CredentialHash.VERSION_GATEKEEPER, deserialized.version);
        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
        assertArrayEquals(PAYLOAD, deserialized.hash);
        assertFalse(deserialized.isBaseZeroPattern);
@@ -453,7 +450,6 @@ public class LockSettingsStorageTests extends AndroidTestCase {
        };
        CredentialHash deserialized = CredentialHash.fromBytes(serialized);

        assertEquals(CredentialHash.VERSION_GATEKEEPER, deserialized.version);
        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
        assertArrayEquals(PAYLOAD, deserialized.hash);
        assertFalse(deserialized.isBaseZeroPattern);