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