Support converting EapTtlsConfig to/from PersistableBundle am: 4f4708bc74 am: afeaab72c5

Original change: https://android-review.googlesource.com/c/platform/packages/modules/IPsec/+/1493167

Change-Id: I6ddb7c9ddf14af837b2ffd19bbf086459c52405f
diff --git a/src/java/android/net/eap/EapSessionConfig.java b/src/java/android/net/eap/EapSessionConfig.java
index 25ecfac..7ab7a8a 100644
--- a/src/java/android/net/eap/EapSessionConfig.java
+++ b/src/java/android/net/eap/EapSessionConfig.java
@@ -30,8 +30,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.eap.message.EapData.EapMethod;
+import com.android.internal.net.ipsec.ike.utils.IkeCertUtils;
 import com.android.server.vcn.util.PersistableBundleUtils;
 
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.TrustAnchor;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
@@ -375,6 +377,8 @@
                     return EapAkaPrimeConfig.fromPersistableBundle(in);
                 case EAP_TYPE_MSCHAP_V2:
                     return EapMsChapV2Config.fromPersistableBundle(in);
+                case EAP_TYPE_TTLS:
+                    return EapTtlsConfig.fromPersistableBundle(in);
                 default:
                     throw new IllegalArgumentException("Invalid EAP Type: " + methodType);
             }
@@ -745,6 +749,9 @@
      * @hide
      */
     public static class EapTtlsConfig extends EapMethodConfig {
+        private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
+        private static final String EAP_SESSION_CONFIG_KEY = "EAP_SESSION_CONFIG_KEY";
+
         @Nullable private final TrustAnchor mOverrideTrustAnchor;
         @NonNull private final EapSessionConfig mInnerEapSessionConfig;
 
@@ -767,6 +774,58 @@
                             : new TrustAnchor(serverCaCert, null /* nameConstraints */);
         }
 
+        /**
+         * Constructs this object by deserializing a PersistableBundle.
+         *
+         * @hide
+         */
+        @NonNull
+        public static EapTtlsConfig fromPersistableBundle(@NonNull PersistableBundle in) {
+            Objects.requireNonNull(in, "PersistableBundle is null");
+
+            PersistableBundle trustCertBundle = in.getPersistableBundle(TRUST_CERT_KEY);
+            X509Certificate caCert = null;
+            if (trustCertBundle != null) {
+                byte[] encodedCert = PersistableBundleUtils.toByteArray(trustCertBundle);
+                caCert = IkeCertUtils.certificateFromByteArray(encodedCert);
+            }
+
+            PersistableBundle eapSessionConfigBundle =
+                    in.getPersistableBundle(EAP_SESSION_CONFIG_KEY);
+            Objects.requireNonNull(eapSessionConfigBundle, "eapSessionConfigBundle is null");
+            EapSessionConfig eapSessionConfig =
+                    EapSessionConfig.fromPersistableBundle(eapSessionConfigBundle);
+
+            return new EapTtlsConfig(caCert, eapSessionConfig);
+        }
+
+        /**
+         * Serializes this object to a PersistableBundle.
+         *
+         * @hide
+         */
+        @Override
+        @NonNull
+        protected PersistableBundle toPersistableBundle() {
+            final PersistableBundle result = super.toPersistableBundle();
+
+            try {
+                if (mOverrideTrustAnchor != null) {
+                    result.putPersistableBundle(
+                            TRUST_CERT_KEY,
+                            PersistableBundleUtils.fromByteArray(
+                                    mOverrideTrustAnchor.getTrustedCert().getEncoded()));
+                }
+
+                result.putPersistableBundle(
+                        EAP_SESSION_CONFIG_KEY, mInnerEapSessionConfig.toPersistableBundle());
+            } catch (CertificateEncodingException e) {
+                throw new IllegalArgumentException("Fail to encode the certificate");
+            }
+
+            return result;
+        }
+
         /** @hide */
         @Override
         public boolean isEapOnlySafeMethod() {
@@ -795,6 +854,31 @@
         public EapSessionConfig getInnerEapSessionConfig() {
             return mInnerEapSessionConfig;
         }
+
+        /** @hide */
+        @Override
+        public int hashCode() {
+            // Use #getTrustedCert() because TrustAnchor does not override #hashCode()
+            return Objects.hash(
+                    super.hashCode(),
+                    mOverrideTrustAnchor.getTrustedCert(),
+                    mInnerEapSessionConfig);
+        }
+
+        /** @hide */
+        @Override
+        public boolean equals(Object o) {
+            if (!super.equals(o) || !(o instanceof EapTtlsConfig)) {
+                return false;
+            }
+
+            EapTtlsConfig other = (EapTtlsConfig) o;
+
+            return Objects.equals(
+                            mOverrideTrustAnchor.getTrustedCert(),
+                            other.mOverrideTrustAnchor.getTrustedCert())
+                    && Objects.equals(mInnerEapSessionConfig, other.mInnerEapSessionConfig);
+        }
     }
 
     /**
diff --git a/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java b/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java
index 02eb97c..906b446 100644
--- a/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java
+++ b/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java
@@ -166,6 +166,15 @@
         assertEquals(trustedCa, config.getServerCaCert());
     }
 
+    @Test
+    public void testPersistableBundleEncodeDecodeEapTtls() throws Exception {
+        EapSessionConfig innerConfig =
+                new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+        X509Certificate trustedCa = CertUtils.createCertFromPemFile("self-signed-ca-a.pem");
+
+        verifyPersistableBundleEncodeDecodeIsLossless(new EapTtlsConfig(trustedCa, innerConfig));
+    }
+
     @Test(expected = NullPointerException.class)
     public void testSetEapIdentityNull() {
         new EapSessionConfig.Builder().setEapIdentity(null);