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
17package com.android.server.locksettings;
18
19import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
20import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
21import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
22
23import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
24import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
25import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
26
27import static org.mockito.Mockito.any;
28import static org.mockito.Mockito.atLeastOnce;
29import static org.mockito.Mockito.never;
30import static org.mockito.Mockito.reset;
31import static org.mockito.Mockito.verify;
32
33import android.app.admin.PasswordMetrics;
34import android.os.RemoteException;
35import android.os.UserHandle;
36
37import com.android.internal.widget.LockPatternUtils;
38import com.android.internal.widget.VerifyCredentialResponse;
39import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
40import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
41import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
42
43import java.util.ArrayList;
44
45import org.mockito.ArgumentCaptor;
46
47
48/**
49 * runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests
50 */
51public 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
57    protected void setUp() throws Exception {
58        super.setUp();
59    }
60
61    @Override
62    protected void tearDown() throws Exception {
63        super.tearDown();
64    }
65
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
87    private void disableSyntheticPassword() throws RemoteException {
88        mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM);
89    }
90
91    private void enableSyntheticPassword() throws RemoteException {
92        mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
93    }
94
95    private boolean hasSyntheticPassword(int userId) throws RemoteException {
96        return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0;
97    }
98
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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