Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +25 −22 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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; Loading @@ -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); Loading services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +49 −22 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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 Loading Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +25 −22 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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; Loading @@ -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); Loading
services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +49 −22 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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 Loading