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     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