1/* 2 * Copyright (C) 2016 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 android.net.wifi; 18 19import static org.junit.Assert.assertArrayEquals; 20import static org.junit.Assert.assertEquals; 21import static org.junit.Assert.assertFalse; 22import static org.junit.Assert.assertNotNull; 23import static org.junit.Assert.assertNotEquals; 24import static org.junit.Assert.assertNull; 25import static org.junit.Assert.assertTrue; 26 27import android.net.wifi.WifiEnterpriseConfig.Eap; 28import android.net.wifi.WifiEnterpriseConfig.Phase2; 29import android.os.Parcel; 30import android.security.Credentials; 31import android.support.test.filters.SmallTest; 32 33import org.junit.Before; 34import org.junit.Test; 35 36import java.security.PrivateKey; 37import java.security.cert.X509Certificate; 38 39 40/** 41 * Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}. 42 */ 43@SmallTest 44public class WifiEnterpriseConfigTest { 45 // Maintain a ground truth of the keystore uri prefix which is expected by wpa_supplicant. 46 public static final String KEYSTORE_URI = "keystore://"; 47 public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; 48 public static final String KEYSTORES_URI = "keystores://"; 49 50 private WifiEnterpriseConfig mEnterpriseConfig; 51 52 @Before 53 public void setUp() throws Exception { 54 mEnterpriseConfig = new WifiEnterpriseConfig(); 55 } 56 57 @Test 58 public void testGetEmptyCaCertificate() { 59 // A newly-constructed WifiEnterpriseConfig object should have no CA certificate. 60 assertNull(mEnterpriseConfig.getCaCertificate()); 61 assertNull(mEnterpriseConfig.getCaCertificates()); 62 // Setting CA certificate to null explicitly. 63 mEnterpriseConfig.setCaCertificate(null); 64 assertNull(mEnterpriseConfig.getCaCertificate()); 65 // Setting CA certificate to null using setCaCertificates(). 66 mEnterpriseConfig.setCaCertificates(null); 67 assertNull(mEnterpriseConfig.getCaCertificates()); 68 // Setting CA certificate to zero-length array. 69 mEnterpriseConfig.setCaCertificates(new X509Certificate[0]); 70 assertNull(mEnterpriseConfig.getCaCertificates()); 71 } 72 73 @Test 74 public void testSetGetSingleCaCertificate() { 75 X509Certificate cert0 = FakeKeys.CA_CERT0; 76 mEnterpriseConfig.setCaCertificate(cert0); 77 assertEquals(mEnterpriseConfig.getCaCertificate(), cert0); 78 } 79 80 @Test 81 public void testSetGetMultipleCaCertificates() { 82 X509Certificate cert0 = FakeKeys.CA_CERT0; 83 X509Certificate cert1 = FakeKeys.CA_CERT1; 84 mEnterpriseConfig.setCaCertificates(new X509Certificate[] {cert0, cert1}); 85 X509Certificate[] result = mEnterpriseConfig.getCaCertificates(); 86 assertEquals(result.length, 2); 87 assertTrue(result[0] == cert0 && result[1] == cert1); 88 } 89 90 @Test 91 public void testSetClientKeyEntryWithNull() { 92 mEnterpriseConfig.setClientKeyEntry(null, null); 93 assertNull(mEnterpriseConfig.getClientCertificateChain()); 94 assertNull(mEnterpriseConfig.getClientCertificate()); 95 mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null); 96 assertNull(mEnterpriseConfig.getClientCertificateChain()); 97 assertNull(mEnterpriseConfig.getClientCertificate()); 98 99 // Setting the client certificate to null should clear the existing chain. 100 PrivateKey clientKey = FakeKeys.RSA_KEY1; 101 X509Certificate clientCert0 = FakeKeys.CLIENT_CERT; 102 X509Certificate clientCert1 = FakeKeys.CA_CERT1; 103 mEnterpriseConfig.setClientKeyEntry(clientKey, clientCert0); 104 assertNotNull(mEnterpriseConfig.getClientCertificate()); 105 mEnterpriseConfig.setClientKeyEntry(null, null); 106 assertNull(mEnterpriseConfig.getClientCertificate()); 107 assertNull(mEnterpriseConfig.getClientCertificateChain()); 108 109 // Setting the chain to null should clear the existing chain. 110 X509Certificate[] clientChain = new X509Certificate[] {clientCert0, clientCert1}; 111 mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); 112 assertNotNull(mEnterpriseConfig.getClientCertificateChain()); 113 mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null); 114 assertNull(mEnterpriseConfig.getClientCertificate()); 115 assertNull(mEnterpriseConfig.getClientCertificateChain()); 116 } 117 118 @Test 119 public void testSetClientCertificateChain() { 120 PrivateKey clientKey = FakeKeys.RSA_KEY1; 121 X509Certificate cert0 = FakeKeys.CLIENT_CERT; 122 X509Certificate cert1 = FakeKeys.CA_CERT1; 123 X509Certificate[] clientChain = new X509Certificate[] {cert0, cert1}; 124 mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); 125 X509Certificate[] result = mEnterpriseConfig.getClientCertificateChain(); 126 assertEquals(result.length, 2); 127 assertTrue(result[0] == cert0 && result[1] == cert1); 128 assertTrue(mEnterpriseConfig.getClientCertificate() == cert0); 129 } 130 131 private boolean isClientCertificateChainInvalid(X509Certificate[] clientChain) { 132 boolean exceptionThrown = false; 133 try { 134 PrivateKey clientKey = FakeKeys.RSA_KEY1; 135 mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); 136 } catch (IllegalArgumentException e) { 137 exceptionThrown = true; 138 } 139 return exceptionThrown; 140 } 141 142 @Test 143 public void testSetInvalidClientCertificateChain() { 144 X509Certificate clientCert = FakeKeys.CLIENT_CERT; 145 X509Certificate caCert = FakeKeys.CA_CERT1; 146 assertTrue("Invalid client certificate", 147 isClientCertificateChainInvalid(new X509Certificate[] {caCert, caCert})); 148 assertTrue("Invalid CA certificate", 149 isClientCertificateChainInvalid(new X509Certificate[] {clientCert, clientCert})); 150 assertTrue("Both certificates invalid", 151 isClientCertificateChainInvalid(new X509Certificate[] {caCert, clientCert})); 152 } 153 154 @Test 155 public void testSaveSingleCaCertificateAlias() { 156 final String alias = "single_alias 0"; 157 mEnterpriseConfig.setCaCertificateAliases(new String[] {alias}); 158 assertEquals(getCaCertField(), CA_CERT_PREFIX + alias); 159 } 160 161 @Test 162 public void testLoadSingleCaCertificateAlias() { 163 final String alias = "single_alias 1"; 164 setCaCertField(CA_CERT_PREFIX + alias); 165 String[] aliases = mEnterpriseConfig.getCaCertificateAliases(); 166 assertEquals(aliases.length, 1); 167 assertEquals(aliases[0], alias); 168 } 169 170 @Test 171 public void testSaveMultipleCaCertificates() { 172 final String alias0 = "single_alias 0"; 173 final String alias1 = "single_alias 1"; 174 mEnterpriseConfig.setCaCertificateAliases(new String[] {alias0, alias1}); 175 assertEquals(getCaCertField(), String.format("%s%s %s", 176 KEYSTORES_URI, 177 WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias0), 178 WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias1))); 179 } 180 181 @Test 182 public void testLoadMultipleCaCertificates() { 183 final String alias0 = "single_alias 0"; 184 final String alias1 = "single_alias 1"; 185 setCaCertField(String.format("%s%s %s", 186 KEYSTORES_URI, 187 WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias0), 188 WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias1))); 189 String[] aliases = mEnterpriseConfig.getCaCertificateAliases(); 190 assertEquals(aliases.length, 2); 191 assertEquals(aliases[0], alias0); 192 assertEquals(aliases[1], alias1); 193 } 194 195 private String getCaCertField() { 196 return mEnterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY); 197 } 198 199 private void setCaCertField(String value) { 200 mEnterpriseConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, value); 201 } 202 203 // Retrieves the value for a specific key supplied to wpa_supplicant. 204 private class SupplicantConfigExtractor implements WifiEnterpriseConfig.SupplicantSaver { 205 private String mValue = null; 206 private String mKey; 207 208 SupplicantConfigExtractor(String key) { 209 mKey = key; 210 } 211 212 @Override 213 public boolean saveValue(String key, String value) { 214 if (key.equals(mKey)) { 215 mValue = value; 216 } 217 return true; 218 } 219 220 public String getValue() { 221 return mValue; 222 } 223 } 224 225 private String getSupplicantEapMethod() { 226 SupplicantConfigExtractor entryExtractor = new SupplicantConfigExtractor( 227 WifiEnterpriseConfig.EAP_KEY); 228 mEnterpriseConfig.saveToSupplicant(entryExtractor); 229 return entryExtractor.getValue(); 230 } 231 232 private String getSupplicantPhase2Method() { 233 SupplicantConfigExtractor entryExtractor = new SupplicantConfigExtractor( 234 WifiEnterpriseConfig.PHASE2_KEY); 235 mEnterpriseConfig.saveToSupplicant(entryExtractor); 236 return entryExtractor.getValue(); 237 } 238 239 /** Verifies the default value for EAP outer and inner methods */ 240 @Test 241 public void eapInnerDefault() { 242 assertEquals(null, getSupplicantEapMethod()); 243 assertEquals(null, getSupplicantPhase2Method()); 244 } 245 246 /** Verifies that the EAP inner method is reset when we switch to TLS */ 247 @Test 248 public void eapPhase2MethodForTls() { 249 // Initially select an EAP method that supports an phase2. 250 mEnterpriseConfig.setEapMethod(Eap.PEAP); 251 mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); 252 assertEquals("PEAP", getSupplicantEapMethod()); 253 assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method()); 254 255 // Change the EAP method to another type which supports a phase2. 256 mEnterpriseConfig.setEapMethod(Eap.TTLS); 257 assertEquals("TTLS", getSupplicantEapMethod()); 258 assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method()); 259 260 // Change the EAP method to TLS which does not support a phase2. 261 mEnterpriseConfig.setEapMethod(Eap.TLS); 262 assertEquals(null, getSupplicantPhase2Method()); 263 } 264 265 /** Verfies that the EAP inner method is reset when we switch phase2 to NONE */ 266 @Test 267 public void eapPhase2None() { 268 // Initially select an EAP method that supports an phase2. 269 mEnterpriseConfig.setEapMethod(Eap.PEAP); 270 mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); 271 assertEquals("PEAP", getSupplicantEapMethod()); 272 assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method()); 273 274 // Change the phase2 method to NONE and ensure the value is cleared. 275 mEnterpriseConfig.setPhase2Method(Phase2.NONE); 276 assertEquals(null, getSupplicantPhase2Method()); 277 } 278 279 /** Verfies that the correct "autheap" parameter is supplied for TTLS/GTC. */ 280 @Test 281 public void peapGtcToTtls() { 282 mEnterpriseConfig.setEapMethod(Eap.PEAP); 283 mEnterpriseConfig.setPhase2Method(Phase2.GTC); 284 assertEquals("PEAP", getSupplicantEapMethod()); 285 assertEquals("\"auth=GTC\"", getSupplicantPhase2Method()); 286 287 mEnterpriseConfig.setEapMethod(Eap.TTLS); 288 assertEquals("TTLS", getSupplicantEapMethod()); 289 assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method()); 290 } 291 292 /** Verfies that the correct "auth" parameter is supplied for PEAP/GTC. */ 293 @Test 294 public void ttlsGtcToPeap() { 295 mEnterpriseConfig.setEapMethod(Eap.TTLS); 296 mEnterpriseConfig.setPhase2Method(Phase2.GTC); 297 assertEquals("TTLS", getSupplicantEapMethod()); 298 assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method()); 299 300 mEnterpriseConfig.setEapMethod(Eap.PEAP); 301 assertEquals("PEAP", getSupplicantEapMethod()); 302 assertEquals("\"auth=GTC\"", getSupplicantPhase2Method()); 303 } 304 305 /** Verfies PEAP/SIM, PEAP/AKA, PEAP/AKA'. */ 306 @Test 307 public void peapSimAkaAkaPrime() { 308 mEnterpriseConfig.setEapMethod(Eap.PEAP); 309 mEnterpriseConfig.setPhase2Method(Phase2.SIM); 310 assertEquals("PEAP", getSupplicantEapMethod()); 311 assertEquals("\"auth=SIM\"", getSupplicantPhase2Method()); 312 313 mEnterpriseConfig.setPhase2Method(Phase2.AKA); 314 assertEquals("\"auth=AKA\"", getSupplicantPhase2Method()); 315 316 mEnterpriseConfig.setPhase2Method(Phase2.AKA_PRIME); 317 assertEquals("\"auth=AKA'\"", getSupplicantPhase2Method()); 318 } 319 320 /** 321 * Verifies that the copy constructor preseves both the masked password and inner method 322 * information. 323 */ 324 @Test 325 public void copyConstructor() { 326 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 327 enterpriseConfig.setPassword("*"); 328 enterpriseConfig.setEapMethod(Eap.TTLS); 329 enterpriseConfig.setPhase2Method(Phase2.GTC); 330 mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); 331 assertEquals("TTLS", getSupplicantEapMethod()); 332 assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method()); 333 assertEquals("*", mEnterpriseConfig.getPassword()); 334 } 335 336 /** 337 * Verifies that the copy from external ignores masked passwords and preserves the 338 * inner method information. 339 */ 340 @Test 341 public void copyFromExternal() { 342 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 343 enterpriseConfig.setPassword("*"); 344 enterpriseConfig.setEapMethod(Eap.TTLS); 345 enterpriseConfig.setPhase2Method(Phase2.GTC); 346 mEnterpriseConfig = new WifiEnterpriseConfig(); 347 mEnterpriseConfig.copyFromExternal(enterpriseConfig, "*"); 348 assertEquals("TTLS", getSupplicantEapMethod()); 349 assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method()); 350 assertNotEquals("*", mEnterpriseConfig.getPassword()); 351 } 352 353 /** Verfies that parceling a WifiEnterpriseConfig preseves method information. */ 354 @Test 355 public void parcelConstructor() { 356 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 357 enterpriseConfig.setEapMethod(Eap.TTLS); 358 enterpriseConfig.setPhase2Method(Phase2.GTC); 359 Parcel parcel = Parcel.obtain(); 360 enterpriseConfig.writeToParcel(parcel, 0); 361 parcel.setDataPosition(0); // Allow parcel to be read from the beginning. 362 mEnterpriseConfig = WifiEnterpriseConfig.CREATOR.createFromParcel(parcel); 363 assertEquals("TTLS", getSupplicantEapMethod()); 364 assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method()); 365 } 366 367 /** 368 * Verifies that parceling a WifiEnterpriseConfig preserves the key 369 * and certificates information. 370 */ 371 @Test 372 public void parcelConfigWithKeyAndCerts() throws Exception { 373 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 374 PrivateKey clientKey = FakeKeys.RSA_KEY1; 375 X509Certificate clientCert = FakeKeys.CLIENT_CERT; 376 X509Certificate[] caCerts = new X509Certificate[] {FakeKeys.CA_CERT0, FakeKeys.CA_CERT1}; 377 enterpriseConfig.setClientKeyEntry(clientKey, clientCert); 378 enterpriseConfig.setCaCertificates(caCerts); 379 Parcel parcel = Parcel.obtain(); 380 enterpriseConfig.writeToParcel(parcel, 0); 381 382 parcel.setDataPosition(0); // Allow parcel to be read from the beginning. 383 mEnterpriseConfig = WifiEnterpriseConfig.CREATOR.createFromParcel(parcel); 384 PrivateKey actualClientKey = mEnterpriseConfig.getClientPrivateKey(); 385 X509Certificate actualClientCert = mEnterpriseConfig.getClientCertificate(); 386 X509Certificate[] actualCaCerts = mEnterpriseConfig.getCaCertificates(); 387 388 /* Verify client private key. */ 389 assertNotNull(actualClientKey); 390 assertEquals(clientKey.getAlgorithm(), actualClientKey.getAlgorithm()); 391 assertArrayEquals(clientKey.getEncoded(), actualClientKey.getEncoded()); 392 393 /* Verify client certificate. */ 394 assertNotNull(actualClientCert); 395 assertArrayEquals(clientCert.getEncoded(), actualClientCert.getEncoded()); 396 397 /* Verify CA certificates. */ 398 assertNotNull(actualCaCerts); 399 assertEquals(caCerts.length, actualCaCerts.length); 400 for (int i = 0; i < caCerts.length; i++) { 401 assertNotNull(actualCaCerts[i]); 402 assertArrayEquals(caCerts[i].getEncoded(), actualCaCerts[i].getEncoded()); 403 } 404 } 405 406 /** Verifies proper operation of the getKeyId() method. */ 407 @Test 408 public void getKeyId() { 409 assertEquals("NULL", mEnterpriseConfig.getKeyId(null)); 410 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 411 enterpriseConfig.setEapMethod(Eap.TTLS); 412 enterpriseConfig.setPhase2Method(Phase2.GTC); 413 assertEquals("TTLS_GTC", mEnterpriseConfig.getKeyId(enterpriseConfig)); 414 mEnterpriseConfig.setEapMethod(Eap.PEAP); 415 mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); 416 assertEquals("PEAP_MSCHAPV2", mEnterpriseConfig.getKeyId(enterpriseConfig)); 417 } 418 419 /** Verifies that passwords are not displayed in toString. */ 420 @Test 421 public void passwordNotInToString() { 422 String password = "supersecret"; 423 mEnterpriseConfig.setPassword(password); 424 assertFalse(mEnterpriseConfig.toString().contains(password)); 425 } 426} 427