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