Merge "Fix downgrades on device with FRP enabled" into udc-dev am: 255c2994b3
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23263942 Change-Id: Ic34a25f4717d7e69e45abe65b22cd59557997bfb Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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;
|
||||||
@@ -379,21 +376,18 @@ class SyntheticPasswordManager {
|
|||||||
buffer.put(data, 0, data.length);
|
buffer.put(data, 0, data.length);
|
||||||
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
|
||||||
short version = buffer.getShort();
|
* toBytes() for why this data format needs to be forwards-compatible.) Therefore,
|
||||||
if (version == ((short) 0) || version == (short) -1) {
|
* recover from this misstep by ignoring the first two bytes.
|
||||||
version = PASSWORD_DATA_V1;
|
*/
|
||||||
} else if (version != PASSWORD_DATA_V2) {
|
result.credentialType = (short) buffer.getInt();
|
||||||
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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user