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