1 /* 2 * Copyright (C) 2022 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.car.settings.security; 18 19 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; 20 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; 21 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; 22 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; 23 24 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; 25 26 import static com.google.common.truth.Truth.assertThat; 27 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.Mockito.when; 31 32 import android.app.admin.DevicePolicyManager; 33 import android.app.admin.PasswordMetrics; 34 import android.content.Context; 35 import android.os.UserHandle; 36 37 import androidx.test.core.app.ApplicationProvider; 38 import androidx.test.ext.junit.runners.AndroidJUnit4; 39 40 import com.android.car.settings.R; 41 import com.android.internal.widget.LockPatternUtils; 42 import com.android.internal.widget.LockscreenCredential; 43 import com.android.settingslib.utils.StringUtil; 44 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.mockito.Mock; 50 import org.mockito.junit.MockitoJUnit; 51 import org.mockito.junit.MockitoRule; 52 53 @RunWith(AndroidJUnit4.class) 54 public final class PasswordHelperTest { 55 // Never valid 56 private static final String SHORT_SEQUENTIAL_PASSWORD = "111"; 57 private static final String LENGTH_3_PATTERN = "123"; 58 // Only valid when None/Low complexity 59 private static final String MEDIUM_SEQUENTIAL_PASSWORD = "22222"; 60 private static final String LONG_SEQUENTIAL_PASSWORD = "111111111"; 61 // Valid for None/Low/Medium complexity 62 private static final String MEDIUM_ALPHANUMERIC_PASSWORD = "a11r1"; 63 private static final String MEDIUM_PIN_PASSWORD = "11397"; 64 // Valid for all complexities 65 private static final String LONG_ALPHANUMERIC_PASSWORD = "a11r1t131"; 66 private static final String LONG_PIN_PASSWORD = "113982125"; 67 private static final String LENGTH_4_PATTERN = "1234"; 68 69 private final Context mContext = ApplicationProvider.getApplicationContext(); 70 private int mUserId; 71 private PasswordMetrics mPasswordMetrics; 72 73 @Mock 74 LockPatternUtils mLockPatternUtils; 75 @Mock 76 LockscreenCredential mExistingCredential; 77 @Rule 78 public final MockitoRule rule = MockitoJUnit.rule(); 79 80 @Before setUp()81 public void setUp() { 82 mUserId = UserHandle.myUserId(); 83 mPasswordMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); 84 when(mLockPatternUtils.getPasswordHistoryHashFactor(any(), anyInt())) 85 .thenReturn(new byte[0]); 86 } 87 createPassword(String password)88 private LockscreenCredential createPassword(String password) { 89 return LockscreenCredential.createPassword(password); 90 } 91 createPin(String pin)92 private LockscreenCredential createPin(String pin) { 93 return LockscreenCredential.createPin(pin); 94 } 95 createPattern(String patternString)96 private LockscreenCredential createPattern(String patternString) { 97 return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( 98 patternString.getBytes())); 99 } 100 passwordTooShort(int lengthRequired)101 private String passwordTooShort(int lengthRequired) { 102 return StringUtil.getIcuPluralsString(mContext, lengthRequired, 103 R.string.lockpassword_password_too_short); 104 } 105 pinTooShort(int lengthRequired)106 private String pinTooShort(int lengthRequired) { 107 return StringUtil.getIcuPluralsString(mContext, lengthRequired, 108 R.string.lockpassword_pin_too_short); 109 } 110 patternTooShort(int lengthRequired)111 private String patternTooShort(int lengthRequired) { 112 return StringUtil.getIcuPluralsString(mContext, lengthRequired, 113 R.string.lockpattern_recording_incorrect_too_short); 114 } 115 newPasswordHelper( @evicePolicyManager.PasswordComplexity int complexity)116 private PasswordHelper newPasswordHelper( 117 @DevicePolicyManager.PasswordComplexity int complexity) { 118 return new PasswordHelper(mContext, mUserId, mLockPatternUtils, mPasswordMetrics, 119 complexity); 120 } 121 assertCredentialValid(@evicePolicyManager.PasswordComplexity int minComplexity, LockscreenCredential credential)122 private void assertCredentialValid(@DevicePolicyManager.PasswordComplexity int minComplexity, 123 LockscreenCredential credential) { 124 PasswordHelper helper = newPasswordHelper(minComplexity); 125 126 // Test the overload of validateCredential() that doesn't require the existing credential. 127 assertThat(helper.validateCredential(credential)).isTrue(); 128 assertThat(helper.getCredentialValidationErrorMessages()).isEqualTo(""); 129 130 // Test the overload of validateCredential() that requires the existing credential. 131 assertThat(helper.validateCredential(credential, mExistingCredential)).isTrue(); 132 assertThat(helper.getCredentialValidationErrorMessages()).isEqualTo(""); 133 134 } 135 assertCredentialInvalid(@evicePolicyManager.PasswordComplexity int minComplexity, LockscreenCredential credential, String expectedError)136 private void assertCredentialInvalid(@DevicePolicyManager.PasswordComplexity int minComplexity, 137 LockscreenCredential credential, String expectedError) { 138 PasswordHelper helper = newPasswordHelper(minComplexity); 139 140 // Test the overload of validateCredential() that doesn't require the existing credential. 141 assertThat(helper.validateCredential(credential)).isFalse(); 142 assertThat(helper.getCredentialValidationErrorMessages()).isEqualTo(expectedError); 143 144 // Test the overload of validateCredential() that requires the existing credential. 145 assertThat(helper.validateCredential(credential, mExistingCredential)).isFalse(); 146 assertThat(helper.getCredentialValidationErrorMessages()).isEqualTo(expectedError); 147 } 148 149 @Test passwordComplexityNone_shortSequentialPassword_invalid()150 public void passwordComplexityNone_shortSequentialPassword_invalid() { 151 assertCredentialInvalid( 152 PASSWORD_COMPLEXITY_NONE, 153 createPassword(SHORT_SEQUENTIAL_PASSWORD), 154 passwordTooShort(4)); 155 } 156 157 @Test passwordComplexityNone_mediumSequentialPassword_valid()158 public void passwordComplexityNone_mediumSequentialPassword_valid() { 159 assertCredentialValid(PASSWORD_COMPLEXITY_NONE, createPassword(MEDIUM_SEQUENTIAL_PASSWORD)); 160 } 161 162 @Test passwordComplexityLow_shortSequentialPassword_invalid()163 public void passwordComplexityLow_shortSequentialPassword_invalid() { 164 assertCredentialInvalid( 165 PASSWORD_COMPLEXITY_LOW, 166 createPassword(SHORT_SEQUENTIAL_PASSWORD), 167 passwordTooShort(4)); 168 } 169 170 @Test passwordComplexityLow_mediumSequentialPassword_valid()171 public void passwordComplexityLow_mediumSequentialPassword_valid() { 172 assertCredentialValid(PASSWORD_COMPLEXITY_LOW, createPassword(MEDIUM_SEQUENTIAL_PASSWORD)); 173 } 174 175 @Test passwordComplexityMedium_shortSequentialPassword_invalid()176 public void passwordComplexityMedium_shortSequentialPassword_invalid() { 177 assertCredentialInvalid( 178 PASSWORD_COMPLEXITY_MEDIUM, 179 createPassword(SHORT_SEQUENTIAL_PASSWORD), 180 passwordTooShort(4)); 181 } 182 183 @Test passwordComplexityMedium_mediumSequentialPassword_invalid()184 public void passwordComplexityMedium_mediumSequentialPassword_invalid() { 185 assertCredentialInvalid( 186 PASSWORD_COMPLEXITY_MEDIUM, 187 createPassword(MEDIUM_SEQUENTIAL_PASSWORD), 188 mContext.getString(R.string.lockpassword_pin_no_sequential_digits)); 189 } 190 191 @Test passwordComplexityMedium_mediumAlphanumericPassword_valid()192 public void passwordComplexityMedium_mediumAlphanumericPassword_valid() { 193 assertCredentialValid( 194 PASSWORD_COMPLEXITY_MEDIUM, 195 createPassword(MEDIUM_ALPHANUMERIC_PASSWORD)); 196 } 197 198 @Test passwordComplexityHigh_mediumSequentialPassword_invalid()199 public void passwordComplexityHigh_mediumSequentialPassword_invalid() { 200 assertCredentialInvalid( 201 PASSWORD_COMPLEXITY_HIGH, 202 createPassword(MEDIUM_SEQUENTIAL_PASSWORD), 203 passwordTooShort(6) + "\n" 204 + mContext.getString(R.string.lockpassword_pin_no_sequential_digits)); 205 } 206 207 @Test passwordComplexityHigh_mediumAlphanumericPassword_invalid()208 public void passwordComplexityHigh_mediumAlphanumericPassword_invalid() { 209 assertCredentialInvalid( 210 PASSWORD_COMPLEXITY_HIGH, 211 createPassword(MEDIUM_ALPHANUMERIC_PASSWORD), 212 passwordTooShort(6)); 213 } 214 215 @Test passwordComplexityHigh_longSequentialPassword_invalid()216 public void passwordComplexityHigh_longSequentialPassword_invalid() { 217 assertCredentialInvalid( 218 PASSWORD_COMPLEXITY_HIGH, 219 createPassword(LONG_SEQUENTIAL_PASSWORD), 220 mContext.getString(R.string.lockpassword_pin_no_sequential_digits)); 221 } 222 223 @Test passwordComplexityHigh_longAlphanumericPassword_valid()224 public void passwordComplexityHigh_longAlphanumericPassword_valid() { 225 assertCredentialValid(PASSWORD_COMPLEXITY_HIGH, createPassword(LONG_ALPHANUMERIC_PASSWORD)); 226 } 227 228 @Test pinComplexityNone_shortSequentialPassword_invalid()229 public void pinComplexityNone_shortSequentialPassword_invalid() { 230 assertCredentialInvalid( 231 PASSWORD_COMPLEXITY_NONE, 232 createPin(SHORT_SEQUENTIAL_PASSWORD), 233 pinTooShort(4)); 234 } 235 236 @Test pinComplexityNone_mediumSequentialPassword_valid()237 public void pinComplexityNone_mediumSequentialPassword_valid() { 238 assertCredentialValid(PASSWORD_COMPLEXITY_NONE, createPin(MEDIUM_SEQUENTIAL_PASSWORD)); 239 } 240 241 @Test pinComplexityLow_shortSequentialPassword_invalid()242 public void pinComplexityLow_shortSequentialPassword_invalid() { 243 assertCredentialInvalid( 244 PASSWORD_COMPLEXITY_LOW, 245 createPin(SHORT_SEQUENTIAL_PASSWORD), 246 pinTooShort(4)); 247 } 248 249 @Test pinComplexityLow_mediumSequentialPassword_valid()250 public void pinComplexityLow_mediumSequentialPassword_valid() { 251 assertCredentialValid(PASSWORD_COMPLEXITY_LOW, createPin(MEDIUM_SEQUENTIAL_PASSWORD)); 252 } 253 254 @Test pinComplexityMedium_shortSequentialPassword_invalid()255 public void pinComplexityMedium_shortSequentialPassword_invalid() { 256 assertCredentialInvalid( 257 PASSWORD_COMPLEXITY_MEDIUM, 258 createPin(SHORT_SEQUENTIAL_PASSWORD), 259 pinTooShort(4)); 260 } 261 262 @Test pinComplexityMedium_mediumSequentialPassword_invalid()263 public void pinComplexityMedium_mediumSequentialPassword_invalid() { 264 assertCredentialInvalid( 265 PASSWORD_COMPLEXITY_MEDIUM, 266 createPin(MEDIUM_SEQUENTIAL_PASSWORD), 267 mContext.getString(R.string.lockpassword_pin_no_sequential_digits)); 268 } 269 270 @Test pinComplexityMedium_mediumPinPassword_valid()271 public void pinComplexityMedium_mediumPinPassword_valid() { 272 assertCredentialValid(PASSWORD_COMPLEXITY_MEDIUM, createPin(MEDIUM_PIN_PASSWORD)); 273 } 274 275 @Test pinComplexityHigh_mediumPinPassword_invalid()276 public void pinComplexityHigh_mediumPinPassword_invalid() { 277 assertCredentialInvalid( 278 PASSWORD_COMPLEXITY_HIGH, 279 createPin(MEDIUM_PIN_PASSWORD), 280 pinTooShort(8)); 281 } 282 283 @Test pinComplexityHigh_longSequentialPassword_invalid()284 public void pinComplexityHigh_longSequentialPassword_invalid() { 285 assertCredentialInvalid( 286 PASSWORD_COMPLEXITY_HIGH, 287 createPin(LONG_SEQUENTIAL_PASSWORD), 288 mContext.getString(R.string.lockpassword_pin_no_sequential_digits)); 289 } 290 291 @Test pinComplexityHigh_longPinPassword_valid()292 public void pinComplexityHigh_longPinPassword_valid() { 293 assertCredentialValid(PASSWORD_COMPLEXITY_HIGH, createPin(LONG_PIN_PASSWORD)); 294 } 295 296 @Test patternComplexityNone_length3Pattern_invalid()297 public void patternComplexityNone_length3Pattern_invalid() { 298 assertCredentialInvalid( 299 PASSWORD_COMPLEXITY_NONE, 300 createPattern(LENGTH_3_PATTERN), 301 patternTooShort(4)); 302 } 303 304 @Test patternComplexityNone_length4Pattern_valid()305 public void patternComplexityNone_length4Pattern_valid() { 306 assertCredentialValid(PASSWORD_COMPLEXITY_NONE, createPattern(LENGTH_4_PATTERN)); 307 } 308 } 309