1/* 2 * Copyright (C) 2018 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 */ 16package com.android.server.locksettings; 17 18import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 19import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 20 21import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; 22import static com.android.server.testutils.TestUtils.assertExpectException; 23 24import static org.mockito.Mockito.anyInt; 25import static org.mockito.Mockito.atLeastOnce; 26import static org.mockito.Mockito.when; 27import static org.mockito.Mockito.verify; 28 29import android.os.RemoteException; 30 31import com.android.internal.widget.LockPatternUtils; 32import com.android.internal.widget.VerifyCredentialResponse; 33import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; 34 35import java.util.ArrayList; 36 37import org.mockito.ArgumentCaptor; 38 39/** 40 * Run the synthetic password tests with caching enabled. 41 * 42 * By default, those tests run without caching. Untrusted credential reset depends on caching so 43 * this class included those tests. 44 */ 45public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { 46 47 @Override 48 protected void setUp() throws Exception { 49 super.setUp(); 50 enableSpCaching(true); 51 } 52 53 private void enableSpCaching(boolean enable) { 54 when(mDevicePolicyManagerInternal 55 .canUserHaveUntrustedCredentialReset(anyInt())).thenReturn(enable); 56 } 57 58 public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException { 59 final String PASSWORD = "testSyntheticPasswordClearCredential-password"; 60 final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword"; 61 62 initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); 63 long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); 64 // clear password 65 mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null, 66 PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); 67 assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 68 69 // set a new password 70 mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, 71 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); 72 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 73 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 74 .getResponseCode()); 75 assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 76 } 77 78 public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException { 79 final String PASSWORD = "testSyntheticPasswordClearCredential-password"; 80 final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword"; 81 82 initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); 83 long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); 84 // Untrusted change password 85 mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, 86 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); 87 assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 88 assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 89 90 // Verify the password 91 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 92 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 93 .getResponseCode()); 94 } 95 96 public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException { 97 final String PASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-password"; 98 final String NEWPASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword"; 99 100 initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); 101 // Untrusted change password 102 mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, 103 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); 104 105 // Verify the password 106 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 107 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 108 .getResponseCode()); 109 110 // Ensure the same secret was passed each time 111 ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class); 112 verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture()); 113 assertEquals(1, secret.getAllValues().stream().distinct().count()); 114 } 115 116 public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException { 117 final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password"; 118 final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword"; 119 120 // Disable caching for this test 121 enableSpCaching(false); 122 123 initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); 124 long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); 125 // Untrusted change password 126 assertExpectException(IllegalStateException.class, /* messageRegex= */ null, 127 () -> mService.setLockCredential( 128 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 129 null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID)); 130 assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); 131 132 // Verify the new password doesn't work but the old one still does 133 assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential( 134 NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 135 .getResponseCode()); 136 assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( 137 PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) 138 .getResponseCode()); 139 } 140 141} 142