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