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;
18
19import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
20import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
21
22import android.os.RemoteException;
23import android.os.UserHandle;
24
25import com.android.internal.widget.LockPatternUtils;
26import com.android.internal.widget.VerifyCredentialResponse;
27import com.android.server.SyntheticPasswordManager.AuthenticationResult;
28import com.android.server.SyntheticPasswordManager.AuthenticationToken;
29
30
31/**
32 * runtest frameworks-services -c com.android.server.SyntheticPasswordTests
33 */
34public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
35
36    @Override
37    protected void setUp() throws Exception {
38        super.setUp();
39    }
40
41    @Override
42    protected void tearDown() throws Exception {
43        super.tearDown();
44    }
45
46    public void testPasswordBasedSyntheticPassword() throws RemoteException {
47        final int USER_ID = 10;
48        final String PASSWORD = "user-password";
49        final String BADPASSWORD = "bad-password";
50        MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService);
51        AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
52                null, USER_ID);
53        long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, PASSWORD,
54                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, USER_ID);
55
56        AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle, PASSWORD, USER_ID);
57        assertEquals(result.authToken.deriveKeyStorePassword(), authToken.deriveKeyStorePassword());
58
59        result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle, BADPASSWORD, USER_ID);
60        assertNull(result.authToken);
61    }
62
63    private void disableSyntheticPassword(int userId) throws RemoteException {
64        mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM);
65    }
66
67    private void enableSyntheticPassword(int userId) throws RemoteException {
68        mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
69    }
70
71    private boolean hasSyntheticPassword(int userId) throws RemoteException {
72        return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0;
73    }
74
75    public void testPasswordMigration() throws RemoteException {
76        final String PASSWORD = "testPasswordMigration-password";
77
78        disableSyntheticPassword(PRIMARY_USER_ID);
79        mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
80        long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
81        final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
82        enableSyntheticPassword(PRIMARY_USER_ID);
83        // Performs migration
84        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
85                mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
86        assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
87        assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
88
89        // SP-based verification
90        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
91                mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
92        assertArrayNotSame(primaryStorageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
93    }
94
95    private void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
96        enableSyntheticPassword(userId);
97        mService.setLockCredential(password, password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD : LockPatternUtils.CREDENTIAL_TYPE_NONE, null, userId);
98    }
99
100    public void testSyntheticPasswordChangeCredential() throws RemoteException {
101        final String PASSWORD = "testSyntheticPasswordChangeCredential-password";
102        final String NEWPASSWORD = "testSyntheticPasswordChangeCredential-newpassword";
103
104        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
105        long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
106        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, PRIMARY_USER_ID);
107        mGateKeeperService.clearSecureUserId(PRIMARY_USER_ID);
108        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
109                mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
110        assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
111    }
112
113    public void testSyntheticPasswordVerifyCredential() throws RemoteException {
114        final String PASSWORD = "testSyntheticPasswordVerifyCredential-password";
115        final String BADPASSWORD = "testSyntheticPasswordVerifyCredential-badpassword";
116
117        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
118        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
119                mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
120
121        assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
122                mService.verifyCredential(BADPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
123    }
124
125    public void testSyntheticPasswordClearCredential() throws RemoteException {
126        final String PASSWORD = "testSyntheticPasswordClearCredential-password";
127        final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
128
129        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
130        long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
131        // clear password
132        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, PRIMARY_USER_ID);
133        assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
134
135        // set a new password
136        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
137        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
138                mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
139        assertNotSame(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
140    }
141
142    public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
143        final String PASSWORD = "testSyntheticPasswordClearCredential-password";
144        final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
145
146        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
147        long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
148        // clear password
149        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
150        assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
151
152        // set a new password
153        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
154        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
155                mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
156        assertNotSame(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
157    }
158
159    public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
160        final String PASSWORD = "testSyntheticPasswordClearCredential-password";
161        final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
162
163        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
164        long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
165        // Untrusted change password
166        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
167        assertNotSame(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
168        assertNotSame(sid ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
169
170        // Verify the password
171        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
172                mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
173    }
174
175
176    public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
177        final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
178        disableSyntheticPassword(PRIMARY_USER_ID);
179        disableSyntheticPassword(MANAGED_PROFILE_USER_ID);
180        mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
181        mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
182        final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
183        final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
184        final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
185        final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
186        assertTrue(primarySid != 0);
187        assertTrue(profileSid != 0);
188        assertTrue(profileSid != primarySid);
189
190        // do migration
191        enableSyntheticPassword(PRIMARY_USER_ID);
192        enableSyntheticPassword(MANAGED_PROFILE_USER_ID);
193        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
194                mService.verifyCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
195
196        // verify
197        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
198                mService.verifyCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
199        assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
200        assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
201        assertArrayNotSame(primaryStorageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
202        assertArrayNotSame(profileStorageKey, mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID));
203        assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
204        assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID));
205    }
206
207    public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
208        final String primaryPassword = "testManagedProfileSeparateChallengeMigration-primary";
209        final String profilePassword = "testManagedProfileSeparateChallengeMigration-profile";
210        mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
211        mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, MANAGED_PROFILE_USER_ID);
212        final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
213        final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
214        final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
215        final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
216        assertTrue(primarySid != 0);
217        assertTrue(profileSid != 0);
218        assertTrue(profileSid != primarySid);
219
220        // do migration
221        enableSyntheticPassword(PRIMARY_USER_ID);
222        enableSyntheticPassword(MANAGED_PROFILE_USER_ID);
223        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
224                mService.verifyCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
225        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
226                mService.verifyCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, MANAGED_PROFILE_USER_ID).getResponseCode());
227
228        // verify
229        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
230                mService.verifyCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
231        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
232                mService.verifyCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, MANAGED_PROFILE_USER_ID).getResponseCode());
233        assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
234        assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
235        assertArrayNotSame(primaryStorageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
236        assertArrayNotSame(profileStorageKey, mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID));
237        assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
238        assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID));
239    }
240
241    public void testTokenBasedResetPassword() throws RemoteException {
242        final String PASSWORD = "password";
243        final String PATTERN = "123654";
244        final String TOKEN = "some-high-entropy-secure-token";
245        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
246        final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
247
248        long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
249        assertFalse(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
250
251        mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode();
252        assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
253
254        mService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
255
256        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
257                mService.verifyCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID).getResponseCode());
258        assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
259    }
260
261    public void testTokenBasedClearPassword() throws RemoteException {
262        final String PASSWORD = "password";
263        final String PATTERN = "123654";
264        final String TOKEN = "some-high-entropy-secure-token";
265        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
266        final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
267
268        long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
269        assertFalse(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
270
271        mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode();
272        assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
273
274        mService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
275        mService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
276
277        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
278                mService.verifyCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID).getResponseCode());
279        assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
280    }
281
282    public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException {
283        final String PASSWORD = "password";
284        final String PATTERN = "123654";
285        final String NEWPASSWORD = "password";
286        final String TOKEN = "some-high-entropy-secure-token";
287        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
288        final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
289
290        long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
291        assertFalse(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
292
293        mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode();
294        assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
295
296        mService.setLockCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, PASSWORD, PRIMARY_USER_ID);
297
298        mService.setLockCredentialWithToken(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
299
300        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
301                mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
302        assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
303    }
304
305    public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration() throws RemoteException {
306        final String TOKEN = "some-high-entropy-secure-token";
307        enableSyntheticPassword(PRIMARY_USER_ID);
308        long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
309        assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
310        assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
311        assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
312    }
313
314    public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration() throws RemoteException {
315        final String TOKEN = "some-high-entropy-secure-token";
316        initializeCredentialUnderSP(null, PRIMARY_USER_ID);
317        long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
318        assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
319        assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
320        assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
321    }
322
323    public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration() throws RemoteException {
324        final String TOKEN = "some-high-entropy-secure-token";
325        final String PASSWORD = "password";
326        // Set up pre-SP user password
327        disableSyntheticPassword(PRIMARY_USER_ID);
328        mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
329                PRIMARY_USER_ID);
330        enableSyntheticPassword(PRIMARY_USER_ID);
331
332        long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
333        // Token not activated immediately since user password exists
334        assertFalse(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
335        // Activate token (password gets migrated to SP at the same time)
336        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
337                mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
338                        PRIMARY_USER_ID).getResponseCode());
339        // Verify token is activated
340        assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
341    }
342
343    // b/34600579
344    //TODO: add non-migration work profile case, and unify/un-unify transition.
345    //TODO: test token after user resets password
346    //TODO: test token based reset after unified work challenge
347    //TODO: test clear password after unified work challenge
348}
349
350