• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.net.eap;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.os.PersistableBundle;
24 import android.telephony.Annotation.UiccAppType;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.internal.net.ipsec.ike.utils.IkeCertUtils;
28 import com.android.server.vcn.util.PersistableBundleUtils;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.security.cert.CertificateEncodingException;
33 import java.security.cert.TrustAnchor;
34 import java.security.cert.X509Certificate;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Map;
39 import java.util.Objects;
40 
41 /**
42  * EapSessionConfig represents a container for EAP method configuration.
43  *
44  * <p>The EAP authentication server decides which EAP method is used, so clients are encouraged to
45  * provide configs for several EAP methods.
46  */
47 public final class EapSessionConfig {
48     private static final String EAP_ID_KEY = "eapIdentity";
49     private static final String EAP_METHOD_CONFIGS_KEY = "eapConfigs";
50 
51     private static final byte[] DEFAULT_IDENTITY = new byte[0];
52 
53     // IANA -> EapMethodConfig for that method
54     private final Map<Integer, EapMethodConfig> mEapConfigs;
55     private final byte[] mEapIdentity;
56 
57     /** @hide */
58     @VisibleForTesting
EapSessionConfig(Map<Integer, EapMethodConfig> eapConfigs, byte[] eapIdentity)59     public EapSessionConfig(Map<Integer, EapMethodConfig> eapConfigs, byte[] eapIdentity) {
60         Objects.requireNonNull(eapConfigs, "eapConfigs must not be null");
61         Objects.requireNonNull(eapIdentity, "eapIdentity must not be null");
62 
63         mEapConfigs = Collections.unmodifiableMap(eapConfigs);
64         mEapIdentity = eapIdentity;
65     }
66 
67     /**
68      * Gets the EAP configs set in this EapSessionConfig.
69      *
70      * @hide
71      */
getEapConfigs()72     public Map<Integer, EapMethodConfig> getEapConfigs() {
73         // Return the underlying Collection directly because it's unmodifiable
74         return mEapConfigs;
75     }
76 
77     /**
78      * Constructs this object by deserializing a PersistableBundle *
79      *
80      * <p>Constructed EapSessionConfigs are guaranteed to be valid, as checked by the
81      * EapSessionConfig.Builder
82      *
83      * @hide
84      */
85     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)86     public static EapSessionConfig fromPersistableBundle(@NonNull PersistableBundle in) {
87         Objects.requireNonNull(in, "PersistableBundle is null");
88 
89         EapSessionConfig.Builder builder = new EapSessionConfig.Builder();
90 
91         PersistableBundle eapIdBundle = in.getPersistableBundle(EAP_ID_KEY);
92         Objects.requireNonNull(eapIdBundle, "EAP ID bundle is null");
93         byte[] eapId = PersistableBundleUtils.toByteArray(eapIdBundle);
94         builder.setEapIdentity(eapId);
95 
96         PersistableBundle configsBundle = in.getPersistableBundle(EAP_METHOD_CONFIGS_KEY);
97         Objects.requireNonNull(configsBundle, "EAP method configs bundle is null");
98         Map<Integer, EapMethodConfig> eapMethodConfigs =
99                 PersistableBundleUtils.toMap(
100                         configsBundle,
101                         PersistableBundleUtils.INTEGER_DESERIALIZER,
102                         EapMethodConfig::fromPersistableBundle);
103         for (EapMethodConfig config : eapMethodConfigs.values()) {
104             builder.addEapMethodConfig(config);
105         }
106 
107         return builder.build();
108     }
109 
110     /**
111      * Serializes this object to a PersistableBundle
112      *
113      * @hide
114      */
115     @NonNull
toPersistableBundle()116     public PersistableBundle toPersistableBundle() {
117         final PersistableBundle result = new PersistableBundle();
118         result.putPersistableBundle(EAP_ID_KEY, PersistableBundleUtils.fromByteArray(mEapIdentity));
119 
120         final PersistableBundle configsBundle =
121                 PersistableBundleUtils.fromMap(
122                         mEapConfigs,
123                         PersistableBundleUtils.INTEGER_SERIALIZER,
124                         EapMethodConfig::toPersistableBundle);
125         result.putPersistableBundle(EAP_METHOD_CONFIGS_KEY, configsBundle);
126         return result;
127     }
128 
129     /** Retrieves client's EAP Identity */
130     @NonNull
getEapIdentity()131     public byte[] getEapIdentity() {
132         return mEapIdentity.clone();
133     }
134 
135     /**
136      * Retrieves configuration for EAP SIM
137      *
138      * @return the configuration for EAP SIM, or null if it was not set
139      */
140     @Nullable
getEapSimConfig()141     public EapSimConfig getEapSimConfig() {
142         return (EapSimConfig) mEapConfigs.get(EapMethodConfig.EAP_TYPE_SIM);
143     }
144 
145     /**
146      * Retrieves configuration for EAP AKA
147      *
148      * @return the configuration for EAP AKA, or null if it was not set
149      */
150     @Nullable
getEapAkaConfig()151     public EapAkaConfig getEapAkaConfig() {
152         return (EapAkaConfig) mEapConfigs.get(EapMethodConfig.EAP_TYPE_AKA);
153     }
154 
155     /**
156      * Retrieves configuration for EAP AKA'
157      *
158      * @return the configuration for EAP AKA', or null if it was not set
159      */
160     @Nullable
getEapAkaPrimeConfig()161     public EapAkaPrimeConfig getEapAkaPrimeConfig() {
162         return (EapAkaPrimeConfig) mEapConfigs.get(EapMethodConfig.EAP_TYPE_AKA_PRIME);
163     }
164 
165     /**
166      * Retrieves configuration for EAP MSCHAPV2
167      *
168      * @return the configuration for EAP MSCHAPV2, or null if it was not set
169      */
170     @Nullable
getEapMsChapV2Config()171     public EapMsChapV2Config getEapMsChapV2Config() {
172         return (EapMsChapV2Config) mEapConfigs.get(EapMethodConfig.EAP_TYPE_MSCHAP_V2);
173     }
174 
175     /**
176      * Retrieves configuration for EAP MSCHAPV2
177      *
178      * @return the configuration for EAP MSCHAPV2, or null if it was not set
179      * @hide
180      * @deprecated Callers should use {@link #getEapMsChapV2Config}
181      */
182     @Deprecated
183     @SystemApi
184     @Nullable
getEapMsChapV2onfig()185     public EapMsChapV2Config getEapMsChapV2onfig() {
186         return getEapMsChapV2Config();
187     }
188 
189     /**
190      * Retrieves configuration for EAP-TTLS
191      *
192      * @return the configuration for EAP-TTLS, or null if it was not set
193      */
194     @Nullable
getEapTtlsConfig()195     public EapTtlsConfig getEapTtlsConfig() {
196         return (EapTtlsConfig) mEapConfigs.get(EapMethodConfig.EAP_TYPE_TTLS);
197     }
198 
199     /** @hide */
200     @Override
hashCode()201     public int hashCode() {
202         return Objects.hash(Arrays.hashCode(mEapIdentity), mEapConfigs);
203     }
204 
205     /** @hide */
206     @Override
equals(Object o)207     public boolean equals(Object o) {
208         if (!(o instanceof EapSessionConfig)) {
209             return false;
210         }
211 
212         EapSessionConfig other = (EapSessionConfig) o;
213         return Arrays.equals(mEapIdentity, other.mEapIdentity)
214                 && mEapConfigs.equals(other.mEapConfigs);
215     }
216 
217     /** This class can be used to incrementally construct an {@link EapSessionConfig}. */
218     public static final class Builder {
219         private final Map<Integer, EapMethodConfig> mEapConfigs;
220         private byte[] mEapIdentity;
221 
222         /** Constructs and returns a new Builder for constructing an {@link EapSessionConfig}. */
Builder()223         public Builder() {
224             mEapConfigs = new HashMap<>();
225             mEapIdentity = DEFAULT_IDENTITY;
226         }
227 
228         /**
229          * Sets the client's EAP Identity.
230          *
231          * @param eapIdentity byte[] representing the client's EAP Identity.
232          * @return Builder this, to facilitate chaining.
233          */
234         @NonNull
setEapIdentity(@onNull byte[] eapIdentity)235         public Builder setEapIdentity(@NonNull byte[] eapIdentity) {
236             Objects.requireNonNull(eapIdentity, "eapIdentity must not be null");
237             this.mEapIdentity = eapIdentity.clone();
238             return this;
239         }
240 
241         /**
242          * Sets the configuration for EAP SIM.
243          *
244          * @param subId int the client's subId to be authenticated.
245          * @param apptype the {@link UiccAppType} apptype to be used for authentication.
246          * @return Builder this, to facilitate chaining.
247          */
248         @NonNull
setEapSimConfig(int subId, @UiccAppType int apptype)249         public Builder setEapSimConfig(int subId, @UiccAppType int apptype) {
250             mEapConfigs.put(EapMethodConfig.EAP_TYPE_SIM, new EapSimConfig(subId, apptype));
251             return this;
252         }
253 
254         /**
255          * Sets the configuration for EAP AKA.
256          *
257          * @param subId int the client's subId to be authenticated.
258          * @param apptype the {@link UiccAppType} apptype to be used for authentication.
259          * @return Builder this, to facilitate chaining.
260          */
261         @NonNull
setEapAkaConfig(int subId, @UiccAppType int apptype)262         public Builder setEapAkaConfig(int subId, @UiccAppType int apptype) {
263             mEapConfigs.put(EapMethodConfig.EAP_TYPE_AKA, new EapAkaConfig(subId, apptype));
264             return this;
265         }
266 
267         /**
268          * Sets the configuration for EAP AKA'.
269          *
270          * @param subId int the client's subId to be authenticated.
271          * @param apptype the {@link UiccAppType} apptype to be used for authentication.
272          * @param networkName String the network name to be used for authentication.
273          * @param allowMismatchedNetworkNames indicates whether the EAP library can ignore potential
274          *     mismatches between the given network name and that received in an EAP-AKA' session.
275          *     If false, mismatched network names will be handled as an Authentication Reject
276          *     message.
277          * @return Builder this, to facilitate chaining.
278          */
279         @NonNull
setEapAkaPrimeConfig( int subId, @UiccAppType int apptype, @NonNull String networkName, boolean allowMismatchedNetworkNames)280         public Builder setEapAkaPrimeConfig(
281                 int subId,
282                 @UiccAppType int apptype,
283                 @NonNull String networkName,
284                 boolean allowMismatchedNetworkNames) {
285             mEapConfigs.put(
286                     EapMethodConfig.EAP_TYPE_AKA_PRIME,
287                     new EapAkaPrimeConfig(
288                             subId, apptype, networkName, allowMismatchedNetworkNames));
289             return this;
290         }
291 
292         /**
293          * Sets the configuration for EAP MSCHAPv2.
294          *
295          * @param username String the client account's username to be authenticated.
296          * @param password String the client account's password to be authenticated.
297          * @return Builder this, to faciliate chaining.
298          */
299         @NonNull
setEapMsChapV2Config(@onNull String username, @NonNull String password)300         public Builder setEapMsChapV2Config(@NonNull String username, @NonNull String password) {
301             mEapConfigs.put(
302                     EapMethodConfig.EAP_TYPE_MSCHAP_V2, new EapMsChapV2Config(username, password));
303             return this;
304         }
305 
306         /**
307          * Sets the configuration for EAP-TTLS.
308          *
309          * <p>Tunneled EAP-TTLS authentications are disallowed, as running multiple layers of
310          * EAP-TTLS increases the data footprint but has no discernible benefits over a single
311          * EAP-TTLS session with a non EAP-TTLS method nested inside it.
312          *
313          * @param serverCaCert the CA certificate for validating the received server certificate(s).
314          *     If a certificate is provided, it MUST be the root CA used by the server, or
315          *     authentication will fail. If no certificate is provided, any root CA in the system's
316          *     truststore is considered acceptable.
317          * @param innerEapSessionConfig represents the configuration for the inner EAP instance
318          * @return Builder this, to facilitate chaining
319          */
320         @NonNull
setEapTtlsConfig( @ullable X509Certificate serverCaCert, @NonNull EapSessionConfig innerEapSessionConfig)321         public Builder setEapTtlsConfig(
322                 @Nullable X509Certificate serverCaCert,
323                 @NonNull EapSessionConfig innerEapSessionConfig) {
324             mEapConfigs.put(
325                     EapMethodConfig.EAP_TYPE_TTLS,
326                     new EapTtlsConfig(serverCaCert, innerEapSessionConfig));
327             return this;
328         }
329 
330         /**
331          * Adds an EAP method configuration. Internal use only.
332          *
333          * <p>This method will override the previously set configuration with the same method type.
334          *
335          * @hide
336          */
337         @NonNull
addEapMethodConfig(@onNull EapMethodConfig config)338         public Builder addEapMethodConfig(@NonNull EapMethodConfig config) {
339             Objects.requireNonNull(config, "EapMethodConfig is null");
340             mEapConfigs.put(config.mMethodType, config);
341             return this;
342         }
343 
344         /**
345          * Constructs and returns an EapSessionConfig with the configurations applied to this
346          * Builder.
347          *
348          * @return the EapSessionConfig constructed by this Builder.
349          */
350         @NonNull
build()351         public EapSessionConfig build() {
352             if (mEapConfigs.isEmpty()) {
353                 throw new IllegalStateException("Must have at least one EAP method configured");
354             }
355 
356             return new EapSessionConfig(mEapConfigs, mEapIdentity);
357         }
358     }
359 
360     /** EapMethodConfig represents a generic EAP method configuration. */
361     public abstract static class EapMethodConfig {
362         private static final String METHOD_TYPE = "methodType";
363 
364         /** @hide */
365         @Retention(RetentionPolicy.SOURCE)
366         @IntDef({EAP_TYPE_SIM, EAP_TYPE_TTLS, EAP_TYPE_AKA, EAP_TYPE_MSCHAP_V2, EAP_TYPE_AKA_PRIME})
367         public @interface EapMethod {}
368 
369         // EAP Type values defined by IANA
370         // @see https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml
371         /**
372          * EAP-Type value for the EAP-SIM method.
373          *
374          * <p>To include EAP-SIM as an authentication method, see {@link
375          * EapSessionConfig.Builder#setEapSimConfig(int, int)}.
376          *
377          * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication
378          *     Protocol Method for Global System for Mobile Communications (GSM) Subscriber Identity
379          *     Modules (EAP-SIM)</a>
380          */
381         public static final int EAP_TYPE_SIM = 18;
382 
383         /**
384          * EAP-Type value for the EAP-TTLS method.
385          *
386          * <p>To include EAP-TTLS as an authentication method, see {@link
387          * EapSessionConfig.Builder#setEapTtlsConfig(X509Certificate, EapSessionConfig)}.
388          *
389          * @see <a href="https://tools.ietf.org/html/rfc5281">RFC 5281, Extensible Authentication
390          *     Protocol Tunneled Transport Layer Security Authenticated Protocol Version 0
391          *     (EAP-TTLSv0)</a>
392          */
393         public static final int EAP_TYPE_TTLS = 21;
394 
395         /**
396          * EAP-Type value for the EAP-AKA method.
397          *
398          * <p>To include EAP-AKA as an authentication method, see {@link
399          * EapSessionConfig.Builder#setEapAkaConfig(int, int)}.
400          *
401          * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication
402          *     Protocol Method for 3rd Generation Authentication and Key Agreement (EAP-AKA)</a>
403          */
404         public static final int EAP_TYPE_AKA = 23;
405 
406         /**
407          * EAP-Type value for the EAP-MS-CHAPv2 method.
408          *
409          * <p>To include EAP-MS-CHAPv2 as an authentication method, see {@link
410          * EapSessionConfig.Builder#setEapMsChapV2Config(String, String)}.
411          *
412          * @see <a href="https://tools.ietf.org/html/draft-kamath-pppext-eap-mschapv2-02">Microsoft
413          *     EAP CHAP Extensions Draft (EAP MSCHAPv2)</a>
414          */
415         public static final int EAP_TYPE_MSCHAP_V2 = 26;
416 
417         /**
418          * EAP-Type value for the EAP-AKA' method.
419          *
420          * <p>To include EAP-AKA' as an authentication method, see {@link
421          * EapSessionConfig.Builder#setEapAkaPrimeConfig(int, int, String, boolean)}.
422          *
423          * @see <a href="https://tools.ietf.org/html/rfc5448">RFC 5448, Improved Extensible
424          *     Authentication Protocol Method for 3rd Generation Authentication and Key Agreement
425          *     (EAP-AKA')</a>
426          */
427         public static final int EAP_TYPE_AKA_PRIME = 50;
428 
429         @EapMethod private final int mMethodType;
430 
431         /** @hide */
EapMethodConfig(@apMethod int methodType)432         EapMethodConfig(@EapMethod int methodType) {
433             mMethodType = methodType;
434         }
435 
436         /**
437          * Constructs this object by deserializing a PersistableBundle
438          *
439          * @hide
440          */
441         @NonNull
fromPersistableBundle(PersistableBundle in)442         public static EapMethodConfig fromPersistableBundle(PersistableBundle in) {
443             Objects.requireNonNull(in, "PersistableBundle is null");
444 
445             int methodType = in.getInt(METHOD_TYPE);
446             switch (methodType) {
447                 case EAP_TYPE_SIM:
448                     return EapSimConfig.fromPersistableBundle(in);
449                 case EAP_TYPE_AKA:
450                     return EapAkaConfig.fromPersistableBundle(in);
451                 case EAP_TYPE_AKA_PRIME:
452                     return EapAkaPrimeConfig.fromPersistableBundle(in);
453                 case EAP_TYPE_MSCHAP_V2:
454                     return EapMsChapV2Config.fromPersistableBundle(in);
455                 case EAP_TYPE_TTLS:
456                     return EapTtlsConfig.fromPersistableBundle(in);
457                 default:
458                     throw new IllegalArgumentException("Invalid EAP Type: " + methodType);
459             }
460         }
461 
462         /**
463          * Serializes this object to a PersistableBundle
464          *
465          * @hide
466          */
467         @NonNull
toPersistableBundle()468         protected PersistableBundle toPersistableBundle() {
469             final PersistableBundle result = new PersistableBundle();
470             result.putInt(METHOD_TYPE, mMethodType);
471             return result;
472         }
473 
474         /**
475          * Retrieves the EAP method type
476          *
477          * @return the IANA-defined EAP method constant
478          */
getMethodType()479         public int getMethodType() {
480             return mMethodType;
481         }
482 
483         /**
484          * Check if this is EAP-only safe method.
485          *
486          * @return whether the method is EAP-only safe
487          *
488          * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998#section 4, for safe eap
489          * methods</a>
490          *
491          * @hide
492          */
isEapOnlySafeMethod()493         public boolean isEapOnlySafeMethod() {
494             return false;
495         }
496 
497         /** @hide */
498         @Override
hashCode()499         public int hashCode() {
500             return Objects.hash(mMethodType);
501         }
502 
503         /** @hide */
504         @Override
equals(Object o)505         public boolean equals(Object o) {
506             if (!(o instanceof EapMethodConfig)) {
507                 return false;
508             }
509 
510             return mMethodType == ((EapMethodConfig) o).mMethodType;
511         }
512     }
513 
514     /**
515      * EapUiccConfig represents the configs needed for EAP methods that rely on UICC cards for
516      * authentication.
517      *
518      * @hide
519      * @deprecated This class is not useful. Callers should only use its two subclasses {@link
520      *     EapSimConfig} and {@link EapAkaConfig}
521      */
522     @Deprecated
523     @SystemApi
524     public abstract static class EapUiccConfig extends EapMethodConfig {
525         /** @hide */
526         protected static final String SUB_ID_KEY = "subId";
527         /** @hide */
528         protected static final String APP_TYPE_KEY = "apptype";
529 
530         private final int mSubId;
531         private final int mApptype;
532 
EapUiccConfig(@apMethod int methodType, int subId, @UiccAppType int apptype)533         private EapUiccConfig(@EapMethod int methodType, int subId, @UiccAppType int apptype) {
534             super(methodType);
535             mSubId = subId;
536             mApptype = apptype;
537         }
538 
539         /**
540          * Serializes this object to a PersistableBundle
541          *
542          * @hide
543          */
544         @Override
545         @NonNull
toPersistableBundle()546         protected PersistableBundle toPersistableBundle() {
547             final PersistableBundle result = super.toPersistableBundle();
548             result.putInt(SUB_ID_KEY, mSubId);
549             result.putInt(APP_TYPE_KEY, mApptype);
550 
551             return result;
552         }
553 
554         /**
555          * Retrieves the subId
556          *
557          * @return the subId
558          */
getSubId()559         public int getSubId() {
560             return mSubId;
561         }
562 
563         /**
564          * Retrieves the UICC app type
565          *
566          * @return the {@link UiccAppType} constant
567          */
getAppType()568         public int getAppType() {
569             return mApptype;
570         }
571 
572         /** @hide */
573         @Override
isEapOnlySafeMethod()574         public boolean isEapOnlySafeMethod() {
575             return true;
576         }
577 
578         /** @hide */
579         @Override
hashCode()580         public int hashCode() {
581             return Objects.hash(super.hashCode(), mSubId, mApptype);
582         }
583 
584         /** @hide */
585         @Override
equals(Object o)586         public boolean equals(Object o) {
587             if (!super.equals(o) || !(o instanceof EapUiccConfig)) {
588                 return false;
589             }
590 
591             EapUiccConfig other = (EapUiccConfig) o;
592 
593             return mSubId == other.mSubId && mApptype == other.mApptype;
594         }
595     }
596 
597     /**
598      * EapSimConfig represents the configs needed for an EAP SIM session.
599      */
600     public static class EapSimConfig extends EapUiccConfig {
601         /** @hide */
602         @VisibleForTesting
EapSimConfig(int subId, @UiccAppType int apptype)603         public EapSimConfig(int subId, @UiccAppType int apptype) {
604             super(EAP_TYPE_SIM, subId, apptype);
605         }
606 
607         /**
608          * Constructs this object by deserializing a PersistableBundle
609          *
610          * @hide
611          */
612         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)613         public static EapSimConfig fromPersistableBundle(@NonNull PersistableBundle in) {
614             Objects.requireNonNull(in, "PersistableBundle is null");
615             return new EapSimConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
616         }
617     }
618 
619     /**
620      * EapAkaConfig represents the configs needed for an EAP AKA session.
621      */
622     public static class EapAkaConfig extends EapUiccConfig {
623         /** @hide */
624         @VisibleForTesting
EapAkaConfig(int subId, @UiccAppType int apptype)625         public EapAkaConfig(int subId, @UiccAppType int apptype) {
626             this(EAP_TYPE_AKA, subId, apptype);
627         }
628 
629         /** @hide */
EapAkaConfig(int methodType, int subId, @UiccAppType int apptype)630         EapAkaConfig(int methodType, int subId, @UiccAppType int apptype) {
631             super(methodType, subId, apptype);
632         }
633 
634         /**
635          * Constructs this object by deserializing a PersistableBundle
636          *
637          * @hide
638          */
639         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)640         public static EapAkaConfig fromPersistableBundle(@NonNull PersistableBundle in) {
641             Objects.requireNonNull(in, "PersistableBundle is null");
642             return new EapAkaConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
643         }
644     }
645 
646     /**
647      * EapAkaPrimeConfig represents the configs needed for an EAP-AKA' session.
648      */
649     public static class EapAkaPrimeConfig extends EapAkaConfig {
650         private static final String NETWORK_NAME_KEY = "networkName";
651         private static final String ALL_MISMATCHED_NETWORK_KEY = "allowMismatchedNetworkNames";
652 
653         @NonNull private final String mNetworkName;
654         private final boolean mAllowMismatchedNetworkNames;
655 
656         /** @hide */
657         @VisibleForTesting
EapAkaPrimeConfig( int subId, @UiccAppType int apptype, @NonNull String networkName, boolean allowMismatchedNetworkNames)658         public EapAkaPrimeConfig(
659                 int subId,
660                 @UiccAppType int apptype,
661                 @NonNull String networkName,
662                 boolean allowMismatchedNetworkNames) {
663             super(EAP_TYPE_AKA_PRIME, subId, apptype);
664 
665             Objects.requireNonNull(networkName, "networkName must not be null");
666 
667             mNetworkName = networkName;
668             mAllowMismatchedNetworkNames = allowMismatchedNetworkNames;
669         }
670 
671         /**
672          * Constructs this object by deserializing a PersistableBundle
673          *
674          * @hide
675          */
676         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)677         public static EapAkaPrimeConfig fromPersistableBundle(@NonNull PersistableBundle in) {
678             Objects.requireNonNull(in, "PersistableBundle is null");
679             return new EapAkaPrimeConfig(
680                     in.getInt(SUB_ID_KEY),
681                     in.getInt(APP_TYPE_KEY),
682                     in.getString(NETWORK_NAME_KEY),
683                     in.getBoolean(ALL_MISMATCHED_NETWORK_KEY));
684         }
685 
686         /**
687          * Serializes this object to a PersistableBundle
688          *
689          * @hide
690          */
691         @Override
692         @NonNull
toPersistableBundle()693         protected PersistableBundle toPersistableBundle() {
694             final PersistableBundle result = super.toPersistableBundle();
695             result.putString(NETWORK_NAME_KEY, mNetworkName);
696             result.putBoolean(ALL_MISMATCHED_NETWORK_KEY, mAllowMismatchedNetworkNames);
697 
698             return result;
699         }
700 
701         /**
702          * Retrieves the UICC app type
703          *
704          * @return the {@link UiccAppType} constant
705          */
706         @NonNull
getNetworkName()707         public String getNetworkName() {
708             return mNetworkName;
709         }
710 
711         /**
712          * Checks if mismatched network names are allowed
713          *
714          * @return whether network name mismatches are allowed
715          */
allowsMismatchedNetworkNames()716         public boolean allowsMismatchedNetworkNames() {
717             return mAllowMismatchedNetworkNames;
718         }
719 
720         /** @hide */
721         @Override
hashCode()722         public int hashCode() {
723             return Objects.hash(super.hashCode(), mNetworkName, mAllowMismatchedNetworkNames);
724         }
725 
726         /** @hide */
727         @Override
equals(Object o)728         public boolean equals(Object o) {
729             if (!super.equals(o) || !(o instanceof EapAkaPrimeConfig)) {
730                 return false;
731             }
732 
733             EapAkaPrimeConfig other = (EapAkaPrimeConfig) o;
734 
735             return mNetworkName.equals(other.mNetworkName)
736                     && mAllowMismatchedNetworkNames == other.mAllowMismatchedNetworkNames;
737         }
738     }
739 
740     /**
741      * EapMsChapV2Config represents the configs needed for an EAP MSCHAPv2 session.
742      */
743     public static class EapMsChapV2Config extends EapMethodConfig {
744         private static final String USERNAME_KEY = "username";
745         private static final String PASSWORD_KEY = "password";
746 
747         @NonNull private final String mUsername;
748         @NonNull private final String mPassword;
749 
750         /** @hide */
751         @VisibleForTesting
EapMsChapV2Config(String username, String password)752         public EapMsChapV2Config(String username, String password) {
753             super(EAP_TYPE_MSCHAP_V2);
754 
755             Objects.requireNonNull(username, "username must not be null");
756             Objects.requireNonNull(password, "password must not be null");
757 
758             mUsername = username;
759             mPassword = password;
760         }
761 
762         /**
763          * Constructs this object by deserializing a PersistableBundle
764          *
765          * @hide
766          */
767         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)768         public static EapMsChapV2Config fromPersistableBundle(@NonNull PersistableBundle in) {
769             Objects.requireNonNull(in, "PersistableBundle is null");
770             return new EapMsChapV2Config(in.getString(USERNAME_KEY), in.getString(PASSWORD_KEY));
771         }
772 
773         /**
774          * Serializes this object to a PersistableBundle
775          *
776          * @hide
777          */
778         @Override
779         @NonNull
toPersistableBundle()780         protected PersistableBundle toPersistableBundle() {
781             final PersistableBundle result = super.toPersistableBundle();
782             result.putString(USERNAME_KEY, mUsername);
783             result.putString(PASSWORD_KEY, mPassword);
784 
785             return result;
786         }
787 
788         /**
789          * Retrieves the username
790          *
791          * @return the username to be used by MSCHAPV2
792          */
793         @NonNull
getUsername()794         public String getUsername() {
795             return mUsername;
796         }
797 
798         /**
799          * Retrieves the password
800          *
801          * @return the password to be used by MSCHAPV2
802          */
803         @NonNull
getPassword()804         public String getPassword() {
805             return mPassword;
806         }
807 
808         /** @hide */
809         @Override
hashCode()810         public int hashCode() {
811             return Objects.hash(super.hashCode(), mUsername, mPassword);
812         }
813 
814         /** @hide */
815         @Override
equals(Object o)816         public boolean equals(Object o) {
817             if (!super.equals(o) || !(o instanceof EapMsChapV2Config)) {
818                 return false;
819             }
820 
821             EapMsChapV2Config other = (EapMsChapV2Config) o;
822 
823             return mUsername.equals(other.mUsername) && mPassword.equals(other.mPassword);
824         }
825     }
826 
827     /**
828      * EapTtlsConfig represents the configs needed for an EAP-TTLS session.
829      */
830     public static class EapTtlsConfig extends EapMethodConfig {
831         private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
832         private static final String EAP_SESSION_CONFIG_KEY = "EAP_SESSION_CONFIG_KEY";
833 
834         @Nullable private final TrustAnchor mOverrideTrustAnchor;
835         @NonNull private final EapSessionConfig mInnerEapSessionConfig;
836 
837         /** @hide */
838         @VisibleForTesting
EapTtlsConfig( @ullable X509Certificate serverCaCert, @NonNull EapSessionConfig innerEapSessionConfig)839         public EapTtlsConfig(
840                 @Nullable X509Certificate serverCaCert,
841                 @NonNull EapSessionConfig innerEapSessionConfig) {
842             super(EAP_TYPE_TTLS);
843             mInnerEapSessionConfig =
844                     Objects.requireNonNull(
845                             innerEapSessionConfig, "innerEapSessionConfig must not be null");
846             if (mInnerEapSessionConfig.getEapConfigs().containsKey(EAP_TYPE_TTLS)) {
847                 throw new IllegalArgumentException("Recursive EAP-TTLS method configs not allowed");
848             }
849 
850             mOverrideTrustAnchor =
851                     (serverCaCert == null)
852                             ? null
853                             : new TrustAnchor(serverCaCert, null /* nameConstraints */);
854         }
855 
856         /**
857          * Constructs this object by deserializing a PersistableBundle.
858          *
859          * @hide
860          */
861         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)862         public static EapTtlsConfig fromPersistableBundle(@NonNull PersistableBundle in) {
863             Objects.requireNonNull(in, "PersistableBundle is null");
864 
865             PersistableBundle trustCertBundle = in.getPersistableBundle(TRUST_CERT_KEY);
866             X509Certificate caCert = null;
867             if (trustCertBundle != null) {
868                 byte[] encodedCert = PersistableBundleUtils.toByteArray(trustCertBundle);
869                 caCert = IkeCertUtils.certificateFromByteArray(encodedCert);
870             }
871 
872             PersistableBundle eapSessionConfigBundle =
873                     in.getPersistableBundle(EAP_SESSION_CONFIG_KEY);
874             Objects.requireNonNull(eapSessionConfigBundle, "eapSessionConfigBundle is null");
875             EapSessionConfig eapSessionConfig =
876                     EapSessionConfig.fromPersistableBundle(eapSessionConfigBundle);
877 
878             return new EapTtlsConfig(caCert, eapSessionConfig);
879         }
880 
881         /**
882          * Serializes this object to a PersistableBundle.
883          *
884          * @hide
885          */
886         @Override
887         @NonNull
toPersistableBundle()888         protected PersistableBundle toPersistableBundle() {
889             final PersistableBundle result = super.toPersistableBundle();
890 
891             try {
892                 if (mOverrideTrustAnchor != null) {
893                     result.putPersistableBundle(
894                             TRUST_CERT_KEY,
895                             PersistableBundleUtils.fromByteArray(
896                                     mOverrideTrustAnchor.getTrustedCert().getEncoded()));
897                 }
898 
899                 result.putPersistableBundle(
900                         EAP_SESSION_CONFIG_KEY, mInnerEapSessionConfig.toPersistableBundle());
901             } catch (CertificateEncodingException e) {
902                 throw new IllegalArgumentException("Fail to encode the certificate");
903             }
904 
905             return result;
906         }
907 
908         /** @hide */
909         @Override
isEapOnlySafeMethod()910         public boolean isEapOnlySafeMethod() {
911             return true;
912         }
913 
914         /**
915          * Retrieves the provided CA certificate for validating the remote certificate(s)
916          *
917          * @return the CA certificate for validating the received server certificate or null if the
918          *     system default is preferred
919          */
920         @Nullable
getServerCaCert()921         public X509Certificate getServerCaCert() {
922             return (mOverrideTrustAnchor == null) ? null : mOverrideTrustAnchor.getTrustedCert();
923         }
924 
925         /**
926          * Retrieves the inner EAP session config
927          *
928          * @return an EapSessionConfig representing the config for tunneled EAP authentication
929          */
930         @NonNull
getInnerEapSessionConfig()931         public EapSessionConfig getInnerEapSessionConfig() {
932             return mInnerEapSessionConfig;
933         }
934 
935         /** @hide */
936         @Override
hashCode()937         public int hashCode() {
938             // Use #getTrustedCert() because TrustAnchor does not override #hashCode()
939 
940             return Objects.hash(
941                     super.hashCode(),
942                     mInnerEapSessionConfig,
943                     (mOverrideTrustAnchor == null) ? null : mOverrideTrustAnchor.getTrustedCert());
944         }
945 
946         /** @hide */
947         @Override
equals(Object o)948         public boolean equals(Object o) {
949             if (!super.equals(o) || !(o instanceof EapTtlsConfig)) {
950                 return false;
951             }
952 
953             EapTtlsConfig other = (EapTtlsConfig) o;
954 
955             if (!Objects.equals(mInnerEapSessionConfig, other.mInnerEapSessionConfig)) {
956                 return false;
957             }
958 
959             if (mOverrideTrustAnchor == null && other.mOverrideTrustAnchor == null) {
960                 return true;
961             }
962 
963             return mOverrideTrustAnchor != null
964                     && other.mOverrideTrustAnchor != null
965                     && Objects.equals(
966                             mOverrideTrustAnchor.getTrustedCert(),
967                             other.mOverrideTrustAnchor.getTrustedCert());
968         }
969     }
970 
971     /**
972      * Checks if all the methods in the session are EAP-only safe
973      *
974      * @return whether all the methods in the session are EAP-only safe
975      *
976      * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998#section 4, for safe eap
977      * methods</a>
978      *
979      * @hide
980      */
areAllMethodsEapOnlySafe()981     public boolean areAllMethodsEapOnlySafe() {
982         for (Map.Entry<Integer, EapMethodConfig> eapConfigsEntry : mEapConfigs.entrySet()) {
983             if (!eapConfigsEntry.getValue().isEapOnlySafeMethod()) {
984                 return false;
985             }
986         }
987 
988         return true;
989     }
990 }
991