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

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

Merge "Fix downgrades on device with FRP enabled" into udc-dev

parents 137c193d 929af52c
Loading
Loading
Loading
Loading
+25 −22
Original line number Original line Diff line number Diff line
@@ -152,9 +152,6 @@ class SyntheticPasswordManager {
    // The security strength of the synthetic password, in bytes
    // The security strength of the synthetic password, in bytes
    private static final int SYNTHETIC_PASSWORD_SECURITY_STRENGTH = 256 / 8;
    private static final int SYNTHETIC_PASSWORD_SECURITY_STRENGTH = 256 / 8;


    public static final short PASSWORD_DATA_V1 = 1;
    public static final short PASSWORD_DATA_V2 = 2;

    private static final int PASSWORD_SCRYPT_LOG_N = 11;
    private static final int PASSWORD_SCRYPT_LOG_N = 11;
    private static final int PASSWORD_SCRYPT_LOG_R = 3;
    private static final int PASSWORD_SCRYPT_LOG_R = 3;
    private static final int PASSWORD_SCRYPT_LOG_P = 1;
    private static final int PASSWORD_SCRYPT_LOG_P = 1;
@@ -380,20 +377,17 @@ class SyntheticPasswordManager {
            buffer.flip();
            buffer.flip();


            /*
            /*
           * Originally this file did not contain a version number. However, its first field was
             * The serialized PasswordData is supposed to begin with credentialType as an int.
           * 'credentialType' as an 'int'. Since 'credentialType' could only be in the range
             * However, all credentialType values fit in a short and the byte order is big endian,
           * [-1, 4] and this file uses big endian byte order, the first two bytes were redundant,
             * so the first two bytes don't convey any non-redundant information.  For this reason,
           * and when interpreted as a 'short' could only contain -1 or 0. Therefore, we've now
             * temporarily during development of Android 14, the first two bytes were "stolen" from
           * reclaimed these two bytes for a 'short' version number and shrunk 'credentialType'
             * credentialType to use for a data format version number.
           * to a 'short'.
             *
             * However, this change was reverted as it was a non-forwards-compatible change.  (See
             * toBytes() for why this data format needs to be forwards-compatible.)  Therefore,
             * recover from this misstep by ignoring the first two bytes.
             */
             */
            short version = buffer.getShort();
            result.credentialType = (short) buffer.getInt();
            if (version == ((short) 0) || version == (short) -1) {
                version = PASSWORD_DATA_V1;
            } else if (version != PASSWORD_DATA_V2) {
                throw new IllegalArgumentException("Unknown PasswordData version: " + version);
            }
            result.credentialType = buffer.getShort();
            result.scryptLogN = buffer.get();
            result.scryptLogN = buffer.get();
            result.scryptLogR = buffer.get();
            result.scryptLogR = buffer.get();
            result.scryptLogP = buffer.get();
            result.scryptLogP = buffer.get();
@@ -407,7 +401,7 @@ class SyntheticPasswordManager {
            } else {
            } else {
                result.passwordHandle = null;
                result.passwordHandle = null;
            }
            }
            if (version == PASSWORD_DATA_V2) {
            if (buffer.remaining() >= Integer.BYTES) {
                result.pinLength = buffer.getInt();
                result.pinLength = buffer.getInt();
            } else {
            } else {
                result.pinLength = PIN_LENGTH_UNAVAILABLE;
                result.pinLength = PIN_LENGTH_UNAVAILABLE;
@@ -415,16 +409,25 @@ class SyntheticPasswordManager {
            return result;
            return result;
        }
        }


        /**
         * Serializes this PasswordData into a byte array.
         * <p>
         * Careful: all changes to the format of the serialized PasswordData must be forwards
         * compatible.  I.e., older versions of Android must still accept the latest PasswordData.
         * This is because a serialized PasswordData is stored in the Factory Reset Protection (FRP)
         * persistent data block.  It's possible that a device has FRP set up on a newer version of
         * Android, is factory reset, and then is set up with an older version of Android.
         */
        public byte[] toBytes() {
        public byte[] toBytes() {


            ByteBuffer buffer = ByteBuffer.allocate(2 * Short.BYTES + 3 * Byte.BYTES
            ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
                    + Integer.BYTES + salt.length + Integer.BYTES +
                    + Integer.BYTES + salt.length + Integer.BYTES +
                    (passwordHandle != null ? passwordHandle.length : 0) + Integer.BYTES);
                    (passwordHandle != null ? passwordHandle.length : 0) + Integer.BYTES);
            // credentialType must fit in a short.  For an explanation, see fromBytes().
            if (credentialType < Short.MIN_VALUE || credentialType > Short.MAX_VALUE) {
            if (credentialType < Short.MIN_VALUE || credentialType > Short.MAX_VALUE) {
                throw new IllegalArgumentException("Unknown credential type: " + credentialType);
                throw new IllegalArgumentException("Unknown credential type: " + credentialType);
            }
            }
            buffer.putShort(PASSWORD_DATA_V2);
            buffer.putInt(credentialType);
            buffer.putShort((short) credentialType);
            buffer.put(scryptLogN);
            buffer.put(scryptLogN);
            buffer.put(scryptLogR);
            buffer.put(scryptLogR);
            buffer.put(scryptLogP);
            buffer.put(scryptLogP);
+49 −22
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE;
import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
@@ -625,11 +626,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
    }
    }


    @Test
    @Test
    public void testPasswordDataV2VersionCredentialTypePin_deserialize() {
    public void testDeserializePasswordData_forPinWithLengthAvailable() {
        // Test that we can deserialize existing PasswordData and don't inadvertently change the
        // wire format.
        byte[] serialized = new byte[] {
        byte[] serialized = new byte[] {
                0, 2, 0, 2, /* CREDENTIAL_TYPE_PASSWORD_OR_PIN */
                0, 0, 0, 3, /* CREDENTIAL_TYPE_PIN */
                11, /* scryptLogN */
                11, /* scryptLogN */
                22, /* scryptLogR */
                22, /* scryptLogR */
                33, /* scryptLogP */
                33, /* scryptLogP */
@@ -637,25 +636,23 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
                1, 2, -1, -2, 55, /* salt */
                1, 2, -1, -2, 55, /* salt */
                0, 0, 0, 6, /* passwordHandle.length */
                0, 0, 0, 6, /* passwordHandle.length */
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                0, 0, 0, 5, /* pinLength */
                0, 0, 0, 6, /* pinLength */
        };
        };
        PasswordData deserialized = PasswordData.fromBytes(serialized);
        PasswordData deserialized = PasswordData.fromBytes(serialized);


        assertEquals(11, deserialized.scryptLogN);
        assertEquals(11, deserialized.scryptLogN);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(5, deserialized.pinLength);
        assertEquals(CREDENTIAL_TYPE_PIN, deserialized.credentialType);
        assertEquals(CREDENTIAL_TYPE_PASSWORD_OR_PIN, deserialized.credentialType);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertEquals(6, deserialized.pinLength);
    }
    }


    @Test
    @Test
    public void testPasswordDataV2VersionNegativePinLengthNoCredential_deserialize() {
    public void testDeserializePasswordData_forPinWithLengthExplicitlyUnavailable() {
        // Test that we can deserialize existing PasswordData and don't inadvertently change the
        // wire format.
        byte[] serialized = new byte[] {
        byte[] serialized = new byte[] {
                0, 2, -1, -1, /* CREDENTIAL_TYPE_NONE */
                0, 0, 0, 3, /* CREDENTIAL_TYPE_PIN */
                11, /* scryptLogN */
                11, /* scryptLogN */
                22, /* scryptLogR */
                22, /* scryptLogR */
                33, /* scryptLogP */
                33, /* scryptLogP */
@@ -663,23 +660,52 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
                1, 2, -1, -2, 55, /* salt */
                1, 2, -1, -2, 55, /* salt */
                0, 0, 0, 6, /* passwordHandle.length */
                0, 0, 0, 6, /* passwordHandle.length */
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                2, 3, -2, -3, 44, 1, /* passwordHandle */
                -1, -1, -1, -2, /* pinLength */
                -1, -1, -1, -1, /* pinLength */
        };
        };
        PasswordData deserialized = PasswordData.fromBytes(serialized);
        PasswordData deserialized = PasswordData.fromBytes(serialized);


        assertEquals(11, deserialized.scryptLogN);
        assertEquals(11, deserialized.scryptLogN);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(-2, deserialized.pinLength);
        assertEquals(CREDENTIAL_TYPE_PIN, deserialized.credentialType);
        assertEquals(CREDENTIAL_TYPE_NONE, deserialized.credentialType);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertEquals(PIN_LENGTH_UNAVAILABLE, deserialized.pinLength);
    }
    }


    @Test
    @Test
    public void testPasswordDataV1VersionNoCredential_deserialize() {
    public void testDeserializePasswordData_forPinWithVersionNumber() {
        // Test that we can deserialize existing PasswordData and don't inadvertently change the
        // Test deserializing a PasswordData that has a version number in the first two bytes.
        // wire format.
        // Files like this were created by some Android 14 beta versions.  This version number was a
        // mistake and should be ignored by the deserializer.
        byte[] serialized = 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 */
        };
        PasswordData deserialized = PasswordData.fromBytes(serialized);

        assertEquals(11, deserialized.scryptLogN);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(CREDENTIAL_TYPE_PIN, deserialized.credentialType);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertEquals(6, deserialized.pinLength);
    }

    @Test
    public void testDeserializePasswordData_forNoneCred() {
        // Test that a PasswordData that uses CREDENTIAL_TYPE_NONE and lacks the PIN length field
        // can be deserialized.  Files like this were created by Android 13 and earlier.  Android 14
        // and later no longer create PasswordData for CREDENTIAL_TYPE_NONE.
        byte[] serialized = new byte[] {
        byte[] serialized = new byte[] {
                -1, -1, -1, -1, /* CREDENTIAL_TYPE_NONE */
                -1, -1, -1, -1, /* CREDENTIAL_TYPE_NONE */
                11, /* scryptLogN */
                11, /* scryptLogN */
@@ -695,16 +721,17 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
        assertEquals(11, deserialized.scryptLogN);
        assertEquals(11, deserialized.scryptLogN);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(PIN_LENGTH_UNAVAILABLE, deserialized.pinLength);
        assertEquals(CREDENTIAL_TYPE_NONE, deserialized.credentialType);
        assertEquals(CREDENTIAL_TYPE_NONE, deserialized.credentialType);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertEquals(PIN_LENGTH_UNAVAILABLE, deserialized.pinLength);
    }
    }


    @Test
    @Test
    public void testPasswordDataV1VersionCredentialTypePin_deserialize() {
    public void testDeserializePasswordData_forPasswordOrPin() {
        // Test that we can deserialize existing PasswordData and don't inadvertently change the
        // Test that a PasswordData that uses CREDENTIAL_TYPE_PASSWORD_OR_PIN and lacks the PIN
        // wire format.
        // length field can be deserialized.  Files like this were created by Android 10 and
        // earlier.  Android 11 eliminated CREDENTIAL_TYPE_PASSWORD_OR_PIN.
        byte[] serialized = new byte[] {
        byte[] serialized = new byte[] {
                0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD_OR_PIN */
                0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD_OR_PIN */
                11, /* scryptLogN */
                11, /* scryptLogN */
@@ -720,10 +747,10 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
        assertEquals(11, deserialized.scryptLogN);
        assertEquals(11, deserialized.scryptLogN);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(22, deserialized.scryptLogR);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(33, deserialized.scryptLogP);
        assertEquals(PIN_LENGTH_UNAVAILABLE, deserialized.pinLength);
        assertEquals(CREDENTIAL_TYPE_PASSWORD_OR_PIN, deserialized.credentialType);
        assertEquals(CREDENTIAL_TYPE_PASSWORD_OR_PIN, deserialized.credentialType);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD, deserialized.salt);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
        assertEquals(PIN_LENGTH_UNAVAILABLE, deserialized.pinLength);
    }
    }


    @Test
    @Test