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