summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/CertUtils.java46
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java73
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java276
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java143
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java87
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java96
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java285
-rw-r--r--tests/vcn/assets/self-signed-ca.pem20
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java113
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java87
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java78
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java117
12 files changed, 1421 insertions, 0 deletions
diff --git a/core/java/android/net/vcn/persistablebundleutils/CertUtils.java b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
new file mode 100644
index 000000000000..b6036b4a6fd1
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * CertUtils provides utility methods for constructing Certificate.
+ *
+ * @hide
+ */
+public class CertUtils {
+ private static final String CERT_TYPE_X509 = "X.509";
+
+ /** Decodes an ASN.1 DER encoded Certificate */
+ public static X509Certificate certificateFromByteArray(byte[] derEncoded) {
+ Objects.requireNonNull(derEncoded, "derEncoded is null");
+
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance(CERT_TYPE_X509);
+ InputStream in = new ByteArrayInputStream(derEncoded);
+ return (X509Certificate) certFactory.generateCertificate(in);
+ } catch (CertificateException e) {
+ throw new IllegalArgumentException("Fail to decode certificate", e);
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
new file mode 100644
index 000000000000..ce5ec75f01a2
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert ChildSaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class ChildSaProposalUtils extends SaProposalUtilsBase {
+ /** Serializes a ChildSaProposal to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(ChildSaProposal proposal) {
+ return SaProposalUtilsBase.toPersistableBundle(proposal);
+ }
+
+ /** Constructs a ChildSaProposal by deserializing a PersistableBundle. */
+ @NonNull
+ public static ChildSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final ChildSaProposal.Builder builder = new ChildSaProposal.Builder();
+
+ final PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
+ Objects.requireNonNull(encryptionBundle, "Encryption algo bundle was null");
+ final List<EncryptionAlgoKeyLenPair> encryptList =
+ PersistableBundleUtils.toList(encryptionBundle, EncryptionAlgoKeyLenPair::new);
+ for (EncryptionAlgoKeyLenPair t : encryptList) {
+ builder.addEncryptionAlgorithm(t.encryptionAlgo, t.keyLen);
+ }
+
+ final int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
+ Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array was null");
+ for (int algo : integrityAlgoIdArray) {
+ builder.addIntegrityAlgorithm(algo);
+ }
+
+ final int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
+ Objects.requireNonNull(dhGroupArray, "DH Group array was null");
+ for (int dh : dhGroupArray) {
+ builder.addDhGroup(dh);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
new file mode 100644
index 000000000000..853a52da766a
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.eap.EapSessionConfig;
+import android.net.eap.EapSessionConfig.EapAkaConfig;
+import android.net.eap.EapSessionConfig.EapAkaPrimeConfig;
+import android.net.eap.EapSessionConfig.EapMethodConfig;
+import android.net.eap.EapSessionConfig.EapMsChapV2Config;
+import android.net.eap.EapSessionConfig.EapSimConfig;
+import android.net.eap.EapSessionConfig.EapTtlsConfig;
+import android.net.eap.EapSessionConfig.EapUiccConfig;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert EapSessionConfig to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class EapSessionConfigUtils {
+ private static final String EAP_ID_KEY = "EAP_ID_KEY";
+ private static final String EAP_SIM_CONFIG_KEY = "EAP_SIM_CONFIG_KEY";
+ private static final String EAP_TTLS_CONFIG_KEY = "EAP_TTLS_CONFIG_KEY";
+ private static final String EAP_AKA_CONFIG_KEY = "EAP_AKA_CONFIG_KEY";
+ private static final String EAP_MSCHAP_V2_CONFIG_KEY = "EAP_MSCHAP_V2_CONFIG_KEY";
+ private static final String EAP_AKA_PRIME_CONFIG_KEY = "EAP_AKA_PRIME_CONFIG_KEY";
+
+ /** Serializes an EapSessionConfig to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapSessionConfig config) {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putPersistableBundle(
+ EAP_ID_KEY, PersistableBundleUtils.fromByteArray(config.getEapIdentity()));
+
+ if (config.getEapSimConfig() != null) {
+ result.putPersistableBundle(
+ EAP_SIM_CONFIG_KEY,
+ EapSimConfigUtils.toPersistableBundle(config.getEapSimConfig()));
+ }
+
+ if (config.getEapTtlsConfig() != null) {
+ result.putPersistableBundle(
+ EAP_TTLS_CONFIG_KEY,
+ EapTtlsConfigUtils.toPersistableBundle(config.getEapTtlsConfig()));
+ }
+
+ if (config.getEapAkaConfig() != null) {
+ result.putPersistableBundle(
+ EAP_AKA_CONFIG_KEY,
+ EapAkaConfigUtils.toPersistableBundle(config.getEapAkaConfig()));
+ }
+
+ if (config.getEapMsChapV2Config() != null) {
+ result.putPersistableBundle(
+ EAP_MSCHAP_V2_CONFIG_KEY,
+ EapMsChapV2ConfigUtils.toPersistableBundle(config.getEapMsChapV2Config()));
+ }
+
+ if (config.getEapAkaPrimeConfig() != null) {
+ result.putPersistableBundle(
+ EAP_AKA_PRIME_CONFIG_KEY,
+ EapAkaPrimeConfigUtils.toPersistableBundle(config.getEapAkaPrimeConfig()));
+ }
+
+ return result;
+ }
+
+ /** Constructs an EapSessionConfig by deserializing a PersistableBundle. */
+ @NonNull
+ public static EapSessionConfig fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final EapSessionConfig.Builder builder = new EapSessionConfig.Builder();
+
+ final PersistableBundle eapIdBundle = in.getPersistableBundle(EAP_ID_KEY);
+ Objects.requireNonNull(eapIdBundle, "EAP ID was null");
+ builder.setEapIdentity(PersistableBundleUtils.toByteArray(eapIdBundle));
+
+ final PersistableBundle simBundle = in.getPersistableBundle(EAP_SIM_CONFIG_KEY);
+ if (simBundle != null) {
+ EapSimConfigUtils.setBuilderByReadingPersistableBundle(simBundle, builder);
+ }
+
+ final PersistableBundle ttlsBundle = in.getPersistableBundle(EAP_TTLS_CONFIG_KEY);
+ if (ttlsBundle != null) {
+ EapTtlsConfigUtils.setBuilderByReadingPersistableBundle(ttlsBundle, builder);
+ }
+
+ final PersistableBundle akaBundle = in.getPersistableBundle(EAP_AKA_CONFIG_KEY);
+ if (akaBundle != null) {
+ EapAkaConfigUtils.setBuilderByReadingPersistableBundle(akaBundle, builder);
+ }
+
+ final PersistableBundle msChapV2Bundle = in.getPersistableBundle(EAP_MSCHAP_V2_CONFIG_KEY);
+ if (msChapV2Bundle != null) {
+ EapMsChapV2ConfigUtils.setBuilderByReadingPersistableBundle(msChapV2Bundle, builder);
+ }
+
+ final PersistableBundle akaPrimeBundle = in.getPersistableBundle(EAP_AKA_PRIME_CONFIG_KEY);
+ if (akaPrimeBundle != null) {
+ EapAkaPrimeConfigUtils.setBuilderByReadingPersistableBundle(akaPrimeBundle, builder);
+ }
+
+ return builder.build();
+ }
+
+ private static class EapMethodConfigUtils {
+ private static final String METHOD_TYPE = "METHOD_TYPE";
+
+ /** Serializes an EapMethodConfig to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapMethodConfig config) {
+ final PersistableBundle result = new PersistableBundle();
+ result.putInt(METHOD_TYPE, config.getMethodType());
+ return result;
+ }
+ }
+
+ private static class EapUiccConfigUtils extends EapMethodConfigUtils {
+ static final String SUB_ID_KEY = "SUB_ID_KEY";
+ static final String APP_TYPE_KEY = "APP_TYPE_KEY";
+
+ @NonNull
+ protected static PersistableBundle toPersistableBundle(@NonNull EapUiccConfig config) {
+ final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+ result.putInt(SUB_ID_KEY, config.getSubId());
+ result.putInt(APP_TYPE_KEY, config.getAppType());
+
+ return result;
+ }
+ }
+
+ private static final class EapSimConfigUtils extends EapUiccConfigUtils {
+ @NonNull
+ public static PersistableBundle toPersistableBundle(EapSimConfig config) {
+ return EapUiccConfigUtils.toPersistableBundle(config);
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapSimConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
+ }
+ }
+
+ private static class EapAkaConfigUtils extends EapUiccConfigUtils {
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapAkaConfig config) {
+ return EapUiccConfigUtils.toPersistableBundle(config);
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapAkaConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
+ }
+ }
+
+ private static final class EapAkaPrimeConfigUtils extends EapAkaConfigUtils {
+ private static final String NETWORK_NAME_KEY = "NETWORK_NAME_KEY";
+ private static final String ALL_MISMATCHED_NETWORK_KEY = "ALL_MISMATCHED_NETWORK_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapAkaPrimeConfig config) {
+ final PersistableBundle result = EapUiccConfigUtils.toPersistableBundle(config);
+ result.putString(NETWORK_NAME_KEY, config.getNetworkName());
+ result.putBoolean(ALL_MISMATCHED_NETWORK_KEY, config.allowsMismatchedNetworkNames());
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapAkaPrimeConfig(
+ in.getInt(SUB_ID_KEY),
+ in.getInt(APP_TYPE_KEY),
+ in.getString(NETWORK_NAME_KEY),
+ in.getBoolean(ALL_MISMATCHED_NETWORK_KEY));
+ }
+ }
+
+ private static final class EapMsChapV2ConfigUtils extends EapMethodConfigUtils {
+ private static final String USERNAME_KEY = "USERNAME_KEY";
+ private static final String PASSWORD_KEY = "PASSWORD_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapMsChapV2Config config) {
+ final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+ result.putString(USERNAME_KEY, config.getUsername());
+ result.putString(PASSWORD_KEY, config.getPassword());
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapMsChapV2Config(in.getString(USERNAME_KEY), in.getString(PASSWORD_KEY));
+ }
+ }
+
+ private static final class EapTtlsConfigUtils extends EapMethodConfigUtils {
+ private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
+ private static final String EAP_SESSION_CONFIG_KEY = "EAP_SESSION_CONFIG_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapTtlsConfig config) {
+ final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+ try {
+ if (config.getServerCaCert() != null) {
+ final PersistableBundle caBundle =
+ PersistableBundleUtils.fromByteArray(
+ config.getServerCaCert().getEncoded());
+ result.putPersistableBundle(TRUST_CERT_KEY, caBundle);
+ }
+ } catch (CertificateEncodingException e) {
+ throw new IllegalStateException("Fail to encode the certificate");
+ }
+
+ result.putPersistableBundle(
+ EAP_SESSION_CONFIG_KEY,
+ EapSessionConfigUtils.toPersistableBundle(config.getInnerEapSessionConfig()));
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final PersistableBundle caBundle = in.getPersistableBundle(TRUST_CERT_KEY);
+ X509Certificate caCert = null;
+ if (caBundle != null) {
+ caCert =
+ CertUtils.certificateFromByteArray(
+ PersistableBundleUtils.toByteArray(caBundle));
+ }
+
+ final PersistableBundle eapSessionConfigBundle =
+ in.getPersistableBundle(EAP_SESSION_CONFIG_KEY);
+ Objects.requireNonNull(eapSessionConfigBundle, "Inner EAP Session Config was null");
+ final EapSessionConfig eapSessionConfig =
+ EapSessionConfigUtils.fromPersistableBundle(eapSessionConfigBundle);
+
+ builder.setEapTtlsConfig(caCert, eapSessionConfig);
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
new file mode 100644
index 000000000000..6acb34ebb78e
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.InetAddresses;
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.Objects;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Abstract utility class to convert IkeIdentification to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeIdentificationUtils {
+ private static final String ID_TYPE_KEY = "ID_TYPE_KEY";
+
+ private static final String DER_ASN1_DN_KEY = "DER_ASN1_DN_KEY";
+ private static final String FQDN_KEY = "FQDN_KEY";
+ private static final String KEY_ID_KEY = "KEY_ID_KEY";
+ private static final String IP4_ADDRESS_KEY = "IP4_ADDRESS_KEY";
+ private static final String IP6_ADDRESS_KEY = "IP6_ADDRESS_KEY";
+ private static final String RFC822_ADDRESS_KEY = "RFC822_ADDRESS_KEY";
+
+ private static final int ID_TYPE_DER_ASN1_DN = 1;
+ private static final int ID_TYPE_FQDN = 2;
+ private static final int ID_TYPE_IPV4_ADDR = 3;
+ private static final int ID_TYPE_IPV6_ADDR = 4;
+ private static final int ID_TYPE_KEY_ID = 5;
+ private static final int ID_TYPE_RFC822_ADDR = 6;
+
+ /** Serializes an IkeIdentification to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull IkeIdentification ikeId) {
+ if (ikeId instanceof IkeDerAsn1DnIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_DER_ASN1_DN);
+ IkeDerAsn1DnIdentification id = (IkeDerAsn1DnIdentification) ikeId;
+ result.putPersistableBundle(
+ DER_ASN1_DN_KEY,
+ PersistableBundleUtils.fromByteArray(id.derAsn1Dn.getEncoded()));
+ return result;
+ } else if (ikeId instanceof IkeFqdnIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_FQDN);
+ IkeFqdnIdentification id = (IkeFqdnIdentification) ikeId;
+ result.putString(FQDN_KEY, id.fqdn);
+ return result;
+ } else if (ikeId instanceof IkeIpv4AddrIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_IPV4_ADDR);
+ IkeIpv4AddrIdentification id = (IkeIpv4AddrIdentification) ikeId;
+ result.putString(IP4_ADDRESS_KEY, id.ipv4Address.getHostAddress());
+ return result;
+ } else if (ikeId instanceof IkeIpv6AddrIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_IPV6_ADDR);
+ IkeIpv6AddrIdentification id = (IkeIpv6AddrIdentification) ikeId;
+ result.putString(IP6_ADDRESS_KEY, id.ipv6Address.getHostAddress());
+ return result;
+ } else if (ikeId instanceof IkeKeyIdIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_KEY_ID);
+ IkeKeyIdIdentification id = (IkeKeyIdIdentification) ikeId;
+ result.putPersistableBundle(KEY_ID_KEY, PersistableBundleUtils.fromByteArray(id.keyId));
+ return result;
+ } else if (ikeId instanceof IkeRfc822AddrIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_RFC822_ADDR);
+ IkeRfc822AddrIdentification id = (IkeRfc822AddrIdentification) ikeId;
+ result.putString(RFC822_ADDRESS_KEY, id.rfc822Name);
+ return result;
+ } else {
+ throw new IllegalStateException("Unrecognized IkeIdentification subclass");
+ }
+ }
+
+ private static PersistableBundle createPersistableBundle(int idType) {
+ final PersistableBundle result = new PersistableBundle();
+ result.putInt(ID_TYPE_KEY, idType);
+ return result;
+ }
+
+ /** Constructs an IkeIdentification by deserializing a PersistableBundle. */
+ @NonNull
+ public static IkeIdentification fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ int idType = in.getInt(ID_TYPE_KEY);
+ switch (idType) {
+ case ID_TYPE_DER_ASN1_DN:
+ final PersistableBundle dnBundle = in.getPersistableBundle(DER_ASN1_DN_KEY);
+ Objects.requireNonNull(dnBundle, "ASN1 DN was null");
+ return new IkeDerAsn1DnIdentification(
+ new X500Principal(PersistableBundleUtils.toByteArray(dnBundle)));
+ case ID_TYPE_FQDN:
+ return new IkeFqdnIdentification(in.getString(FQDN_KEY));
+ case ID_TYPE_IPV4_ADDR:
+ final String v4AddressStr = in.getString(IP4_ADDRESS_KEY);
+ Objects.requireNonNull(v4AddressStr, "IPv4 address was null");
+ return new IkeIpv4AddrIdentification(
+ (Inet4Address) InetAddresses.parseNumericAddress(v4AddressStr));
+ case ID_TYPE_IPV6_ADDR:
+ final String v6AddressStr = in.getString(IP6_ADDRESS_KEY);
+ Objects.requireNonNull(v6AddressStr, "IPv6 address was null");
+ return new IkeIpv6AddrIdentification(
+ (Inet6Address) InetAddresses.parseNumericAddress(v6AddressStr));
+ case ID_TYPE_KEY_ID:
+ final PersistableBundle keyIdBundle = in.getPersistableBundle(KEY_ID_KEY);
+ Objects.requireNonNull(in, "Key ID was null");
+ return new IkeKeyIdIdentification(PersistableBundleUtils.toByteArray(keyIdBundle));
+ case ID_TYPE_RFC822_ADDR:
+ return new IkeRfc822AddrIdentification(in.getString(RFC822_ADDRESS_KEY));
+ default:
+ throw new IllegalStateException("Unrecognized IKE ID type: " + idType);
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
new file mode 100644
index 000000000000..1459671f4136
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert IkeSaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeSaProposalUtils extends SaProposalUtilsBase {
+ private static final String PRF_KEY = "PRF_KEY";
+
+ /** Serializes an IkeSaProposal to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(IkeSaProposal proposal) {
+ final PersistableBundle result = SaProposalUtilsBase.toPersistableBundle(proposal);
+
+ final int[] prfArray =
+ proposal.getPseudorandomFunctions().stream().mapToInt(i -> i).toArray();
+ result.putIntArray(PRF_KEY, prfArray);
+
+ return result;
+ }
+
+ /** Constructs an IkeSaProposal by deserializing a PersistableBundle. */
+ @NonNull
+ public static IkeSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final IkeSaProposal.Builder builder = new IkeSaProposal.Builder();
+
+ final PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
+ Objects.requireNonNull(encryptionBundle, "Encryption algo bundle was null");
+ final List<EncryptionAlgoKeyLenPair> encryptList =
+ PersistableBundleUtils.toList(encryptionBundle, EncryptionAlgoKeyLenPair::new);
+ for (EncryptionAlgoKeyLenPair t : encryptList) {
+ builder.addEncryptionAlgorithm(t.encryptionAlgo, t.keyLen);
+ }
+
+ final int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
+ Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array was null");
+ for (int algo : integrityAlgoIdArray) {
+ builder.addIntegrityAlgorithm(algo);
+ }
+
+ final int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
+ Objects.requireNonNull(dhGroupArray, "DH Group array was null");
+ for (int dh : dhGroupArray) {
+ builder.addDhGroup(dh);
+ }
+
+ final int[] prfArray = in.getIntArray(PRF_KEY);
+ Objects.requireNonNull(prfArray, "PRF array was null");
+ for (int prf : prfArray) {
+ builder.addPseudorandomFunction(prf);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
new file mode 100644
index 000000000000..0c9ee8432798
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.SaProposal;
+import android.os.PersistableBundle;
+import android.util.Pair;
+
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Abstract utility class to convert SaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+abstract class SaProposalUtilsBase {
+ static final String ENCRYPT_ALGO_KEY = "ENCRYPT_ALGO_KEY";
+ static final String INTEGRITY_ALGO_KEY = "INTEGRITY_ALGO_KEY";
+ static final String DH_GROUP_KEY = "DH_GROUP_KEY";
+
+ static class EncryptionAlgoKeyLenPair {
+ private static final String ALGO_KEY = "ALGO_KEY";
+ private static final String KEY_LEN_KEY = "KEY_LEN_KEY";
+
+ public final int encryptionAlgo;
+ public final int keyLen;
+
+ EncryptionAlgoKeyLenPair(int encryptionAlgo, int keyLen) {
+ this.encryptionAlgo = encryptionAlgo;
+ this.keyLen = keyLen;
+ }
+
+ EncryptionAlgoKeyLenPair(PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ this.encryptionAlgo = in.getInt(ALGO_KEY);
+ this.keyLen = in.getInt(KEY_LEN_KEY);
+ }
+
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(ALGO_KEY, encryptionAlgo);
+ result.putInt(KEY_LEN_KEY, keyLen);
+
+ return result;
+ }
+ }
+
+ /**
+ * Serializes common info of a SaProposal to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ static PersistableBundle toPersistableBundle(SaProposal proposal) {
+ final PersistableBundle result = new PersistableBundle();
+
+ final List<EncryptionAlgoKeyLenPair> encryptAlgoKeyLenPairs = new ArrayList<>();
+ for (Pair<Integer, Integer> pair : proposal.getEncryptionAlgorithms()) {
+ encryptAlgoKeyLenPairs.add(new EncryptionAlgoKeyLenPair(pair.first, pair.second));
+ }
+ final PersistableBundle encryptionBundle =
+ PersistableBundleUtils.fromList(
+ encryptAlgoKeyLenPairs, EncryptionAlgoKeyLenPair::toPersistableBundle);
+ result.putPersistableBundle(ENCRYPT_ALGO_KEY, encryptionBundle);
+
+ final int[] integrityAlgoIdArray =
+ proposal.getIntegrityAlgorithms().stream().mapToInt(i -> i).toArray();
+ result.putIntArray(INTEGRITY_ALGO_KEY, integrityAlgoIdArray);
+
+ final int[] dhGroupArray = proposal.getDhGroups().stream().mapToInt(i -> i).toArray();
+ result.putIntArray(DH_GROUP_KEY, dhGroupArray);
+
+ return result;
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
new file mode 100644
index 000000000000..e62acac14bd7
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Address;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Netmask;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6Address;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert TunnelModeChildSessionParams to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class TunnelModeChildSessionParamsUtils {
+ private static final String TAG = TunnelModeChildSessionParamsUtils.class.getSimpleName();
+
+ private static final String INBOUND_TS_KEY = "INBOUND_TS_KEY";
+ private static final String OUTBOUND_TS_KEY = "OUTBOUND_TS_KEY";
+ private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
+ private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
+ private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
+ private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
+
+ private static class ConfigRequest {
+ private static final int TYPE_IPV4_ADDRESS = 1;
+ private static final int TYPE_IPV6_ADDRESS = 2;
+ private static final int TYPE_IPV4_DNS = 3;
+ private static final int TYPE_IPV6_DNS = 4;
+ private static final int TYPE_IPV4_DHCP = 5;
+ private static final int TYPE_IPV4_NETMASK = 6;
+
+ private static final String TYPE_KEY = "type";
+ private static final String VALUE_KEY = "address";
+ private static final String IP6_PREFIX_LEN = "ip6PrefixLen";
+
+ private static final int PREFIX_LEN_UNUSED = -1;
+
+ public final int type;
+ public final int ip6PrefixLen;
+
+ // Null when it is an empty request
+ @Nullable public final InetAddress address;
+
+ ConfigRequest(TunnelModeChildConfigRequest config) {
+ int prefixLen = PREFIX_LEN_UNUSED;
+
+ if (config instanceof ConfigRequestIpv4Address) {
+ type = TYPE_IPV4_ADDRESS;
+ address = ((ConfigRequestIpv4Address) config).getAddress();
+ } else if (config instanceof ConfigRequestIpv6Address) {
+ type = TYPE_IPV6_ADDRESS;
+ address = ((ConfigRequestIpv6Address) config).getAddress();
+ if (address != null) {
+ prefixLen = ((ConfigRequestIpv6Address) config).getPrefixLength();
+ }
+ } else if (config instanceof ConfigRequestIpv4DnsServer) {
+ type = TYPE_IPV4_DNS;
+ address = null;
+ } else if (config instanceof ConfigRequestIpv6DnsServer) {
+ type = TYPE_IPV6_DNS;
+ address = null;
+ } else if (config instanceof ConfigRequestIpv4DhcpServer) {
+ type = TYPE_IPV4_DHCP;
+ address = null;
+ } else if (config instanceof ConfigRequestIpv4Netmask) {
+ type = TYPE_IPV4_NETMASK;
+ address = null;
+ } else {
+ throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
+ }
+
+ ip6PrefixLen = prefixLen;
+ }
+
+ ConfigRequest(PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ type = in.getInt(TYPE_KEY);
+ ip6PrefixLen = in.getInt(IP6_PREFIX_LEN);
+
+ String addressStr = in.getString(VALUE_KEY);
+ if (addressStr == null) {
+ address = null;
+ } else {
+ address = InetAddresses.parseNumericAddress(addressStr);
+ }
+ }
+
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(TYPE_KEY, type);
+ result.putInt(IP6_PREFIX_LEN, ip6PrefixLen);
+
+ if (address != null) {
+ result.putString(VALUE_KEY, address.getHostAddress());
+ }
+
+ return result;
+ }
+ }
+
+ /** Serializes a TunnelModeChildSessionParams to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(
+ @NonNull TunnelModeChildSessionParams params) {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle saProposalBundle =
+ PersistableBundleUtils.fromList(
+ params.getSaProposals(), ChildSaProposalUtils::toPersistableBundle);
+ result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
+
+ final PersistableBundle inTsBundle =
+ PersistableBundleUtils.fromList(
+ params.getInboundTrafficSelectors(),
+ IkeTrafficSelectorUtils::toPersistableBundle);
+ result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle);
+
+ final PersistableBundle outTsBundle =
+ PersistableBundleUtils.fromList(
+ params.getOutboundTrafficSelectors(),
+ IkeTrafficSelectorUtils::toPersistableBundle);
+ result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle);
+
+ result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
+ result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
+
+ final List<ConfigRequest> reqList = new ArrayList<>();
+ for (TunnelModeChildConfigRequest req : params.getConfigurationRequests()) {
+ reqList.add(new ConfigRequest(req));
+ }
+ final PersistableBundle configReqListBundle =
+ PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
+ result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
+
+ return result;
+ }
+
+ private static List<IkeTrafficSelector> getTsFromPersistableBundle(
+ PersistableBundle in, String key) {
+ PersistableBundle tsBundle = in.getPersistableBundle(key);
+ Objects.requireNonNull(tsBundle, "Value for key " + key + " was null");
+ return PersistableBundleUtils.toList(
+ tsBundle, IkeTrafficSelectorUtils::fromPersistableBundle);
+ }
+
+ /** Constructs a TunnelModeChildSessionParams by deserializing a PersistableBundle. */
+ @NonNull
+ public static TunnelModeChildSessionParams fromPersistableBundle(
+ @NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final TunnelModeChildSessionParams.Builder builder =
+ new TunnelModeChildSessionParams.Builder();
+
+ final PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
+ Objects.requireNonNull(proposalBundle, "SA proposal was null");
+ final List<ChildSaProposal> proposals =
+ PersistableBundleUtils.toList(
+ proposalBundle, ChildSaProposalUtils::fromPersistableBundle);
+ for (ChildSaProposal p : proposals) {
+ builder.addSaProposal(p);
+ }
+
+ for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, INBOUND_TS_KEY)) {
+ builder.addInboundTrafficSelectors(ts);
+ }
+
+ for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, OUTBOUND_TS_KEY)) {
+ builder.addOutboundTrafficSelectors(ts);
+ }
+
+ builder.setLifetimeSeconds(
+ in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
+ final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
+ Objects.requireNonNull(configReqListBundle, "Config request list was null");
+ final List<ConfigRequest> reqList =
+ PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
+
+ boolean hasIpv4AddressReq = false;
+ boolean hasIpv4NetmaskReq = false;
+ for (ConfigRequest req : reqList) {
+ switch (req.type) {
+ case ConfigRequest.TYPE_IPV4_ADDRESS:
+ hasIpv4AddressReq = true;
+ if (req.address == null) {
+ builder.addInternalAddressRequest(AF_INET);
+ } else {
+ builder.addInternalAddressRequest((Inet4Address) req.address);
+ }
+ break;
+ case ConfigRequest.TYPE_IPV6_ADDRESS:
+ if (req.address == null) {
+ builder.addInternalAddressRequest(AF_INET6);
+ } else {
+ builder.addInternalAddressRequest(
+ (Inet6Address) req.address, req.ip6PrefixLen);
+ }
+ break;
+ case ConfigRequest.TYPE_IPV4_NETMASK:
+ // Do not need to set netmask because it will be automatically set by the
+ // builder when an IPv4 internal address request is set.
+ hasIpv4NetmaskReq = true;
+ break;
+ case ConfigRequest.TYPE_IPV4_DNS:
+ if (req.address != null) {
+ Log.w(TAG, "Requesting a specific IPv4 DNS server is unsupported");
+ }
+ builder.addInternalDnsServerRequest(AF_INET);
+ break;
+ case ConfigRequest.TYPE_IPV6_DNS:
+ if (req.address != null) {
+ Log.w(TAG, "Requesting a specific IPv6 DNS server is unsupported");
+ }
+ builder.addInternalDnsServerRequest(AF_INET6);
+ break;
+ case ConfigRequest.TYPE_IPV4_DHCP:
+ if (req.address != null) {
+ Log.w(TAG, "Requesting a specific IPv4 DHCP server is unsupported");
+ }
+ builder.addInternalDhcpServerRequest(AF_INET);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unrecognized config request type: " + req.type);
+ }
+ }
+
+ if (hasIpv4AddressReq != hasIpv4NetmaskReq) {
+ Log.w(
+ TAG,
+ String.format(
+ "Expect IPv4 address request and IPv4 netmask request either both"
+ + " exist or both absent, but found hasIpv4AddressReq exists? %b,"
+ + " hasIpv4AddressReq exists? %b, ",
+ hasIpv4AddressReq, hasIpv4NetmaskReq));
+ }
+
+ return builder.build();
+ }
+}
diff --git a/tests/vcn/assets/self-signed-ca.pem b/tests/vcn/assets/self-signed-ca.pem
new file mode 100644
index 000000000000..5135ea7077a8
--- /dev/null
+++ b/tests/vcn/assets/self-signed-ca.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiagAwIBAgIICrKLpR7LxlowDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE
+BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxHDAaBgNVBAMTE2NhLnRlc3QuYW5kcm9p
+ZC5uZXQwHhcNMTkwNzE2MTcxNTUyWhcNMjkwNzEzMTcxNTUyWjA9MQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHQW5kcm9pZDEcMBoGA1UEAxMTY2EudGVzdC5hbmRyb2lk
+Lm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANsvTwad2Nie0VOy
+Xb1VtHL0R760Jm4vr14JWMcX4oiE6jUdTNdXQ0CGb65wvulP2aEeukFH0D/cvBMR
+Bv9+haEwo9/grIXg9ALNKp+GfuZYw/dfnUMHFn3g2+SUgP6BoMZc4lkHktjkDKxp
+99Q6h4NP/ip1labkhBeB9+Z6l78LTixKRKspNITWASJed9bjzshYxKHi6dJy3maQ
+1LwYKmK7PEGRpoDoT8yZhFbxsVDUojGnJKH1RLXVOn/psG6dI/+IsbTipAttj5zc
+g2VAD56PZG2Jd+vsup+g4Dy72hyy242x5c/H2LKZn4X0B0B+IXyii/ZVc+DJldQ5
+JqplOL8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
+HQYDVR0OBBYEFGYUzuvZUaVJl8mcxejuFiUNGcTfMA0GCSqGSIb3DQEBCwUAA4IB
+AQDQYeqjvHsK2ZqSqxakDp0nu36Plbj48Wvx1ru7GW2faz7i0w/Zkxh06zniILCb
+QJRjDebSTHc5SSbCFrRTvqagaLDhbH42/hQncWqIoJqW+pmznJET4JiBO0sqzm05
+yQWsLI/h9Ir28Y2g5N+XPBU0VVVejQqH4iI0iwQx7y7ABssQ0Xa/K73VPbeGaKd6
+Prt4wjJvTlIL2yE2+0MggJ3F2rNptL5SDpg3g+4/YQ6wVRBFil95kUqplEsCtU4P
+t+8RghiEmsRx/8CywKfZ5Hex87ODhsSDmDApcefbd5gxoWVkqxZUkPcKwYv1ucm8
+u4r44fj4/9W0Zeooav5Yoh1q
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
new file mode 100644
index 000000000000..bc8e9d3200b6
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static android.telephony.TelephonyManager.APPTYPE_USIM;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.eap.EapSessionConfig;
+import android.os.PersistableBundle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EapSessionConfigUtilsTest {
+ private static final byte[] EAP_ID = "test@android.net".getBytes(StandardCharsets.US_ASCII);
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final int SUB_ID = 1;
+ private static final String NETWORK_NAME = "android.net";
+ private static final boolean ALLOW_MISMATCHED_NETWORK_NAMES = true;
+
+ private EapSessionConfig.Builder createBuilderWithId() {
+ return new EapSessionConfig.Builder().setEapIdentity(EAP_ID);
+ }
+
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(EapSessionConfig config) {
+ final PersistableBundle bundle = EapSessionConfigUtils.toPersistableBundle(config);
+ final EapSessionConfig resultConfig = EapSessionConfigUtils.fromPersistableBundle(bundle);
+
+ assertEquals(config, resultConfig);
+ }
+
+ @Test
+ public void testSetEapMsChapV2EncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapSimEncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId().setEapSimConfig(SUB_ID, APPTYPE_USIM).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapAkaEncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId().setEapAkaConfig(SUB_ID, APPTYPE_USIM).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapAkaPrimeEncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId()
+ .setEapAkaPrimeConfig(
+ SUB_ID, APPTYPE_USIM, NETWORK_NAME, ALLOW_MISMATCHED_NETWORK_NAMES)
+ .build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapTtlsEncodeDecodeIsLossless() throws Exception {
+ final InputStream inputStream =
+ InstrumentationRegistry.getContext()
+ .getResources()
+ .getAssets()
+ .open("self-signed-ca.pem");
+ final CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ final X509Certificate trustedCa =
+ (X509Certificate) factory.generateCertificate(inputStream);
+
+ final EapSessionConfig innerConfig =
+ new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+
+ final EapSessionConfig config =
+ new EapSessionConfig.Builder().setEapTtlsConfig(trustedCa, innerConfig).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
new file mode 100644
index 000000000000..4f3930f9b5af
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.security.auth.x500.X500Principal;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IkeIdentificationUtilsTest {
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeIdentification id) {
+ final PersistableBundle bundle = IkeIdentificationUtils.toPersistableBundle(id);
+ final IkeIdentification result = IkeIdentificationUtils.fromPersistableBundle(bundle);
+
+ assertEquals(result, id);
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIpv4AddressId() throws Exception {
+ final Inet4Address ipv4Address = (Inet4Address) InetAddress.getByName("192.0.2.100");
+ verifyPersistableBundleEncodeDecodeIsLossless(new IkeIpv4AddrIdentification(ipv4Address));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIpv6AddressId() throws Exception {
+ final Inet6Address ipv6Address = (Inet6Address) InetAddress.getByName("2001:db8:2::100");
+ verifyPersistableBundleEncodeDecodeIsLossless(new IkeIpv6AddrIdentification(ipv6Address));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeRfc822AddrId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(new IkeFqdnIdentification("ike.android.net"));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeFqdnId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeRfc822AddrIdentification("androidike@example.com"));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeKeyId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeKeyIdIdentification("androidIkeKeyId".getBytes()));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeDerAsn1DnId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeDerAsn1DnIdentification(
+ new X500Principal("CN=small.server.test.android.net, O=Android, C=US")));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
new file mode 100644
index 000000000000..8ae8692b4f75
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.SaProposal;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SaProposalUtilsTest {
+ @Test
+ public void testPersistableBundleEncodeDecodeIsLosslessIkeProposal() throws Exception {
+ final IkeSaProposal proposal =
+ new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_3DES, SaProposal.KEY_LEN_UNUSED)
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
+ .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256)
+ .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+ .addDhGroup(SaProposal.DH_GROUP_3072_BIT_MODP)
+ .build();
+
+ final PersistableBundle bundle = IkeSaProposalUtils.toPersistableBundle(proposal);
+ final SaProposal resultProposal = IkeSaProposalUtils.fromPersistableBundle(bundle);
+
+ assertEquals(proposal, resultProposal);
+ }
+
+ /** Package private so that TunnelModeChildSessionParamsUtilsTest can use it */
+ static ChildSaProposal buildTestChildSaProposal() {
+ return new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_192)
+ .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+ .addDhGroup(SaProposal.DH_GROUP_4096_BIT_MODP)
+ .build();
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIsLosslessChildProposal() throws Exception {
+ final ChildSaProposal proposal = buildTestChildSaProposal();
+
+ final PersistableBundle bundle = ChildSaProposalUtils.toPersistableBundle(proposal);
+ final SaProposal resultProposal = ChildSaProposalUtils.fromPersistableBundle(bundle);
+
+ assertEquals(proposal, resultProposal);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
new file mode 100644
index 000000000000..b3cd0ab80599
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TunnelModeChildSessionParamsUtilsTest {
+ private TunnelModeChildSessionParams.Builder createBuilderMinimum() {
+ final ChildSaProposal saProposal = SaProposalUtilsTest.buildTestChildSaProposal();
+ return new TunnelModeChildSessionParams.Builder().addSaProposal(saProposal);
+ }
+
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(
+ TunnelModeChildSessionParams params) {
+ final PersistableBundle bundle =
+ TunnelModeChildSessionParamsUtils.toPersistableBundle(params);
+ final TunnelModeChildSessionParams result =
+ TunnelModeChildSessionParamsUtils.fromPersistableBundle(bundle);
+
+ assertEquals(params, result);
+ }
+
+ @Test
+ public void testMinimumParamsEncodeDecodeIsLossless() throws Exception {
+ final TunnelModeChildSessionParams sessionParams = createBuilderMinimum().build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+
+ @Test
+ public void testSetTsEncodeDecodeIsLossless() throws Exception {
+ final IkeTrafficSelector tsInbound =
+ new IkeTrafficSelector(
+ 16,
+ 65520,
+ InetAddresses.parseNumericAddress("192.0.2.100"),
+ InetAddresses.parseNumericAddress("192.0.2.101"));
+ final IkeTrafficSelector tsOutbound =
+ new IkeTrafficSelector(
+ 32,
+ 256,
+ InetAddresses.parseNumericAddress("192.0.2.200"),
+ InetAddresses.parseNumericAddress("192.0.2.255"));
+
+ final TunnelModeChildSessionParams sessionParams =
+ createBuilderMinimum()
+ .addInboundTrafficSelectors(tsInbound)
+ .addOutboundTrafficSelectors(tsOutbound)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+
+ @Test
+ public void testSetLifetimesEncodeDecodeIsLossless() throws Exception {
+ final int hardLifetime = (int) TimeUnit.HOURS.toSeconds(3L);
+ final int softLifetime = (int) TimeUnit.HOURS.toSeconds(1L);
+
+ final TunnelModeChildSessionParams sessionParams =
+ createBuilderMinimum().setLifetimeSeconds(hardLifetime, softLifetime).build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+
+ @Test
+ public void testSetConfigRequestsEncodeDecodeIsLossless() throws Exception {
+ final int ipv6PrefixLen = 64;
+ final Inet4Address ipv4Address =
+ (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.100");
+ final Inet6Address ipv6Address =
+ (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
+
+ final TunnelModeChildSessionParams sessionParams =
+ createBuilderMinimum()
+ .addInternalAddressRequest(AF_INET)
+ .addInternalAddressRequest(AF_INET6)
+ .addInternalAddressRequest(ipv4Address)
+ .addInternalAddressRequest(ipv6Address, ipv6PrefixLen)
+ .addInternalDnsServerRequest(AF_INET)
+ .addInternalDnsServerRequest(AF_INET6)
+ .addInternalDhcpServerRequest(AF_INET)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+}