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

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

Merge "Automatically fix bad FRP block written by Android 14 Beta 2" into udc-dev

parents 68aff1a3 f29cab97
Loading
Loading
Loading
Loading
+17 −8
Original line number Diff line number Diff line
@@ -889,22 +889,31 @@ public class LockSettingsService extends ILockSettings.Stub {

    }

    private void migrateOldDataAfterSystemReady() {
        // Migrate the FRP credential to the persistent data block
    @VisibleForTesting
    void migrateOldDataAfterSystemReady() {
        // Write the FRP persistent data block if needed.
        //
        // The original purpose of this code was to write the FRP block for the first time, when
        // upgrading from Android 8.1 or earlier which didn't use the FRP block.  This code has
        // since been repurposed to also fix the "bad" (non-forwards-compatible) FRP block written
        // by Android 14 Beta 2.  For this reason, the database key used here has been renamed from
        // "migrated_frp" to "migrated_frp2" to cause migrateFrpCredential() to run again on devices
        // where it had run before.
        if (LockPatternUtils.frpCredentialEnabled(mContext)
                && !getBoolean("migrated_frp", false, 0)) {
                && !getBoolean("migrated_frp2", false, 0)) {
            migrateFrpCredential();
            setBoolean("migrated_frp", true, 0);
            setBoolean("migrated_frp2", true, 0);
        }
    }

    /**
     * Migrate the credential for the FRP credential owner user if the following are satisfied:
     * - the user has a secure credential
     * - the FRP credential is not set up
     * Write the FRP persistent data block if the following are satisfied:
     * - the user who owns the FRP credential has a nonempty credential
     * - the FRP persistent data block doesn't exist or uses the "bad" format from Android 14 Beta 2
     */
    private void migrateFrpCredential() {
        if (mStorage.readPersistentDataBlock() != PersistentData.NONE) {
        PersistentData data = mStorage.readPersistentDataBlock();
        if (data != PersistentData.NONE && !data.isBadFormatFromAndroid14Beta()) {
            return;
        }
        for (UserInfo userInfo : mUserManager.getUsers()) {
+5 −0
Original line number Diff line number Diff line
@@ -606,6 +606,11 @@ class LockSettingsStorage {
            this.payload = payload;
        }

        public boolean isBadFormatFromAndroid14Beta() {
            return (this.type == TYPE_SP_GATEKEEPER || this.type == TYPE_SP_WEAVER)
                && SyntheticPasswordManager.PasswordData.isBadFormatFromAndroid14Beta(this.payload);
        }

        public static PersistentData fromBytes(byte[] frpData) {
            if (frpData == null || frpData.length == 0) {
                return NONE;
+9 −0
Original line number Diff line number Diff line
@@ -370,6 +370,15 @@ class SyntheticPasswordManager {
            return result;
        }

        /**
         * Returns true if the given serialized PasswordData begins with the value 2 as a short.
         * This detects the "bad" (non-forwards-compatible) PasswordData format that was temporarily
         * used during development of Android 14.  For more details, see fromBytes() below.
         */
        public static boolean isBadFormatFromAndroid14Beta(byte[] data) {
            return data != null && data.length >= 2 && data[0] == 0 && data[1] == 2;
        }

        public static PasswordData fromBytes(byte[] data) {
            PasswordData result = new PasswordData();
            ByteBuffer buffer = ByteBuffer.allocate(data.length);
+68 −1
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
@@ -38,8 +40,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.nio.ByteBuffer;

/** Test setting a lockscreen credential and then verify it under USER_FRP */
/** Tests that involve the Factory Reset Protection (FRP) credential. */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -148,4 +151,68 @@ public class LockscreenFrpTest extends BaseLockSettingsServiceTests {
                mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */)
                        .getResponseCode());
    }

    // The FRP block that gets written by the current version of Android must still be accepted by
    // old versions of Android.  This test tries to detect non-forward-compatible changes in
    // PasswordData#toBytes(), which would break that.
    @Test
    public void testFrpBlock_isForwardsCompatible() {
        mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
        PersistentData data = mStorage.readPersistentDataBlock();
        ByteBuffer buffer = ByteBuffer.wrap(data.payload);

        final int credentialType = buffer.getInt();
        assertEquals(CREDENTIAL_TYPE_PIN, credentialType);

        final byte scryptLogN = buffer.get();
        assertTrue(scryptLogN >= 0);

        final byte scryptLogR = buffer.get();
        assertTrue(scryptLogR >= 0);

        final byte scryptLogP = buffer.get();
        assertTrue(scryptLogP >= 0);

        final int saltLength = buffer.getInt();
        assertTrue(saltLength > 0);
        final byte[] salt = new byte[saltLength];
        buffer.get(salt);

        final int passwordHandleLength = buffer.getInt();
        assertTrue(passwordHandleLength > 0);
        final byte[] passwordHandle = new byte[passwordHandleLength];
        buffer.get(passwordHandle);
    }

    @Test
    public void testFrpBlock_inBadAndroid14FormatIsAutomaticallyFixed() {
        mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);

        // Write a "bad" FRP block with PasswordData beginning with the bytes [0, 2].
        byte[] badPasswordData = new byte[] {
                0, 2, /* version 2 */
                0, 3, /* CREDENTIAL_TYPE_PIN */
                11, /* scryptLogN */
                22, /* scryptLogR */
                33, /* scryptLogP */
                0, 0, 0, 5, /* salt.length */
                1, 2, -1, -2, 55, /* salt */
                0, 0, 0, 6, /* passwordHandle.length */
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                0, 0, 0, 6, /* pinLength */
        };
        mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_GATEKEEPER, PRIMARY_USER_ID, 0,
                badPasswordData);

        // Execute the code that should fix the FRP block.
        assertFalse(mStorage.getBoolean("migrated_frp2", false, 0));
        mService.migrateOldDataAfterSystemReady();
        assertTrue(mStorage.getBoolean("migrated_frp2", false, 0));

        // Verify that the FRP block has been fixed.
        PersistentData data = mStorage.readPersistentDataBlock();
        assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
        ByteBuffer buffer = ByteBuffer.wrap(data.payload);
        assertEquals(CREDENTIAL_TYPE_PIN, buffer.getInt());
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -638,6 +638,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                0, 0, 0, 6, /* pinLength */
        };
        assertFalse(PasswordData.isBadFormatFromAndroid14Beta(serialized));
        PasswordData deserialized = PasswordData.fromBytes(serialized);

        assertEquals(11, deserialized.scryptLogN);
@@ -690,6 +691,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                0, 0, 0, 6, /* pinLength */
        };
        assertTrue(PasswordData.isBadFormatFromAndroid14Beta(serialized));
        PasswordData deserialized = PasswordData.fromBytes(serialized);

        assertEquals(11, deserialized.scryptLogN);