• 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             setEapAkaConfig(subId, apptype, null);
264             return this;
265         }
266 
267         /**
268          * Sets the configuration for EAP AKA with options.
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 options optional configuration for EAP AKA
273          * @return Builder this, to facilitate chaining.
274          */
275         @NonNull
setEapAkaConfig( int subId, @UiccAppType int apptype, @NonNull EapAkaOption options)276         public Builder setEapAkaConfig(
277                 int subId, @UiccAppType int apptype, @NonNull EapAkaOption options) {
278             mEapConfigs.put(
279                     EapMethodConfig.EAP_TYPE_AKA, new EapAkaConfig(subId, apptype, options));
280             return this;
281         }
282 
283         /**
284          * Sets the configuration for EAP AKA'.
285          *
286          * @param subId int the client's subId to be authenticated.
287          * @param apptype the {@link UiccAppType} apptype to be used for authentication.
288          * @param networkName String the network name to be used for authentication.
289          * @param allowMismatchedNetworkNames indicates whether the EAP library can ignore potential
290          *     mismatches between the given network name and that received in an EAP-AKA' session.
291          *     If false, mismatched network names will be handled as an Authentication Reject
292          *     message.
293          * @return Builder this, to facilitate chaining.
294          */
295         @NonNull
setEapAkaPrimeConfig( int subId, @UiccAppType int apptype, @NonNull String networkName, boolean allowMismatchedNetworkNames)296         public Builder setEapAkaPrimeConfig(
297                 int subId,
298                 @UiccAppType int apptype,
299                 @NonNull String networkName,
300                 boolean allowMismatchedNetworkNames) {
301             mEapConfigs.put(
302                     EapMethodConfig.EAP_TYPE_AKA_PRIME,
303                     new EapAkaPrimeConfig(
304                             subId, apptype, networkName, allowMismatchedNetworkNames));
305             return this;
306         }
307 
308         /**
309          * Sets the configuration for EAP MSCHAPv2.
310          *
311          * @param username String the client account's username to be authenticated.
312          * @param password String the client account's password to be authenticated.
313          * @return Builder this, to faciliate chaining.
314          */
315         @NonNull
setEapMsChapV2Config(@onNull String username, @NonNull String password)316         public Builder setEapMsChapV2Config(@NonNull String username, @NonNull String password) {
317             mEapConfigs.put(
318                     EapMethodConfig.EAP_TYPE_MSCHAP_V2, new EapMsChapV2Config(username, password));
319             return this;
320         }
321 
322         /**
323          * Sets the configuration for EAP-TTLS.
324          *
325          * <p>Tunneled EAP-TTLS authentications are disallowed, as running multiple layers of
326          * EAP-TTLS increases the data footprint but has no discernible benefits over a single
327          * EAP-TTLS session with a non EAP-TTLS method nested inside it.
328          *
329          * @param serverCaCert the CA certificate for validating the received server certificate(s).
330          *     If a certificate is provided, it MUST be the root CA used by the server, or
331          *     authentication will fail. If no certificate is provided, any root CA in the system's
332          *     truststore is considered acceptable.
333          * @param innerEapSessionConfig represents the configuration for the inner EAP instance
334          * @return Builder this, to facilitate chaining
335          */
336         @NonNull
setEapTtlsConfig( @ullable X509Certificate serverCaCert, @NonNull EapSessionConfig innerEapSessionConfig)337         public Builder setEapTtlsConfig(
338                 @Nullable X509Certificate serverCaCert,
339                 @NonNull EapSessionConfig innerEapSessionConfig) {
340             mEapConfigs.put(
341                     EapMethodConfig.EAP_TYPE_TTLS,
342                     new EapTtlsConfig(serverCaCert, innerEapSessionConfig));
343             return this;
344         }
345 
346         /**
347          * Adds an EAP method configuration. Internal use only.
348          *
349          * <p>This method will override the previously set configuration with the same method type.
350          *
351          * @hide
352          */
353         @NonNull
addEapMethodConfig(@onNull EapMethodConfig config)354         public Builder addEapMethodConfig(@NonNull EapMethodConfig config) {
355             Objects.requireNonNull(config, "EapMethodConfig is null");
356             mEapConfigs.put(config.mMethodType, config);
357             return this;
358         }
359 
360         /**
361          * Constructs and returns an EapSessionConfig with the configurations applied to this
362          * Builder.
363          *
364          * @return the EapSessionConfig constructed by this Builder.
365          */
366         @NonNull
build()367         public EapSessionConfig build() {
368             if (mEapConfigs.isEmpty()) {
369                 throw new IllegalStateException("Must have at least one EAP method configured");
370             }
371 
372             return new EapSessionConfig(mEapConfigs, mEapIdentity);
373         }
374     }
375 
376     /** EapMethodConfig represents a generic EAP method configuration. */
377     public abstract static class EapMethodConfig {
378         private static final String METHOD_TYPE = "methodType";
379 
380         /** @hide */
381         @Retention(RetentionPolicy.SOURCE)
382         @IntDef({EAP_TYPE_SIM, EAP_TYPE_TTLS, EAP_TYPE_AKA, EAP_TYPE_MSCHAP_V2, EAP_TYPE_AKA_PRIME})
383         public @interface EapMethod {}
384 
385         // EAP Type values defined by IANA
386         // @see https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml
387         /**
388          * EAP-Type value for the EAP-SIM method.
389          *
390          * <p>To include EAP-SIM as an authentication method, see {@link
391          * EapSessionConfig.Builder#setEapSimConfig(int, int)}.
392          *
393          * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication
394          *     Protocol Method for Global System for Mobile Communications (GSM) Subscriber Identity
395          *     Modules (EAP-SIM)</a>
396          */
397         public static final int EAP_TYPE_SIM = 18;
398 
399         /**
400          * EAP-Type value for the EAP-TTLS method.
401          *
402          * <p>To include EAP-TTLS as an authentication method, see {@link
403          * EapSessionConfig.Builder#setEapTtlsConfig(X509Certificate, EapSessionConfig)}.
404          *
405          * @see <a href="https://tools.ietf.org/html/rfc5281">RFC 5281, Extensible Authentication
406          *     Protocol Tunneled Transport Layer Security Authenticated Protocol Version 0
407          *     (EAP-TTLSv0)</a>
408          */
409         public static final int EAP_TYPE_TTLS = 21;
410 
411         /**
412          * EAP-Type value for the EAP-AKA method.
413          *
414          * <p>To include EAP-AKA as an authentication method, see {@link
415          * EapSessionConfig.Builder#setEapAkaConfig(int, int)}.
416          *
417          * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication
418          *     Protocol Method for 3rd Generation Authentication and Key Agreement (EAP-AKA)</a>
419          */
420         public static final int EAP_TYPE_AKA = 23;
421 
422         /**
423          * EAP-Type value for the EAP-MS-CHAPv2 method.
424          *
425          * <p>To include EAP-MS-CHAPv2 as an authentication method, see {@link
426          * EapSessionConfig.Builder#setEapMsChapV2Config(String, String)}.
427          *
428          * @see <a href="https://tools.ietf.org/html/draft-kamath-pppext-eap-mschapv2-02">Microsoft
429          *     EAP CHAP Extensions Draft (EAP MSCHAPv2)</a>
430          */
431         public static final int EAP_TYPE_MSCHAP_V2 = 26;
432 
433         /**
434          * EAP-Type value for the EAP-AKA' method.
435          *
436          * <p>To include EAP-AKA' as an authentication method, see {@link
437          * EapSessionConfig.Builder#setEapAkaPrimeConfig(int, int, String, boolean)}.
438          *
439          * @see <a href="https://tools.ietf.org/html/rfc5448">RFC 5448, Improved Extensible
440          *     Authentication Protocol Method for 3rd Generation Authentication and Key Agreement
441          *     (EAP-AKA')</a>
442          */
443         public static final int EAP_TYPE_AKA_PRIME = 50;
444 
445         @EapMethod private final int mMethodType;
446 
447         /** @hide */
EapMethodConfig(@apMethod int methodType)448         EapMethodConfig(@EapMethod int methodType) {
449             mMethodType = methodType;
450         }
451 
452         /**
453          * Constructs this object by deserializing a PersistableBundle
454          *
455          * @hide
456          */
457         @NonNull
fromPersistableBundle(PersistableBundle in)458         public static EapMethodConfig fromPersistableBundle(PersistableBundle in) {
459             Objects.requireNonNull(in, "PersistableBundle is null");
460 
461             int methodType = in.getInt(METHOD_TYPE);
462             switch (methodType) {
463                 case EAP_TYPE_SIM:
464                     return EapSimConfig.fromPersistableBundle(in);
465                 case EAP_TYPE_AKA:
466                     return EapAkaConfig.fromPersistableBundle(in);
467                 case EAP_TYPE_AKA_PRIME:
468                     return EapAkaPrimeConfig.fromPersistableBundle(in);
469                 case EAP_TYPE_MSCHAP_V2:
470                     return EapMsChapV2Config.fromPersistableBundle(in);
471                 case EAP_TYPE_TTLS:
472                     return EapTtlsConfig.fromPersistableBundle(in);
473                 default:
474                     throw new IllegalArgumentException("Invalid EAP Type: " + methodType);
475             }
476         }
477 
478         /**
479          * Serializes this object to a PersistableBundle
480          *
481          * @hide
482          */
483         @NonNull
toPersistableBundle()484         protected PersistableBundle toPersistableBundle() {
485             final PersistableBundle result = new PersistableBundle();
486             result.putInt(METHOD_TYPE, mMethodType);
487             return result;
488         }
489 
490         /**
491          * Retrieves the EAP method type
492          *
493          * @return the IANA-defined EAP method constant
494          */
getMethodType()495         public int getMethodType() {
496             return mMethodType;
497         }
498 
499         /**
500          * Check if this is EAP-only safe method.
501          *
502          * @return whether the method is EAP-only safe
503          *
504          * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998#section 4, for safe eap
505          * methods</a>
506          *
507          * @hide
508          */
isEapOnlySafeMethod()509         public boolean isEapOnlySafeMethod() {
510             return false;
511         }
512 
513         /** @hide */
514         @Override
hashCode()515         public int hashCode() {
516             return Objects.hash(mMethodType);
517         }
518 
519         /** @hide */
520         @Override
equals(Object o)521         public boolean equals(Object o) {
522             if (!(o instanceof EapMethodConfig)) {
523                 return false;
524             }
525 
526             return mMethodType == ((EapMethodConfig) o).mMethodType;
527         }
528     }
529 
530     /**
531      * EapUiccConfig represents the configs needed for EAP methods that rely on UICC cards for
532      * authentication.
533      *
534      * @hide
535      * @deprecated This class is not useful. Callers should only use its two subclasses {@link
536      *     EapSimConfig} and {@link EapAkaConfig}
537      */
538     @Deprecated
539     @SystemApi
540     public abstract static class EapUiccConfig extends EapMethodConfig {
541         /** @hide */
542         protected static final String SUB_ID_KEY = "subId";
543         /** @hide */
544         protected static final String APP_TYPE_KEY = "apptype";
545 
546         private final int mSubId;
547         private final int mApptype;
548 
EapUiccConfig(@apMethod int methodType, int subId, @UiccAppType int apptype)549         private EapUiccConfig(@EapMethod int methodType, int subId, @UiccAppType int apptype) {
550             super(methodType);
551             mSubId = subId;
552             mApptype = apptype;
553         }
554 
555         /**
556          * Serializes this object to a PersistableBundle
557          *
558          * @hide
559          */
560         @Override
561         @NonNull
toPersistableBundle()562         protected PersistableBundle toPersistableBundle() {
563             final PersistableBundle result = super.toPersistableBundle();
564             result.putInt(SUB_ID_KEY, mSubId);
565             result.putInt(APP_TYPE_KEY, mApptype);
566 
567             return result;
568         }
569 
570         /**
571          * Retrieves the subId
572          *
573          * @return the subId
574          */
getSubId()575         public int getSubId() {
576             return mSubId;
577         }
578 
579         /**
580          * Retrieves the UICC app type
581          *
582          * @return the {@link UiccAppType} constant
583          */
getAppType()584         public int getAppType() {
585             return mApptype;
586         }
587 
588         /** @hide */
589         @Override
isEapOnlySafeMethod()590         public boolean isEapOnlySafeMethod() {
591             return true;
592         }
593 
594         /** @hide */
595         @Override
hashCode()596         public int hashCode() {
597             return Objects.hash(super.hashCode(), mSubId, mApptype);
598         }
599 
600         /** @hide */
601         @Override
equals(Object o)602         public boolean equals(Object o) {
603             if (!super.equals(o) || !(o instanceof EapUiccConfig)) {
604                 return false;
605             }
606 
607             EapUiccConfig other = (EapUiccConfig) o;
608 
609             return mSubId == other.mSubId && mApptype == other.mApptype;
610         }
611     }
612 
613     /**
614      * EapSimConfig represents the configs needed for an EAP SIM session.
615      */
616     public static class EapSimConfig extends EapUiccConfig {
617         /** @hide */
618         @VisibleForTesting
EapSimConfig(int subId, @UiccAppType int apptype)619         public EapSimConfig(int subId, @UiccAppType int apptype) {
620             super(EAP_TYPE_SIM, subId, apptype);
621         }
622 
623         /**
624          * Constructs this object by deserializing a PersistableBundle
625          *
626          * @hide
627          */
628         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)629         public static EapSimConfig fromPersistableBundle(@NonNull PersistableBundle in) {
630             Objects.requireNonNull(in, "PersistableBundle is null");
631             return new EapSimConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
632         }
633     }
634 
635     /**
636      * EapAkaConfig represents the configs needed for an EAP AKA session.
637      */
638     public static class EapAkaConfig extends EapUiccConfig {
639         private static final String AKA_OPTION_KEY = "akaOption";
640 
641         private final EapAkaOption mEapAkaOption;
642 
643         /** @hide */
644         @VisibleForTesting
EapAkaConfig(int subId, @UiccAppType int apptype)645         public EapAkaConfig(int subId, @UiccAppType int apptype) {
646             this(EAP_TYPE_AKA, subId, apptype, null);
647         }
648 
649         /** @hide */
650         @VisibleForTesting
EapAkaConfig(int subId, @UiccAppType int apptype, EapAkaOption options)651         public EapAkaConfig(int subId, @UiccAppType int apptype, EapAkaOption options) {
652             this(EAP_TYPE_AKA, subId, apptype, options);
653         }
654 
655         /** @hide */
EapAkaConfig(int methodType, int subId, @UiccAppType int apptype, EapAkaOption options)656         EapAkaConfig(int methodType, int subId, @UiccAppType int apptype, EapAkaOption options) {
657             super(methodType, subId, apptype);
658             mEapAkaOption = options;
659         }
660 
661         /**
662          * Constructs this object by deserializing a PersistableBundle
663          *
664          * @hide
665          */
666         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)667         public static EapAkaConfig fromPersistableBundle(@NonNull PersistableBundle in) {
668             Objects.requireNonNull(in, "PersistableBundle is null");
669 
670             EapAkaOption eapAkaOption = null;
671             PersistableBundle bundle = in.getPersistableBundle(AKA_OPTION_KEY);
672             if (bundle != null) {
673                 eapAkaOption = EapAkaOption.fromPersistableBundle(bundle);
674             }
675 
676             return new EapAkaConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY), eapAkaOption);
677         }
678 
679         /**
680          * Serializes this object to a PersistableBundle
681          *
682          * @hide
683          */
684         @Override
685         @NonNull
toPersistableBundle()686         protected PersistableBundle toPersistableBundle() {
687             final PersistableBundle result = super.toPersistableBundle();
688             if (mEapAkaOption != null) {
689                 result.putPersistableBundle(AKA_OPTION_KEY, mEapAkaOption.toPersistableBundle());
690             }
691 
692             return result;
693         }
694 
695         /**
696          * Retrieves EapAkaOption
697          *
698          * @return the {@link EapAkaOption}
699          */
700         @NonNull
getEapAkaOption()701         public EapAkaOption getEapAkaOption() {
702             return mEapAkaOption;
703         }
704     }
705 
706     /**
707      * EapAkaPrimeConfig represents the configs needed for an EAP-AKA' session.
708      */
709     public static class EapAkaPrimeConfig extends EapAkaConfig {
710         private static final String NETWORK_NAME_KEY = "networkName";
711         private static final String ALL_MISMATCHED_NETWORK_KEY = "allowMismatchedNetworkNames";
712 
713         @NonNull private final String mNetworkName;
714         private final boolean mAllowMismatchedNetworkNames;
715 
716         /** @hide */
717         @VisibleForTesting
EapAkaPrimeConfig( int subId, @UiccAppType int apptype, @NonNull String networkName, boolean allowMismatchedNetworkNames)718         public EapAkaPrimeConfig(
719                 int subId,
720                 @UiccAppType int apptype,
721                 @NonNull String networkName,
722                 boolean allowMismatchedNetworkNames) {
723             super(EAP_TYPE_AKA_PRIME, subId, apptype, null);
724 
725             Objects.requireNonNull(networkName, "networkName must not be null");
726 
727             mNetworkName = networkName;
728             mAllowMismatchedNetworkNames = allowMismatchedNetworkNames;
729         }
730 
731         /**
732          * Constructs this object by deserializing a PersistableBundle
733          *
734          * @hide
735          */
736         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)737         public static EapAkaPrimeConfig fromPersistableBundle(@NonNull PersistableBundle in) {
738             Objects.requireNonNull(in, "PersistableBundle is null");
739             return new EapAkaPrimeConfig(
740                     in.getInt(SUB_ID_KEY),
741                     in.getInt(APP_TYPE_KEY),
742                     in.getString(NETWORK_NAME_KEY),
743                     in.getBoolean(ALL_MISMATCHED_NETWORK_KEY));
744         }
745 
746         /**
747          * Serializes this object to a PersistableBundle
748          *
749          * @hide
750          */
751         @Override
752         @NonNull
toPersistableBundle()753         protected PersistableBundle toPersistableBundle() {
754             final PersistableBundle result = super.toPersistableBundle();
755             result.putString(NETWORK_NAME_KEY, mNetworkName);
756             result.putBoolean(ALL_MISMATCHED_NETWORK_KEY, mAllowMismatchedNetworkNames);
757 
758             return result;
759         }
760 
761         /**
762          * Retrieves the UICC app type
763          *
764          * @return the {@link UiccAppType} constant
765          */
766         @NonNull
getNetworkName()767         public String getNetworkName() {
768             return mNetworkName;
769         }
770 
771         /**
772          * Checks if mismatched network names are allowed
773          *
774          * @return whether network name mismatches are allowed
775          */
allowsMismatchedNetworkNames()776         public boolean allowsMismatchedNetworkNames() {
777             return mAllowMismatchedNetworkNames;
778         }
779 
780         /** @hide */
781         @Override
hashCode()782         public int hashCode() {
783             return Objects.hash(super.hashCode(), mNetworkName, mAllowMismatchedNetworkNames);
784         }
785 
786         /** @hide */
787         @Override
equals(Object o)788         public boolean equals(Object o) {
789             if (!super.equals(o) || !(o instanceof EapAkaPrimeConfig)) {
790                 return false;
791             }
792 
793             EapAkaPrimeConfig other = (EapAkaPrimeConfig) o;
794 
795             return mNetworkName.equals(other.mNetworkName)
796                     && mAllowMismatchedNetworkNames == other.mAllowMismatchedNetworkNames;
797         }
798     }
799 
800     /**
801      * EapMsChapV2Config represents the configs needed for an EAP MSCHAPv2 session.
802      */
803     public static class EapMsChapV2Config extends EapMethodConfig {
804         private static final String USERNAME_KEY = "username";
805         private static final String PASSWORD_KEY = "password";
806 
807         @NonNull private final String mUsername;
808         @NonNull private final String mPassword;
809 
810         /** @hide */
811         @VisibleForTesting
EapMsChapV2Config(String username, String password)812         public EapMsChapV2Config(String username, String password) {
813             super(EAP_TYPE_MSCHAP_V2);
814 
815             Objects.requireNonNull(username, "username must not be null");
816             Objects.requireNonNull(password, "password must not be null");
817 
818             mUsername = username;
819             mPassword = password;
820         }
821 
822         /**
823          * Constructs this object by deserializing a PersistableBundle
824          *
825          * @hide
826          */
827         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)828         public static EapMsChapV2Config fromPersistableBundle(@NonNull PersistableBundle in) {
829             Objects.requireNonNull(in, "PersistableBundle is null");
830             return new EapMsChapV2Config(in.getString(USERNAME_KEY), in.getString(PASSWORD_KEY));
831         }
832 
833         /**
834          * Serializes this object to a PersistableBundle
835          *
836          * @hide
837          */
838         @Override
839         @NonNull
toPersistableBundle()840         protected PersistableBundle toPersistableBundle() {
841             final PersistableBundle result = super.toPersistableBundle();
842             result.putString(USERNAME_KEY, mUsername);
843             result.putString(PASSWORD_KEY, mPassword);
844 
845             return result;
846         }
847 
848         /**
849          * Retrieves the username
850          *
851          * @return the username to be used by MSCHAPV2
852          */
853         @NonNull
getUsername()854         public String getUsername() {
855             return mUsername;
856         }
857 
858         /**
859          * Retrieves the password
860          *
861          * @return the password to be used by MSCHAPV2
862          */
863         @NonNull
getPassword()864         public String getPassword() {
865             return mPassword;
866         }
867 
868         /** @hide */
869         @Override
hashCode()870         public int hashCode() {
871             return Objects.hash(super.hashCode(), mUsername, mPassword);
872         }
873 
874         /** @hide */
875         @Override
equals(Object o)876         public boolean equals(Object o) {
877             if (!super.equals(o) || !(o instanceof EapMsChapV2Config)) {
878                 return false;
879             }
880 
881             EapMsChapV2Config other = (EapMsChapV2Config) o;
882 
883             return mUsername.equals(other.mUsername) && mPassword.equals(other.mPassword);
884         }
885     }
886 
887     /**
888      * EapTtlsConfig represents the configs needed for an EAP-TTLS session.
889      */
890     public static class EapTtlsConfig extends EapMethodConfig {
891         private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
892         private static final String EAP_SESSION_CONFIG_KEY = "EAP_SESSION_CONFIG_KEY";
893 
894         @Nullable private final TrustAnchor mOverrideTrustAnchor;
895         @NonNull private final EapSessionConfig mInnerEapSessionConfig;
896 
897         /** @hide */
898         @VisibleForTesting
EapTtlsConfig( @ullable X509Certificate serverCaCert, @NonNull EapSessionConfig innerEapSessionConfig)899         public EapTtlsConfig(
900                 @Nullable X509Certificate serverCaCert,
901                 @NonNull EapSessionConfig innerEapSessionConfig) {
902             super(EAP_TYPE_TTLS);
903             mInnerEapSessionConfig =
904                     Objects.requireNonNull(
905                             innerEapSessionConfig, "innerEapSessionConfig must not be null");
906             if (mInnerEapSessionConfig.getEapConfigs().containsKey(EAP_TYPE_TTLS)) {
907                 throw new IllegalArgumentException("Recursive EAP-TTLS method configs not allowed");
908             }
909 
910             mOverrideTrustAnchor =
911                     (serverCaCert == null)
912                             ? null
913                             : new TrustAnchor(serverCaCert, null /* nameConstraints */);
914         }
915 
916         /**
917          * Constructs this object by deserializing a PersistableBundle.
918          *
919          * @hide
920          */
921         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)922         public static EapTtlsConfig fromPersistableBundle(@NonNull PersistableBundle in) {
923             Objects.requireNonNull(in, "PersistableBundle is null");
924 
925             PersistableBundle trustCertBundle = in.getPersistableBundle(TRUST_CERT_KEY);
926             X509Certificate caCert = null;
927             if (trustCertBundle != null) {
928                 byte[] encodedCert = PersistableBundleUtils.toByteArray(trustCertBundle);
929                 caCert = IkeCertUtils.certificateFromByteArray(encodedCert);
930             }
931 
932             PersistableBundle eapSessionConfigBundle =
933                     in.getPersistableBundle(EAP_SESSION_CONFIG_KEY);
934             Objects.requireNonNull(eapSessionConfigBundle, "eapSessionConfigBundle is null");
935             EapSessionConfig eapSessionConfig =
936                     EapSessionConfig.fromPersistableBundle(eapSessionConfigBundle);
937 
938             return new EapTtlsConfig(caCert, eapSessionConfig);
939         }
940 
941         /**
942          * Serializes this object to a PersistableBundle.
943          *
944          * @hide
945          */
946         @Override
947         @NonNull
toPersistableBundle()948         protected PersistableBundle toPersistableBundle() {
949             final PersistableBundle result = super.toPersistableBundle();
950 
951             try {
952                 if (mOverrideTrustAnchor != null) {
953                     result.putPersistableBundle(
954                             TRUST_CERT_KEY,
955                             PersistableBundleUtils.fromByteArray(
956                                     mOverrideTrustAnchor.getTrustedCert().getEncoded()));
957                 }
958 
959                 result.putPersistableBundle(
960                         EAP_SESSION_CONFIG_KEY, mInnerEapSessionConfig.toPersistableBundle());
961             } catch (CertificateEncodingException e) {
962                 throw new IllegalArgumentException("Fail to encode the certificate");
963             }
964 
965             return result;
966         }
967 
968         /** @hide */
969         @Override
isEapOnlySafeMethod()970         public boolean isEapOnlySafeMethod() {
971             return true;
972         }
973 
974         /**
975          * Retrieves the provided CA certificate for validating the remote certificate(s)
976          *
977          * @return the CA certificate for validating the received server certificate or null if the
978          *     system default is preferred
979          */
980         @Nullable
getServerCaCert()981         public X509Certificate getServerCaCert() {
982             return (mOverrideTrustAnchor == null) ? null : mOverrideTrustAnchor.getTrustedCert();
983         }
984 
985         /**
986          * Retrieves the inner EAP session config
987          *
988          * @return an EapSessionConfig representing the config for tunneled EAP authentication
989          */
990         @NonNull
getInnerEapSessionConfig()991         public EapSessionConfig getInnerEapSessionConfig() {
992             return mInnerEapSessionConfig;
993         }
994 
995         /** @hide */
996         @Override
hashCode()997         public int hashCode() {
998             // Use #getTrustedCert() because TrustAnchor does not override #hashCode()
999 
1000             return Objects.hash(
1001                     super.hashCode(),
1002                     mInnerEapSessionConfig,
1003                     (mOverrideTrustAnchor == null) ? null : mOverrideTrustAnchor.getTrustedCert());
1004         }
1005 
1006         /** @hide */
1007         @Override
equals(Object o)1008         public boolean equals(Object o) {
1009             if (!super.equals(o) || !(o instanceof EapTtlsConfig)) {
1010                 return false;
1011             }
1012 
1013             EapTtlsConfig other = (EapTtlsConfig) o;
1014 
1015             if (!Objects.equals(mInnerEapSessionConfig, other.mInnerEapSessionConfig)) {
1016                 return false;
1017             }
1018 
1019             if (mOverrideTrustAnchor == null && other.mOverrideTrustAnchor == null) {
1020                 return true;
1021             }
1022 
1023             return mOverrideTrustAnchor != null
1024                     && other.mOverrideTrustAnchor != null
1025                     && Objects.equals(
1026                             mOverrideTrustAnchor.getTrustedCert(),
1027                             other.mOverrideTrustAnchor.getTrustedCert());
1028         }
1029     }
1030 
1031     /**
1032      * EapAkaOption represents optional configurations for EAP AKA authentication.
1033      */
1034     public static final class EapAkaOption {
1035         /** @hide */
1036         private static final String REAUTH_ID_KEY = "reauthId";
1037 
1038         /** @hide */
1039         private final byte[] mReauthId;
1040 
1041         /** @hide */
1042         @VisibleForTesting
EapAkaOption(@ullable byte[] reauthId)1043         public EapAkaOption(@Nullable byte[] reauthId) {
1044             if (reauthId != null) {
1045                 mReauthId = new byte[reauthId.length];
1046                 System.arraycopy(reauthId, 0, mReauthId, 0, reauthId.length);
1047             } else {
1048                 mReauthId = null;
1049             }
1050         }
1051 
1052         /**
1053          * Constructs this object by deserializing a PersistableBundle
1054          *
1055          * @hide
1056          */
1057         @NonNull
fromPersistableBundle(@onNull PersistableBundle in)1058         public static EapAkaOption fromPersistableBundle(@NonNull PersistableBundle in) {
1059             Objects.requireNonNull(in, "PersistableBundle is null");
1060 
1061             EapAkaOption.Builder builder = new EapAkaOption.Builder();
1062             PersistableBundle reauthIdBundle = in.getPersistableBundle(REAUTH_ID_KEY);
1063             if (reauthIdBundle != null) {
1064                 byte[] reauthId = PersistableBundleUtils.toByteArray(reauthIdBundle);
1065                 builder.setReauthId(reauthId);
1066             }
1067 
1068             return builder.build();
1069         }
1070 
1071         /**
1072          * Serializes this object to a PersistableBundle
1073          *
1074          * @hide
1075          */
1076         @NonNull
toPersistableBundle()1077         protected PersistableBundle toPersistableBundle() {
1078             final PersistableBundle result = new PersistableBundle();
1079 
1080             if (mReauthId != null) {
1081                 result.putPersistableBundle(
1082                         REAUTH_ID_KEY, PersistableBundleUtils.fromByteArray(mReauthId));
1083             }
1084             return result;
1085         }
1086 
1087         /**
1088          * Retrieves the re-authentication ID
1089          *
1090          * @return the re-authentication ID
1091          */
1092         @Nullable
getReauthId()1093         public byte[] getReauthId() {
1094             return mReauthId;
1095         }
1096 
1097         /** @hide */
1098         @Override
hashCode()1099         public int hashCode() {
1100             return Objects.hash(super.hashCode(), mReauthId);
1101         }
1102 
1103         /** @hide */
1104         @Override
equals(Object o)1105         public boolean equals(Object o) {
1106             if (!(o instanceof EapAkaOption)) {
1107                 return false;
1108             }
1109 
1110             EapAkaOption other = (EapAkaOption) o;
1111 
1112             return Arrays.equals(mReauthId, other.mReauthId);
1113         }
1114 
1115         /**
1116          * This class can be used to incrementally construct an {@link EapAkaOption}.
1117          */
1118         public static final class Builder {
1119             byte[] mReauthId;
1120 
1121             /**
1122              * Set fast re-authentication ID
1123              *
1124              * <p>If keys are found matching the combination of reauthId and permanent ID,
1125              * re-authentication will be attempted.
1126              *
1127              * <p>Permanent ID MUST be set in setEapIdentity
1128              *
1129              * <p>Upon session establishment, new re-authentication IDs will be listed in the
1130              * EapAkaInfo returned as part of IkeSessionCallback#onOpened().
1131              *
1132              * <p>Reauthentication is generally considered less secure, as it does not prove the
1133              * existence of the full credentials, and should be used only when a strong correlation
1134              * can be provided to the full authentication (eg shared keys from previous
1135              * authentication runs)
1136              *
1137              * @see <a href="https://datatracker.ietf.org/doc/html/rfc4187#section-5">RFC 4186,
1138              *     Extensible Authentication Protocol Method for 3rd Generation Authentication and
1139              *     Key Agreement (EAP-AKA)</a>
1140              *
1141              * @param reauthId re-authentication ID encoded with UTF-8
1142              * @return Builder this, to facilitate chaining.
1143              */
1144             @NonNull
setReauthId(@onNull byte[] reauthId)1145             public Builder setReauthId(@NonNull byte[] reauthId) {
1146                 mReauthId = reauthId;
1147                 return this;
1148             }
1149 
1150             /**
1151              * Constructs and returns an EapAkaOption with the configurations applied to this
1152              * Builder.
1153              *
1154              * @return the EapAkaOption constructed by this Builder.
1155              */
1156             @NonNull
build()1157             public EapAkaOption build() {
1158                 return new EapAkaOption(mReauthId);
1159             }
1160         }
1161     }
1162 
1163     /**
1164      * Checks if all the methods in the session are EAP-only safe
1165      *
1166      * @return whether all the methods in the session are EAP-only safe
1167      *
1168      * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998#section 4, for safe eap
1169      * methods</a>
1170      *
1171      * @hide
1172      */
areAllMethodsEapOnlySafe()1173     public boolean areAllMethodsEapOnlySafe() {
1174         for (Map.Entry<Integer, EapMethodConfig> eapConfigsEntry : mEapConfigs.entrySet()) {
1175             if (!eapConfigsEntry.getValue().isEapOnlySafeMethod()) {
1176                 return false;
1177             }
1178         }
1179 
1180         return true;
1181     }
1182 }
1183