1 /*
2  * Copyright (C) 2017 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 
17 package com.android.server.locksettings;
18 
19 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
20 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
21 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
22 
23 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
24 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
25 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
26 
27 import static org.mockito.Mockito.any;
28 import static org.mockito.Mockito.atLeastOnce;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.reset;
31 import static org.mockito.Mockito.verify;
32 
33 import android.app.admin.PasswordMetrics;
34 import android.os.RemoteException;
35 import android.os.UserHandle;
36 
37 import com.android.internal.widget.LockPatternUtils;
38 import com.android.internal.widget.VerifyCredentialResponse;
39 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
40 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
41 import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
42 
43 import java.util.ArrayList;
44 
45 import org.mockito.ArgumentCaptor;
46 
47 
48 /**
49  * runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests
50  */
51 public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
52 
53     public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 55};
54     public static final byte[] PAYLOAD2 = new byte[] {2, 3, -2, -3, 44, 1};
55 
56     @Override
setUp()57     protected void setUp() throws Exception {
58         super.setUp();
59     }
60 
61     @Override
tearDown()62     protected void tearDown() throws Exception {
63         super.tearDown();
64     }
65 
testPasswordBasedSyntheticPassword()66     public void testPasswordBasedSyntheticPassword() throws RemoteException {
67         final int USER_ID = 10;
68         final String PASSWORD = "user-password";
69         final String BADPASSWORD = "bad-password";
70         MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
71                 mGateKeeperService, mUserManager);
72         AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
73                 null, USER_ID);
74         long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, PASSWORD,
75                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, PASSWORD_QUALITY_ALPHABETIC,
76                 USER_ID);
77 
78         AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(
79                 mGateKeeperService, handle, PASSWORD, USER_ID, null);
80         assertEquals(result.authToken.deriveKeyStorePassword(), authToken.deriveKeyStorePassword());
81 
82         result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle,
83                 BADPASSWORD, USER_ID, null);
84         assertNull(result.authToken);
85     }
86 
disableSyntheticPassword()87     private void disableSyntheticPassword() throws RemoteException {
88         mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM);
89     }
90 
enableSyntheticPassword()91     private void enableSyntheticPassword() throws RemoteException {
92         mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
93     }
94 
hasSyntheticPassword(int userId)95     private boolean hasSyntheticPassword(int userId) throws RemoteException {
96         return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0;
97     }
98 
testPasswordMigration()99     public void testPasswordMigration() throws RemoteException {
100         final String PASSWORD = "testPasswordMigration-password";
101 
102         disableSyntheticPassword();
103         mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
104                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
105         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
106         final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
107         enableSyntheticPassword();
108         // Performs migration
109         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
110                 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
111                     .getResponseCode());
112         assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
113         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
114 
115         // SP-based verification
116         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
117                 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
118                         .getResponseCode());
119         assertArrayNotEquals(primaryStorageKey,
120                 mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
121     }
122 
initializeCredentialUnderSP(String password, int userId)123     protected void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
124         enableSyntheticPassword();
125         int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
126                 : PASSWORD_QUALITY_UNSPECIFIED;
127         int type = password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
128                 : LockPatternUtils.CREDENTIAL_TYPE_NONE;
129         mService.setLockCredential(password, type, null, quality, userId);
130     }
131 
testSyntheticPasswordChangeCredential()132     public void testSyntheticPasswordChangeCredential() throws RemoteException {
133         final String PASSWORD = "testSyntheticPasswordChangeCredential-password";
134         final String NEWPASSWORD = "testSyntheticPasswordChangeCredential-newpassword";
135 
136         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
137         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
138         mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
139                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
140         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
141                 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
142                         .getResponseCode());
143         assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
144     }
145 
testSyntheticPasswordVerifyCredential()146     public void testSyntheticPasswordVerifyCredential() throws RemoteException {
147         final String PASSWORD = "testSyntheticPasswordVerifyCredential-password";
148         final String BADPASSWORD = "testSyntheticPasswordVerifyCredential-badpassword";
149 
150         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
151         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
152                 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
153                         .getResponseCode());
154 
155         assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
156                 BADPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
157                     .getResponseCode());
158     }
159 
testSyntheticPasswordClearCredential()160     public void testSyntheticPasswordClearCredential() throws RemoteException {
161         final String PASSWORD = "testSyntheticPasswordClearCredential-password";
162         final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
163 
164         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
165         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
166         // clear password
167         mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD,
168                 PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
169         assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
170 
171         // set a new password
172         mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
173                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
174         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
175                 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
176                     .getResponseCode());
177         assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
178     }
179 
testSyntheticPasswordChangeCredentialKeepsAuthSecret()180     public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException {
181         final String PASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password";
182         final String NEWPASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new";
183 
184         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
185         mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
186                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
187         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
188                 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
189                         .getResponseCode());
190 
191         // Check the same secret was passed each time
192         ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class);
193         verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture());
194         assertEquals(1, secret.getAllValues().stream().distinct().count());
195     }
196 
testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret()197     public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
198         final String PASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password";
199         final String NEWPASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new";
200 
201         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
202         reset(mAuthSecretService);
203         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
204                 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
205                         .getResponseCode());
206         verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
207     }
208 
testSecondaryUserDoesNotPassAuthSecret()209     public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException {
210         final String PASSWORD = "testSecondaryUserDoesNotPassAuthSecret-password";
211         final String NEWPASSWORD = "testSecondaryUserDoesNotPassAuthSecret-new";
212 
213         initializeCredentialUnderSP(PASSWORD, SECONDARY_USER_ID);
214         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
215                 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID)
216                         .getResponseCode());
217         verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
218     }
219 
testNoSyntheticPasswordOrCredentialDoesNotPassAuthSecret()220     public void testNoSyntheticPasswordOrCredentialDoesNotPassAuthSecret() throws RemoteException {
221         // Setting null doesn't create a synthetic password
222         initializeCredentialUnderSP(null, PRIMARY_USER_ID);
223 
224         reset(mAuthSecretService);
225         mService.onUnlockUser(PRIMARY_USER_ID);
226         mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
227         verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
228     }
229 
testSyntheticPasswordAndCredentialDoesNotPassAuthSecret()230     public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException {
231         final String PASSWORD = "passwordForASyntheticPassword";
232         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
233 
234         reset(mAuthSecretService);
235         mService.onUnlockUser(PRIMARY_USER_ID);
236         mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
237         verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
238     }
239 
testSyntheticPasswordButNoCredentialPassesAuthSecret()240     public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException {
241         final String PASSWORD = "getASyntheticPassword";
242         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
243         mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD,
244                 PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
245 
246         reset(mAuthSecretService);
247         mService.onUnlockUser(PRIMARY_USER_ID);
248         mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
249         verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
250     }
251 
testManagedProfileUnifiedChallengeMigration()252     public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
253         final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
254         disableSyntheticPassword();
255         mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
256                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
257         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
258         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
259         final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
260         final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
261         final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
262         assertTrue(primarySid != 0);
263         assertTrue(profileSid != 0);
264         assertTrue(profileSid != primarySid);
265 
266         // do migration
267         enableSyntheticPassword();
268         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
269                 UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
270                         .getResponseCode());
271 
272         // verify
273         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
274                 UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
275                         .getResponseCode());
276         assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
277         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
278         assertArrayNotEquals(primaryStorageKey,
279                 mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
280         assertArrayNotEquals(profileStorageKey,
281                 mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID));
282         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
283         assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID));
284     }
285 
testManagedProfileSeparateChallengeMigration()286     public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
287         final String primaryPassword = "testManagedProfileSeparateChallengeMigration-primary";
288         final String profilePassword = "testManagedProfileSeparateChallengeMigration-profile";
289         disableSyntheticPassword();
290         mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
291                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
292         mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
293                 PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID);
294         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
295         final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
296         final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
297         final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
298         assertTrue(primarySid != 0);
299         assertTrue(profileSid != 0);
300         assertTrue(profileSid != primarySid);
301 
302         // do migration
303         enableSyntheticPassword();
304         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
305                 primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
306                         .getResponseCode());
307         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
308                 profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
309                 0, MANAGED_PROFILE_USER_ID).getResponseCode());
310 
311         // verify
312         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
313                 primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
314                         .getResponseCode());
315         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
316                 profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
317                 0, MANAGED_PROFILE_USER_ID).getResponseCode());
318         assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
319         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
320         assertArrayNotEquals(primaryStorageKey,
321                 mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
322         assertArrayNotEquals(profileStorageKey,
323                 mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID));
324         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
325         assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID));
326     }
327 
testTokenBasedResetPassword()328     public void testTokenBasedResetPassword() throws RemoteException {
329         final String PASSWORD = "password";
330         final String PATTERN = "123654";
331         final String TOKEN = "some-high-entropy-secure-token";
332         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
333         final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
334 
335         long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
336         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
337 
338         mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
339                 PRIMARY_USER_ID).getResponseCode();
340         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
341 
342         mLocalService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
343                 handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
344 
345         // Verify DPM gets notified about new device lock
346         mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
347         PasswordMetrics metric = PasswordMetrics.computeForPassword(PATTERN);
348         metric.quality = PASSWORD_QUALITY_SOMETHING;
349         verify(mDevicePolicyManager).setActivePasswordState(metric, PRIMARY_USER_ID);
350 
351         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
352                 PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
353                     .getResponseCode());
354         assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
355     }
356 
testTokenBasedClearPassword()357     public void testTokenBasedClearPassword() throws RemoteException {
358         final String PASSWORD = "password";
359         final String PATTERN = "123654";
360         final String TOKEN = "some-high-entropy-secure-token";
361         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
362         final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
363 
364         long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
365         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
366 
367         mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
368                 0, PRIMARY_USER_ID).getResponseCode();
369         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
370 
371         mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
372                 handle, TOKEN.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
373         mLocalService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
374                 handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
375 
376         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
377                 PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
378                         .getResponseCode());
379         assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
380     }
381 
testTokenBasedResetPasswordAfterCredentialChanges()382     public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException {
383         final String PASSWORD = "password";
384         final String PATTERN = "123654";
385         final String NEWPASSWORD = "password";
386         final String TOKEN = "some-high-entropy-secure-token";
387         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
388         final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
389 
390         long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
391         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
392 
393         mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
394                 0, PRIMARY_USER_ID).getResponseCode();
395         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
396 
397         mService.setLockCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, PASSWORD,
398                 PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
399 
400         mLocalService.setLockCredentialWithToken(NEWPASSWORD,
401                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(),
402                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
403 
404         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
405                 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
406                     .getResponseCode());
407         assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
408     }
409 
testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration()410     public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration()
411             throws RemoteException {
412         final String TOKEN = "some-high-entropy-secure-token";
413         enableSyntheticPassword();
414         long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
415         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
416         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
417         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
418     }
419 
testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration()420     public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration()
421             throws RemoteException {
422         final String TOKEN = "some-high-entropy-secure-token";
423         initializeCredentialUnderSP(null, PRIMARY_USER_ID);
424         long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
425         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
426         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
427         assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
428     }
429 
testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration()430     public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration()
431             throws RemoteException {
432         final String TOKEN = "some-high-entropy-secure-token";
433         final String PASSWORD = "password";
434         // Set up pre-SP user password
435         disableSyntheticPassword();
436         mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
437                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
438         enableSyntheticPassword();
439 
440         long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
441         // Token not activated immediately since user password exists
442         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
443         // Activate token (password gets migrated to SP at the same time)
444         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
445                 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
446                     .getResponseCode());
447         // Verify token is activated
448         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
449     }
450 
testgetHashFactorPrimaryUser()451     public void testgetHashFactorPrimaryUser() throws RemoteException {
452         final String password = "password";
453         mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
454                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
455         final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID);
456         assertNotNull(hashFactor);
457 
458         mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
459                 PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
460         final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID);
461         assertNotNull(newHashFactor);
462         // Hash factor should never change after password change/removal
463         assertArrayEquals(hashFactor, newHashFactor);
464     }
465 
testgetHashFactorManagedProfileUnifiedChallenge()466     public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
467         final String pattern = "1236";
468         mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, null,
469                 PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
470         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
471         assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
472     }
473 
testgetHashFactorManagedProfileSeparateChallenge()474     public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
475         final String primaryPassword = "primary";
476         final String profilePassword = "profile";
477         mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
478                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
479         mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
480                 PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID);
481         assertNotNull(mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID));
482     }
483 
testPasswordData_serializeDeserialize()484     public void testPasswordData_serializeDeserialize() {
485         PasswordData data = new PasswordData();
486         data.scryptN = 11;
487         data.scryptR = 22;
488         data.scryptP = 33;
489         data.passwordType = CREDENTIAL_TYPE_PASSWORD;
490         data.salt = PAYLOAD;
491         data.passwordHandle = PAYLOAD2;
492 
493         PasswordData deserialized = PasswordData.fromBytes(data.toBytes());
494 
495         assertEquals(11, deserialized.scryptN);
496         assertEquals(22, deserialized.scryptR);
497         assertEquals(33, deserialized.scryptP);
498         assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType);
499         assertArrayEquals(PAYLOAD, deserialized.salt);
500         assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
501     }
502 
testPasswordData_deserialize()503     public void testPasswordData_deserialize() {
504         // Test that we can deserialize existing PasswordData and don't inadvertently change the
505         // wire format.
506         byte[] serialized = new byte[] {
507                 0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD */
508                 11, /* scryptN */
509                 22, /* scryptR */
510                 33, /* scryptP */
511                 0, 0, 0, 5, /* salt.length */
512                 1, 2, -1, -2, 55, /* salt */
513                 0, 0, 0, 6, /* passwordHandle.length */
514                 2, 3, -2, -3, 44, 1, /* passwordHandle */
515         };
516         PasswordData deserialized = PasswordData.fromBytes(serialized);
517 
518         assertEquals(11, deserialized.scryptN);
519         assertEquals(22, deserialized.scryptR);
520         assertEquals(33, deserialized.scryptP);
521         assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType);
522         assertArrayEquals(PAYLOAD, deserialized.salt);
523         assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
524     }
525 
526     // b/62213311
527     //TODO: add non-migration work profile case, and unify/un-unify transition.
528     //TODO: test token after user resets password
529     //TODO: test token based reset after unified work challenge
530     //TODO: test clear password after unified work challenge
531 }
532