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