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 17 package com.android.server.wifi.hotspot2; 18 19 import android.net.wifi.EAPConstants; 20 import android.net.wifi.WifiConfiguration; 21 import android.net.wifi.WifiEnterpriseConfig; 22 import android.net.wifi.hotspot2.PasspointConfiguration; 23 import android.net.wifi.hotspot2.pps.Credential; 24 import android.net.wifi.hotspot2.pps.Credential.SimCredential; 25 import android.net.wifi.hotspot2.pps.Credential.UserCredential; 26 import android.net.wifi.hotspot2.pps.HomeSp; 27 import android.security.Credentials; 28 import android.text.TextUtils; 29 import android.util.Base64; 30 import android.util.Log; 31 32 import com.android.server.wifi.IMSIParameter; 33 import com.android.server.wifi.SIMAccessor; 34 import com.android.server.wifi.WifiKeyStore; 35 import com.android.server.wifi.hotspot2.anqp.ANQPElement; 36 import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; 37 import com.android.server.wifi.hotspot2.anqp.DomainNameElement; 38 import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; 39 import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement; 40 import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement; 41 import com.android.server.wifi.hotspot2.anqp.eap.AuthParam; 42 import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth; 43 44 import java.nio.charset.StandardCharsets; 45 import java.security.MessageDigest; 46 import java.security.NoSuchAlgorithmException; 47 import java.security.cert.CertificateEncodingException; 48 import java.security.cert.X509Certificate; 49 import java.util.Arrays; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Objects; 53 54 /** 55 * Abstraction for Passpoint service provider. This class contains the both static 56 * Passpoint configuration data and the runtime data (e.g. blacklisted SSIDs, statistics). 57 */ 58 public class PasspointProvider { 59 private static final String TAG = "PasspointProvider"; 60 61 /** 62 * Used as part of alias string for certificates and keys. The alias string is in the format 63 * of: [KEY_TYPE]_HS2_[ProviderID] 64 * For example: "CACERT_HS2_0", "USRCERT_HS2_0", "USRPKEY_HS2_0" 65 */ 66 private static final String ALIAS_HS_TYPE = "HS2_"; 67 68 private final PasspointConfiguration mConfig; 69 private final WifiKeyStore mKeyStore; 70 71 /** 72 * Aliases for the private keys and certificates installed in the keystore. Each alias 73 * is a suffix of the actual certificate or key name installed in the keystore. The 74 * certificate or key name in the keystore is consist of |Type|_|alias|. 75 * This will be consistent with the usage of the term "alias" in {@link WifiEnterpriseConfig}. 76 */ 77 private String mCaCertificateAlias; 78 private String mClientPrivateKeyAlias; 79 private String mClientCertificateAlias; 80 81 private final long mProviderId; 82 private final int mCreatorUid; 83 84 private final IMSIParameter mImsiParameter; 85 private final List<String> mMatchingSIMImsiList; 86 87 private final int mEAPMethodID; 88 private final AuthParam mAuthParam; 89 90 private boolean mHasEverConnected; 91 92 public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, 93 SIMAccessor simAccessor, long providerId, int creatorUid) { 94 this(config, keyStore, simAccessor, providerId, creatorUid, null, null, null, false); 95 } 96 97 public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, 98 SIMAccessor simAccessor, long providerId, int creatorUid, String caCertificateAlias, 99 String clientCertificateAlias, String clientPrivateKeyAlias, 100 boolean hasEverConnected) { 101 // Maintain a copy of the configuration to avoid it being updated by others. 102 mConfig = new PasspointConfiguration(config); 103 mKeyStore = keyStore; 104 mProviderId = providerId; 105 mCreatorUid = creatorUid; 106 mCaCertificateAlias = caCertificateAlias; 107 mClientCertificateAlias = clientCertificateAlias; 108 mClientPrivateKeyAlias = clientPrivateKeyAlias; 109 mHasEverConnected = hasEverConnected; 110 111 // Setup EAP method and authentication parameter based on the credential. 112 if (mConfig.getCredential().getUserCredential() != null) { 113 mEAPMethodID = EAPConstants.EAP_TTLS; 114 mAuthParam = new NonEAPInnerAuth(NonEAPInnerAuth.getAuthTypeID( 115 mConfig.getCredential().getUserCredential().getNonEapInnerMethod())); 116 mImsiParameter = null; 117 mMatchingSIMImsiList = null; 118 } else if (mConfig.getCredential().getCertCredential() != null) { 119 mEAPMethodID = EAPConstants.EAP_TLS; 120 mAuthParam = null; 121 mImsiParameter = null; 122 mMatchingSIMImsiList = null; 123 } else { 124 mEAPMethodID = mConfig.getCredential().getSimCredential().getEapType(); 125 mAuthParam = null; 126 mImsiParameter = IMSIParameter.build( 127 mConfig.getCredential().getSimCredential().getImsi()); 128 mMatchingSIMImsiList = simAccessor.getMatchingImsis(mImsiParameter); 129 } 130 } 131 132 public PasspointConfiguration getConfig() { 133 // Return a copy of the configuration to avoid it being updated by others. 134 return new PasspointConfiguration(mConfig); 135 } 136 137 public String getCaCertificateAlias() { 138 return mCaCertificateAlias; 139 } 140 141 public String getClientPrivateKeyAlias() { 142 return mClientPrivateKeyAlias; 143 } 144 145 public String getClientCertificateAlias() { 146 return mClientCertificateAlias; 147 } 148 149 public long getProviderId() { 150 return mProviderId; 151 } 152 153 public int getCreatorUid() { 154 return mCreatorUid; 155 } 156 157 public boolean getHasEverConnected() { 158 return mHasEverConnected; 159 } 160 161 public void setHasEverConnected(boolean hasEverConnected) { 162 mHasEverConnected = hasEverConnected; 163 } 164 165 /** 166 * Install certificates and key based on current configuration. 167 * Note: the certificates and keys in the configuration will get cleared once 168 * they're installed in the keystore. 169 * 170 * @return true on success 171 */ 172 public boolean installCertsAndKeys() { 173 // Install CA certificate. 174 if (mConfig.getCredential().getCaCertificate() != null) { 175 String certName = Credentials.CA_CERTIFICATE + ALIAS_HS_TYPE + mProviderId; 176 if (!mKeyStore.putCertInKeyStore(certName, 177 mConfig.getCredential().getCaCertificate())) { 178 Log.e(TAG, "Failed to install CA Certificate"); 179 uninstallCertsAndKeys(); 180 return false; 181 } 182 mCaCertificateAlias = ALIAS_HS_TYPE + mProviderId; 183 } 184 185 // Install the client private key. 186 if (mConfig.getCredential().getClientPrivateKey() != null) { 187 String keyName = Credentials.USER_PRIVATE_KEY + ALIAS_HS_TYPE + mProviderId; 188 if (!mKeyStore.putKeyInKeyStore(keyName, 189 mConfig.getCredential().getClientPrivateKey())) { 190 Log.e(TAG, "Failed to install client private key"); 191 uninstallCertsAndKeys(); 192 return false; 193 } 194 mClientPrivateKeyAlias = ALIAS_HS_TYPE + mProviderId; 195 } 196 197 // Install the client certificate. 198 if (mConfig.getCredential().getClientCertificateChain() != null) { 199 X509Certificate clientCert = getClientCertificate( 200 mConfig.getCredential().getClientCertificateChain(), 201 mConfig.getCredential().getCertCredential().getCertSha256Fingerprint()); 202 if (clientCert == null) { 203 Log.e(TAG, "Failed to locate client certificate"); 204 uninstallCertsAndKeys(); 205 return false; 206 } 207 String certName = Credentials.USER_CERTIFICATE + ALIAS_HS_TYPE + mProviderId; 208 if (!mKeyStore.putCertInKeyStore(certName, clientCert)) { 209 Log.e(TAG, "Failed to install client certificate"); 210 uninstallCertsAndKeys(); 211 return false; 212 } 213 mClientCertificateAlias = ALIAS_HS_TYPE + mProviderId; 214 } 215 216 // Clear the keys and certificates in the configuration. 217 mConfig.getCredential().setCaCertificate(null); 218 mConfig.getCredential().setClientPrivateKey(null); 219 mConfig.getCredential().setClientCertificateChain(null); 220 return true; 221 } 222 223 /** 224 * Remove any installed certificates and key. 225 */ 226 public void uninstallCertsAndKeys() { 227 if (mCaCertificateAlias != null) { 228 if (!mKeyStore.removeEntryFromKeyStore( 229 Credentials.CA_CERTIFICATE + mCaCertificateAlias)) { 230 Log.e(TAG, "Failed to remove entry: " + mCaCertificateAlias); 231 } 232 mCaCertificateAlias = null; 233 } 234 if (mClientPrivateKeyAlias != null) { 235 if (!mKeyStore.removeEntryFromKeyStore( 236 Credentials.USER_PRIVATE_KEY + mClientPrivateKeyAlias)) { 237 Log.e(TAG, "Failed to remove entry: " + mClientPrivateKeyAlias); 238 } 239 mClientPrivateKeyAlias = null; 240 } 241 if (mClientCertificateAlias != null) { 242 if (!mKeyStore.removeEntryFromKeyStore( 243 Credentials.USER_CERTIFICATE + mClientCertificateAlias)) { 244 Log.e(TAG, "Failed to remove entry: " + mClientCertificateAlias); 245 } 246 mClientCertificateAlias = null; 247 } 248 } 249 250 /** 251 * Return the matching status with the given AP, based on the ANQP elements from the AP. 252 * 253 * @param anqpElements ANQP elements from the AP 254 * @return {@link PasspointMatch} 255 */ 256 public PasspointMatch match(Map<ANQPElementType, ANQPElement> anqpElements) { 257 PasspointMatch providerMatch = matchProvider(anqpElements); 258 259 // Perform authentication match against the NAI Realm. 260 int authMatch = ANQPMatcher.matchNAIRealm( 261 (NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm), 262 mConfig.getCredential().getRealm(), mEAPMethodID, mAuthParam); 263 264 // Auth mismatch, demote provider match. 265 if (authMatch == AuthMatch.NONE) { 266 return PasspointMatch.None; 267 } 268 269 // No realm match, return provider match as is. 270 if ((authMatch & AuthMatch.REALM) == 0) { 271 return providerMatch; 272 } 273 274 // Realm match, promote provider match to roaming if no other provider match is found. 275 return providerMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider 276 : providerMatch; 277 } 278 279 /** 280 * Generate a WifiConfiguration based on the provider's configuration. The generated 281 * WifiConfiguration will include all the necessary credentials for network connection except 282 * the SSID, which should be added by the caller when the config is being used for network 283 * connection. 284 * 285 * @return {@link WifiConfiguration} 286 */ 287 public WifiConfiguration getWifiConfig() { 288 WifiConfiguration wifiConfig = new WifiConfiguration(); 289 wifiConfig.FQDN = mConfig.getHomeSp().getFqdn(); 290 if (mConfig.getHomeSp().getRoamingConsortiumOis() != null) { 291 wifiConfig.roamingConsortiumIds = Arrays.copyOf( 292 mConfig.getHomeSp().getRoamingConsortiumOis(), 293 mConfig.getHomeSp().getRoamingConsortiumOis().length); 294 } 295 wifiConfig.providerFriendlyName = mConfig.getHomeSp().getFriendlyName(); 296 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 297 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); 298 299 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 300 enterpriseConfig.setRealm(mConfig.getCredential().getRealm()); 301 enterpriseConfig.setDomainSuffixMatch(mConfig.getHomeSp().getFqdn()); 302 if (mConfig.getCredential().getUserCredential() != null) { 303 buildEnterpriseConfigForUserCredential(enterpriseConfig, 304 mConfig.getCredential().getUserCredential()); 305 setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); 306 } else if (mConfig.getCredential().getCertCredential() != null) { 307 buildEnterpriseConfigForCertCredential(enterpriseConfig); 308 setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); 309 } else { 310 buildEnterpriseConfigForSimCredential(enterpriseConfig, 311 mConfig.getCredential().getSimCredential()); 312 } 313 wifiConfig.enterpriseConfig = enterpriseConfig; 314 return wifiConfig; 315 } 316 317 /** 318 * @return true if provider is backed by a SIM credential. 319 */ 320 public boolean isSimCredential() { 321 return mConfig.getCredential().getSimCredential() != null; 322 } 323 324 /** 325 * Convert a legacy {@link WifiConfiguration} representation of a Passpoint configuration to 326 * a {@link PasspointConfiguration}. This is used for migrating legacy Passpoint 327 * configuration (release N and older). 328 * 329 * @param wifiConfig The {@link WifiConfiguration} to convert 330 * @return {@link PasspointConfiguration} 331 */ 332 public static PasspointConfiguration convertFromWifiConfig(WifiConfiguration wifiConfig) { 333 PasspointConfiguration passpointConfig = new PasspointConfiguration(); 334 335 // Setup HomeSP. 336 HomeSp homeSp = new HomeSp(); 337 if (TextUtils.isEmpty(wifiConfig.FQDN)) { 338 Log.e(TAG, "Missing FQDN"); 339 return null; 340 } 341 homeSp.setFqdn(wifiConfig.FQDN); 342 homeSp.setFriendlyName(wifiConfig.providerFriendlyName); 343 if (wifiConfig.roamingConsortiumIds != null) { 344 homeSp.setRoamingConsortiumOis(Arrays.copyOf( 345 wifiConfig.roamingConsortiumIds, wifiConfig.roamingConsortiumIds.length)); 346 } 347 passpointConfig.setHomeSp(homeSp); 348 349 // Setup Credential. 350 Credential credential = new Credential(); 351 credential.setRealm(wifiConfig.enterpriseConfig.getRealm()); 352 switch (wifiConfig.enterpriseConfig.getEapMethod()) { 353 case WifiEnterpriseConfig.Eap.TTLS: 354 credential.setUserCredential(buildUserCredentialFromEnterpriseConfig( 355 wifiConfig.enterpriseConfig)); 356 break; 357 case WifiEnterpriseConfig.Eap.TLS: 358 Credential.CertificateCredential certCred = new Credential.CertificateCredential(); 359 certCred.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3); 360 credential.setCertCredential(certCred); 361 break; 362 case WifiEnterpriseConfig.Eap.SIM: 363 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 364 EAPConstants.EAP_SIM, wifiConfig.enterpriseConfig)); 365 break; 366 case WifiEnterpriseConfig.Eap.AKA: 367 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 368 EAPConstants.EAP_AKA, wifiConfig.enterpriseConfig)); 369 break; 370 case WifiEnterpriseConfig.Eap.AKA_PRIME: 371 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 372 EAPConstants.EAP_AKA_PRIME, wifiConfig.enterpriseConfig)); 373 break; 374 default: 375 Log.e(TAG, "Unsupport EAP method: " + wifiConfig.enterpriseConfig.getEapMethod()); 376 return null; 377 } 378 if (credential.getUserCredential() == null && credential.getCertCredential() == null 379 && credential.getSimCredential() == null) { 380 Log.e(TAG, "Missing credential"); 381 return null; 382 } 383 passpointConfig.setCredential(credential); 384 385 return passpointConfig; 386 } 387 388 @Override 389 public boolean equals(Object thatObject) { 390 if (this == thatObject) { 391 return true; 392 } 393 if (!(thatObject instanceof PasspointProvider)) { 394 return false; 395 } 396 PasspointProvider that = (PasspointProvider) thatObject; 397 return mProviderId == that.mProviderId 398 && TextUtils.equals(mCaCertificateAlias, that.mCaCertificateAlias) 399 && TextUtils.equals(mClientCertificateAlias, that.mClientCertificateAlias) 400 && TextUtils.equals(mClientPrivateKeyAlias, that.mClientPrivateKeyAlias) 401 && (mConfig == null ? that.mConfig == null : mConfig.equals(that.mConfig)); 402 } 403 404 @Override 405 public int hashCode() { 406 return Objects.hash(mProviderId, mCaCertificateAlias, mClientCertificateAlias, 407 mClientPrivateKeyAlias, mConfig); 408 } 409 410 @Override 411 public String toString() { 412 StringBuilder builder = new StringBuilder(); 413 builder.append("ProviderId: ").append(mProviderId).append("\n"); 414 builder.append("CreatorUID: ").append(mCreatorUid).append("\n"); 415 builder.append("Configuration Begin ---\n"); 416 builder.append(mConfig); 417 builder.append("Configuration End ---\n"); 418 return builder.toString(); 419 } 420 421 /** 422 * Retrieve the client certificate from the certificates chain. The certificate 423 * with the matching SHA256 digest is the client certificate. 424 * 425 * @param certChain The client certificates chain 426 * @param expectedSha256Fingerprint The expected SHA256 digest of the client certificate 427 * @return {@link java.security.cert.X509Certificate} 428 */ 429 private static X509Certificate getClientCertificate(X509Certificate[] certChain, 430 byte[] expectedSha256Fingerprint) { 431 if (certChain == null) { 432 return null; 433 } 434 try { 435 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 436 for (X509Certificate certificate : certChain) { 437 digester.reset(); 438 byte[] fingerprint = digester.digest(certificate.getEncoded()); 439 if (Arrays.equals(expectedSha256Fingerprint, fingerprint)) { 440 return certificate; 441 } 442 } 443 } catch (CertificateEncodingException | NoSuchAlgorithmException e) { 444 return null; 445 } 446 447 return null; 448 } 449 450 /** 451 * Perform a provider match based on the given ANQP elements. 452 * 453 * @param anqpElements List of ANQP elements 454 * @return {@link PasspointMatch} 455 */ 456 private PasspointMatch matchProvider(Map<ANQPElementType, ANQPElement> anqpElements) { 457 // Domain name matching. 458 if (ANQPMatcher.matchDomainName( 459 (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), 460 mConfig.getHomeSp().getFqdn(), mImsiParameter, mMatchingSIMImsiList)) { 461 return PasspointMatch.HomeProvider; 462 } 463 464 // Roaming Consortium OI matching. 465 if (ANQPMatcher.matchRoamingConsortium( 466 (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium), 467 mConfig.getHomeSp().getRoamingConsortiumOis())) { 468 return PasspointMatch.RoamingProvider; 469 } 470 471 // 3GPP Network matching. 472 if (ANQPMatcher.matchThreeGPPNetwork( 473 (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork), 474 mImsiParameter, mMatchingSIMImsiList)) { 475 return PasspointMatch.RoamingProvider; 476 } 477 return PasspointMatch.None; 478 } 479 480 /** 481 * Fill in WifiEnterpriseConfig with information from an user credential. 482 * 483 * @param config Instance of {@link WifiEnterpriseConfig} 484 * @param credential Instance of {@link UserCredential} 485 */ 486 private void buildEnterpriseConfigForUserCredential(WifiEnterpriseConfig config, 487 Credential.UserCredential credential) { 488 byte[] pwOctets = Base64.decode(credential.getPassword(), Base64.DEFAULT); 489 String decodedPassword = new String(pwOctets, StandardCharsets.UTF_8); 490 config.setEapMethod(WifiEnterpriseConfig.Eap.TTLS); 491 config.setIdentity(credential.getUsername()); 492 config.setPassword(decodedPassword); 493 config.setCaCertificateAlias(mCaCertificateAlias); 494 int phase2Method = WifiEnterpriseConfig.Phase2.NONE; 495 switch (credential.getNonEapInnerMethod()) { 496 case Credential.UserCredential.AUTH_METHOD_PAP: 497 phase2Method = WifiEnterpriseConfig.Phase2.PAP; 498 break; 499 case Credential.UserCredential.AUTH_METHOD_MSCHAP: 500 phase2Method = WifiEnterpriseConfig.Phase2.MSCHAP; 501 break; 502 case Credential.UserCredential.AUTH_METHOD_MSCHAPV2: 503 phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2; 504 break; 505 default: 506 // Should never happen since this is already validated when the provider is 507 // added. 508 Log.wtf(TAG, "Unsupported Auth: " + credential.getNonEapInnerMethod()); 509 break; 510 } 511 config.setPhase2Method(phase2Method); 512 } 513 514 /** 515 * Fill in WifiEnterpriseConfig with information from a certificate credential. 516 * 517 * @param config Instance of {@link WifiEnterpriseConfig} 518 */ 519 private void buildEnterpriseConfigForCertCredential(WifiEnterpriseConfig config) { 520 config.setEapMethod(WifiEnterpriseConfig.Eap.TLS); 521 config.setClientCertificateAlias(mClientCertificateAlias); 522 config.setCaCertificateAlias(mCaCertificateAlias); 523 } 524 525 /** 526 * Fill in WifiEnterpriseConfig with information from a SIM credential. 527 * 528 * @param config Instance of {@link WifiEnterpriseConfig} 529 * @param credential Instance of {@link SimCredential} 530 */ 531 private void buildEnterpriseConfigForSimCredential(WifiEnterpriseConfig config, 532 Credential.SimCredential credential) { 533 int eapMethod = WifiEnterpriseConfig.Eap.NONE; 534 switch(credential.getEapType()) { 535 case EAPConstants.EAP_SIM: 536 eapMethod = WifiEnterpriseConfig.Eap.SIM; 537 break; 538 case EAPConstants.EAP_AKA: 539 eapMethod = WifiEnterpriseConfig.Eap.AKA; 540 break; 541 case EAPConstants.EAP_AKA_PRIME: 542 eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME; 543 break; 544 default: 545 // Should never happen since this is already validated when the provider is 546 // added. 547 Log.wtf(TAG, "Unsupported EAP Method: " + credential.getEapType()); 548 break; 549 } 550 config.setEapMethod(eapMethod); 551 config.setPlmn(credential.getImsi()); 552 } 553 554 private static void setAnonymousIdentityToNaiRealm(WifiEnterpriseConfig config, String realm) { 555 /** 556 * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so 557 * that this value will be sent to the EAP server as part of the EAP-Response/ Identity 558 * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity 559 * packet, and revert to using the (real) identity field for subsequent transactions that 560 * request an identity (e.g. in EAP-TTLS). 561 * 562 * This NAI realm value (the portion of the identity after the '@') is used to tell the 563 * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a 564 * placeholder that is not used--it is set to this value by convention. See Section 5.1 of 565 * RFC3748 for more details. 566 * 567 * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the 568 * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to 569 * identify the device. 570 */ 571 config.setAnonymousIdentity("anonymous@" + realm); 572 } 573 574 /** 575 * Helper function for creating a 576 * {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from the given 577 * {@link WifiEnterpriseConfig} 578 * 579 * @param config The enterprise configuration containing the credential 580 * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} 581 */ 582 private static Credential.UserCredential buildUserCredentialFromEnterpriseConfig( 583 WifiEnterpriseConfig config) { 584 Credential.UserCredential userCredential = new Credential.UserCredential(); 585 userCredential.setEapType(EAPConstants.EAP_TTLS); 586 587 if (TextUtils.isEmpty(config.getIdentity())) { 588 Log.e(TAG, "Missing username for user credential"); 589 return null; 590 } 591 userCredential.setUsername(config.getIdentity()); 592 593 if (TextUtils.isEmpty(config.getPassword())) { 594 Log.e(TAG, "Missing password for user credential"); 595 return null; 596 } 597 String encodedPassword = 598 new String(Base64.encode(config.getPassword().getBytes(StandardCharsets.UTF_8), 599 Base64.DEFAULT), StandardCharsets.UTF_8); 600 userCredential.setPassword(encodedPassword); 601 602 switch(config.getPhase2Method()) { 603 case WifiEnterpriseConfig.Phase2.PAP: 604 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_PAP); 605 break; 606 case WifiEnterpriseConfig.Phase2.MSCHAP: 607 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP); 608 break; 609 case WifiEnterpriseConfig.Phase2.MSCHAPV2: 610 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2); 611 break; 612 default: 613 Log.e(TAG, "Unsupported phase2 method for TTLS: " + config.getPhase2Method()); 614 return null; 615 } 616 return userCredential; 617 } 618 619 /** 620 * Helper function for creating a 621 * {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from the given 622 * {@link WifiEnterpriseConfig} 623 * 624 * @param eapType The EAP type of the SIM credential 625 * @param config The enterprise configuration containing the credential 626 * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} 627 */ 628 private static Credential.SimCredential buildSimCredentialFromEnterpriseConfig( 629 int eapType, WifiEnterpriseConfig config) { 630 Credential.SimCredential simCredential = new Credential.SimCredential(); 631 if (TextUtils.isEmpty(config.getPlmn())) { 632 Log.e(TAG, "Missing IMSI for SIM credential"); 633 return null; 634 } 635 simCredential.setImsi(config.getPlmn()); 636 simCredential.setEapType(eapType); 637 return simCredential; 638 } 639 } 640