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