1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.locksettings; 17 18 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; 19 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; 20 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; 21 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; 22 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; 23 import static com.android.internal.widget.LockPatternUtils.USER_FRP; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertTrue; 28 29 import android.app.PropertyInvalidatedCache; 30 import android.app.admin.DevicePolicyManager; 31 import android.platform.test.annotations.Presubmit; 32 33 import androidx.test.filters.SmallTest; 34 import androidx.test.runner.AndroidJUnit4; 35 36 import com.android.internal.widget.VerifyCredentialResponse; 37 import com.android.server.locksettings.LockSettingsStorage.PersistentData; 38 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 43 import java.nio.ByteBuffer; 44 45 /** Tests that involve the Factory Reset Protection (FRP) credential. */ 46 @SmallTest 47 @Presubmit 48 @RunWith(AndroidJUnit4.class) 49 public class LockscreenFrpTest extends BaseLockSettingsServiceTests { 50 51 @Before setUp()52 public void setUp() throws Exception { 53 PropertyInvalidatedCache.disableForTestMode(); 54 55 // FRP credential can only be verified prior to provisioning 56 setDeviceProvisioned(false); 57 58 mService.initializeSyntheticPassword(PRIMARY_USER_ID); 59 } 60 61 @Test testFrpCredential_setPin()62 public void testFrpCredential_setPin() { 63 mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); 64 65 assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP)); 66 assertEquals(VerifyCredentialResponse.RESPONSE_OK, 67 mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */) 68 .getResponseCode()); 69 } 70 71 @Test testFrpCredential_setPattern()72 public void testFrpCredential_setPattern() { 73 mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID); 74 75 assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP)); 76 assertEquals(VerifyCredentialResponse.RESPONSE_OK, 77 mService.verifyCredential(newPattern("4321"), USER_FRP, 0 /* flags */) 78 .getResponseCode()); 79 } 80 81 @Test testFrpCredential_setPassword()82 public void testFrpCredential_setPassword() { 83 mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID); 84 85 assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); 86 assertEquals(VerifyCredentialResponse.RESPONSE_OK, 87 mService.verifyCredential(newPassword("4321"), USER_FRP, 0 /* flags */) 88 .getResponseCode()); 89 } 90 91 @Test testFrpCredential_changeCredential()92 public void testFrpCredential_changeCredential() { 93 mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); 94 mService.setLockCredential(newPattern("5678"), newPassword("1234"), PRIMARY_USER_ID); 95 96 assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP)); 97 assertEquals(VerifyCredentialResponse.RESPONSE_OK, 98 mService.verifyCredential(newPattern("5678"), USER_FRP, 0 /* flags */) 99 .getResponseCode()); 100 } 101 102 @Test testFrpCredential_removeCredential()103 public void testFrpCredential_removeCredential() { 104 mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); 105 assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); 106 107 setDeviceProvisioned(true); 108 mService.setLockCredential(nonePassword(), newPassword("1234"), PRIMARY_USER_ID); 109 assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(USER_FRP)); 110 } 111 112 @Test testFrpCredential_cannotVerifyAfterProvsioning()113 public void testFrpCredential_cannotVerifyAfterProvsioning() { 114 mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); 115 116 setDeviceProvisioned(true); 117 assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, 118 mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */) 119 .getResponseCode()); 120 } 121 122 @Test testFrpCredential_legacyPinTypePersistentData()123 public void testFrpCredential_legacyPinTypePersistentData() { 124 mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); 125 PersistentData data = mStorage.readPersistentDataBlock(); 126 // Tweak the existing persistent data to make it look like one with legacy credential type 127 assertEquals(CREDENTIAL_TYPE_PIN, data.payload[3]); 128 data.payload[3] = CREDENTIAL_TYPE_PASSWORD_OR_PIN; 129 mStorage.writePersistentDataBlock(data.type, data.userId, 130 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, data.payload); 131 132 assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP)); 133 assertEquals(VerifyCredentialResponse.RESPONSE_OK, 134 mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */) 135 .getResponseCode()); 136 137 } 138 139 @Test testFrpCredential_legacyPasswordTypePersistentData()140 public void testFrpCredential_legacyPasswordTypePersistentData() { 141 mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); 142 PersistentData data = mStorage.readPersistentDataBlock(); 143 // Tweak the existing persistent data to make it look like one with legacy credential type 144 assertEquals(CREDENTIAL_TYPE_PASSWORD, data.payload[3]); 145 data.payload[3] = CREDENTIAL_TYPE_PASSWORD_OR_PIN; 146 mStorage.writePersistentDataBlock(data.type, data.userId, 147 DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, data.payload); 148 149 assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); 150 assertEquals(VerifyCredentialResponse.RESPONSE_OK, 151 mService.verifyCredential(newPin("1234"), USER_FRP, 0 /* flags */) 152 .getResponseCode()); 153 } 154 155 // The FRP block that gets written by the current version of Android must still be accepted by 156 // old versions of Android. This test tries to detect non-forward-compatible changes in 157 // PasswordData#toBytes(), which would break that. 158 @Test testFrpBlock_isForwardsCompatible()159 public void testFrpBlock_isForwardsCompatible() { 160 mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); 161 PersistentData data = mStorage.readPersistentDataBlock(); 162 ByteBuffer buffer = ByteBuffer.wrap(data.payload); 163 164 final int credentialType = buffer.getInt(); 165 assertEquals(CREDENTIAL_TYPE_PIN, credentialType); 166 167 final byte scryptLogN = buffer.get(); 168 assertTrue(scryptLogN >= 0); 169 170 final byte scryptLogR = buffer.get(); 171 assertTrue(scryptLogR >= 0); 172 173 final byte scryptLogP = buffer.get(); 174 assertTrue(scryptLogP >= 0); 175 176 final int saltLength = buffer.getInt(); 177 assertTrue(saltLength > 0); 178 final byte[] salt = new byte[saltLength]; 179 buffer.get(salt); 180 181 final int passwordHandleLength = buffer.getInt(); 182 assertTrue(passwordHandleLength > 0); 183 final byte[] passwordHandle = new byte[passwordHandleLength]; 184 buffer.get(passwordHandle); 185 } 186 187 @Test testFrpBlock_inBadAndroid14FormatIsAutomaticallyFixed()188 public void testFrpBlock_inBadAndroid14FormatIsAutomaticallyFixed() { 189 mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); 190 191 // Write a "bad" FRP block with PasswordData beginning with the bytes [0, 2]. 192 byte[] badPasswordData = new byte[] { 193 0, 2, /* version 2 */ 194 0, 3, /* CREDENTIAL_TYPE_PIN */ 195 11, /* scryptLogN */ 196 22, /* scryptLogR */ 197 33, /* scryptLogP */ 198 0, 0, 0, 5, /* salt.length */ 199 1, 2, -1, -2, 55, /* salt */ 200 0, 0, 0, 6, /* passwordHandle.length */ 201 2, 3, -2, -3, 44, 1, /* passwordHandle */ 202 0, 0, 0, 6, /* pinLength */ 203 }; 204 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_GATEKEEPER, PRIMARY_USER_ID, 0, 205 badPasswordData); 206 207 // Execute the code that should fix the FRP block. 208 assertFalse(mStorage.getBoolean("migrated_frp2", false, 0)); 209 mService.migrateOldDataAfterSystemReady(); 210 assertTrue(mStorage.getBoolean("migrated_frp2", false, 0)); 211 212 // Verify that the FRP block has been fixed. 213 PersistentData data = mStorage.readPersistentDataBlock(); 214 assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type); 215 ByteBuffer buffer = ByteBuffer.wrap(data.payload); 216 assertEquals(CREDENTIAL_TYPE_PIN, buffer.getInt()); 217 } 218 } 219