From f14e3e03a6025b642e604ad167e40523a764aa67 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Fri, 19 Feb 2021 15:37:01 -0800 Subject: [PATCH 1/8] Support converting IkeSaProposal to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(new tests added) Change-Id: I108e5a946296f65bbe92549464aa68a2ec453659 --- .../IkeSaProposalUtils.java | 87 +++++++++++++++++ .../SaProposalUtilsBase.java | 96 +++++++++++++++++++ .../SaProposalUtilsTest.java | 55 +++++++++++ 3 files changed, 238 insertions(+) create mode 100644 core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java create mode 100644 core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java create mode 100644 tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java 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 0000000000000..1459671f4136a --- /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 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 0000000000000..0c9ee84327988 --- /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 encryptAlgoKeyLenPairs = new ArrayList<>(); + for (Pair 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/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java new file mode 100644 index 0000000000000..d3d3a2c5c8a25 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java @@ -0,0 +1,55 @@ +/* + * 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.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); + } +} From 6ce7d0db644410d19874e20f2783734ec0512724 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Fri, 19 Feb 2021 17:03:51 -0800 Subject: [PATCH 2/8] Support converting ChildSaProposal to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(new tests added) Change-Id: I726a29a65b10b23718fe3b1658d70a8b84cfd2de --- .../ChildSaProposalUtils.java | 73 +++++++++++++++++++ .../SaProposalUtilsTest.java | 21 ++++++ 2 files changed, 94 insertions(+) create mode 100644 core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java 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 0000000000000..ce5ec75f01a2e --- /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 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/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java index d3d3a2c5c8a25..e9e8e2444d5f4 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java @@ -18,6 +18,7 @@ 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; @@ -52,4 +53,24 @@ public class SaProposalUtilsTest { assertEquals(proposal, resultProposal); } + + @Test + public void testPersistableBundleEncodeDecodeIsLosslessChildProposal() throws Exception { + final ChildSaProposal proposal = + 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(); + + final PersistableBundle bundle = ChildSaProposalUtils.toPersistableBundle(proposal); + final SaProposal resultProposal = ChildSaProposalUtils.fromPersistableBundle(bundle); + + assertEquals(proposal, resultProposal); + } } From 32fed8965b24f06f1448a3da9cb5299255a31e4a Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Fri, 19 Feb 2021 18:46:12 -0800 Subject: [PATCH 3/8] Support converting TunnelModeChildSessionParams to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: I4d27ac37ce750930cefa73de25e5deea74591d0e --- .../TunnelModeChildSessionParamsUtils.java | 117 ++++++++++++++++++ .../SaProposalUtilsTest.java | 24 ++-- ...TunnelModeChildSessionParamsUtilsTest.java | 91 ++++++++++++++ 3 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java create mode 100644 tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java 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 0000000000000..66a43309241c7 --- /dev/null +++ b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.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 com.android.internal.annotations.VisibleForTesting.Visibility; + +import android.annotation.NonNull; +import android.net.ipsec.ike.ChildSaProposal; +import android.net.ipsec.ike.IkeTrafficSelector; +import android.net.ipsec.ike.TunnelModeChildSessionParams; +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 TunnelModeChildSessionParams to/from PersistableBundle. + * + * @hide + */ +@VisibleForTesting(visibility = Visibility.PRIVATE) +public final class TunnelModeChildSessionParamsUtils { + 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"; + + /** Serializes a TunnelModeChildSessionParams to a PersistableBundle. */ + @NonNull + public static PersistableBundle toPersistableBundle(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()); + + // TODO: b/163604823 Support serializing configuration requests. + + return result; + } + + private static List 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 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)); + + // TODO: b/163604823 Support deserializing configuration requests. + + return builder.build(); + } +} diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java index e9e8e2444d5f4..8ae8692b4f75f 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java @@ -54,19 +54,21 @@ public class SaProposalUtilsTest { 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 = - 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(); + final ChildSaProposal proposal = buildTestChildSaProposal(); final PersistableBundle bundle = ChildSaProposalUtils.toPersistableBundle(proposal); final SaProposal resultProposal = ChildSaProposalUtils.fromPersistableBundle(bundle); 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 0000000000000..b757aa2b4e261 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java @@ -0,0 +1,91 @@ +/* + * 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.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.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); + } +} From ca4e2a4041c1be9e1d0cf3938a2438c89d3c794f Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Mon, 22 Feb 2021 11:25:07 -0800 Subject: [PATCH 4/8] Support converting TunnelModeConfigRequest to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: I9c53d4252edb0e8816c84d280b48c4725bd0edc8 --- .../TunnelModeChildSessionParamsUtils.java | 174 +++++++++++++++++- ...TunnelModeChildSessionParamsUtilsTest.java | 26 +++ 2 files changed, 197 insertions(+), 3 deletions(-) diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java index 66a43309241c7..e62acac14bd7d 100644 --- a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java +++ b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java @@ -16,17 +16,34 @@ 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; @@ -37,15 +54,99 @@ import java.util.Objects; */ @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(TunnelModeChildSessionParams params) { + public static PersistableBundle toPersistableBundle( + @NonNull TunnelModeChildSessionParams params) { final PersistableBundle result = new PersistableBundle(); final PersistableBundle saProposalBundle = @@ -68,7 +169,13 @@ public final class TunnelModeChildSessionParamsUtils { result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds()); result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds()); - // TODO: b/163604823 Support serializing configuration requests. + final List 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; } @@ -109,8 +216,69 @@ public final class TunnelModeChildSessionParamsUtils { 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 reqList = + PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new); - // TODO: b/163604823 Support deserializing configuration requests. + 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/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java index b757aa2b4e261..b3cd0ab80599c 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java @@ -16,6 +16,9 @@ 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; @@ -30,6 +33,8 @@ 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) @@ -88,4 +93,25 @@ public class TunnelModeChildSessionParamsUtilsTest { 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); + } } From 63db000ae05a578a4003e6918107a2b58c36a698 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Mon, 22 Feb 2021 17:28:13 -0800 Subject: [PATCH 5/8] Support converting EapSessionConfig to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: Iac7077b4ca37dfc07055aac5b3a8118610a27999 --- .../EapSessionConfigUtils.java | 112 ++++++++++++++++++ .../EapSessionConfigUtilsTest.java | 57 +++++++++ 2 files changed, 169 insertions(+) create mode 100644 core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java create mode 100644 tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java 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 0000000000000..e0047d08a82fe --- /dev/null +++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java @@ -0,0 +1,112 @@ +/* + * 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.EapMethodConfig; +import android.net.eap.EapSessionConfig.EapMsChapV2Config; +import android.os.PersistableBundle; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.vcn.util.PersistableBundleUtils; + +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_MSCHAP_V2_CONFIG_KEY = "EAP_MSCHAP_V2_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.getEapMsChapV2Config() != null) { + result.putPersistableBundle( + EAP_MSCHAP_V2_CONFIG_KEY, + EapMsChapV2ConfigUtils.toPersistableBundle(config.getEapMsChapV2Config())); + } + + // TODO: Handle other EAP methods: EAP-SIM, EAP-AKA, EAP-AKA', EAP-TTLS. + + 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 msChapV2Bundle = in.getPersistableBundle(EAP_MSCHAP_V2_CONFIG_KEY); + if (msChapV2Bundle != null) { + EapMsChapV2ConfigUtils.setBuilderByReadingPersistableBundle(msChapV2Bundle, builder); + } + + // TODO: Handle other EAP methods: EAP-SIM, EAP-AKA, EAP-AKA', EAP-TTLS. + 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 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)); + } + } +} 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 0000000000000..537202bc72e56 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java @@ -0,0 +1,57 @@ +/* + * 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.eap.EapSessionConfig; +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.nio.charset.StandardCharsets; + +@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 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); + } +} From 3344f916bd240cc2942a133cc13ea567ab84fe88 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Tue, 23 Feb 2021 16:13:49 -0800 Subject: [PATCH 6/8] Support converting EAP-SIM, AKA and AKA' to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: Ib4a0ed69b57df10c5ede9524d0b03f4f03d68919 --- .../EapSessionConfigUtils.java | 109 +++++++++++++++++- .../EapSessionConfigUtilsTest.java | 32 +++++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java index e0047d08a82fe..b2b98620729bf 100644 --- a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java +++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java @@ -16,12 +16,17 @@ 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.EapUiccConfig; import android.os.PersistableBundle; import com.android.internal.annotations.VisibleForTesting; @@ -37,7 +42,10 @@ import java.util.Objects; @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_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 @@ -47,13 +55,31 @@ public final class EapSessionConfigUtils { 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.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())); } - // TODO: Handle other EAP methods: EAP-SIM, EAP-AKA, EAP-AKA', EAP-TTLS. + if (config.getEapAkaPrimeConfig() != null) { + result.putPersistableBundle( + EAP_AKA_PRIME_CONFIG_KEY, + EapAkaPrimeConfigUtils.toPersistableBundle(config.getEapAkaPrimeConfig())); + } + + // TODO: Handle EAP-TTLS. return result; } @@ -69,12 +95,27 @@ public final class EapSessionConfigUtils { 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 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); } - // TODO: Handle other EAP methods: EAP-SIM, EAP-AKA, EAP-AKA', EAP-TTLS. + final PersistableBundle akaPrimeBundle = in.getPersistableBundle(EAP_AKA_PRIME_CONFIG_KEY); + if (akaPrimeBundle != null) { + EapAkaPrimeConfigUtils.setBuilderByReadingPersistableBundle(akaPrimeBundle, builder); + } + + // TODO: Handle EAP-TTLS. return builder.build(); } @@ -90,6 +131,70 @@ public final class EapSessionConfigUtils { } } + 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"; diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java index 537202bc72e56..860d723818b4e 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java @@ -16,6 +16,8 @@ package android.net.vcn.persistablebundleutils; +import static android.telephony.TelephonyManager.APPTYPE_USIM; + import static org.junit.Assert.assertEquals; import android.net.eap.EapSessionConfig; @@ -35,6 +37,9 @@ 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); @@ -54,4 +59,31 @@ public class EapSessionConfigUtilsTest { 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); + } } From a3d6e04d27a15c13c6fafcf7fae1b2555e60ea17 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Tue, 23 Feb 2021 16:40:18 -0800 Subject: [PATCH 7/8] Support converting EAP-TTLS to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: I1e338d9c8940636b38f4725787de30fbbc1323ec --- .../vcn/persistablebundleutils/CertUtils.java | 46 +++++++++++++ .../EapSessionConfigUtils.java | 65 ++++++++++++++++++- tests/vcn/assets/self-signed-ca.pem | 20 ++++++ .../EapSessionConfigUtilsTest.java | 24 +++++++ 4 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 core/java/android/net/vcn/persistablebundleutils/CertUtils.java create mode 100644 tests/vcn/assets/self-signed-ca.pem 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 0000000000000..b6036b4a6fd1b --- /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/EapSessionConfigUtils.java b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java index b2b98620729bf..853a52da766a5 100644 --- a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java +++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java @@ -26,12 +26,15 @@ 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; /** @@ -43,6 +46,7 @@ import java.util.Objects; 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"; @@ -61,6 +65,12 @@ public final class EapSessionConfigUtils { 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, @@ -79,8 +89,6 @@ public final class EapSessionConfigUtils { EapAkaPrimeConfigUtils.toPersistableBundle(config.getEapAkaPrimeConfig())); } - // TODO: Handle EAP-TTLS. - return result; } @@ -100,6 +108,11 @@ public final class EapSessionConfigUtils { 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); @@ -115,7 +128,6 @@ public final class EapSessionConfigUtils { EapAkaPrimeConfigUtils.setBuilderByReadingPersistableBundle(akaPrimeBundle, builder); } - // TODO: Handle EAP-TTLS. return builder.build(); } @@ -214,4 +226,51 @@ public final class EapSessionConfigUtils { 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/tests/vcn/assets/self-signed-ca.pem b/tests/vcn/assets/self-signed-ca.pem new file mode 100644 index 0000000000000..5135ea7077a81 --- /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 index 860d723818b4e..bc8e9d3200b61 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java @@ -23,13 +23,17 @@ 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 @@ -86,4 +90,24 @@ public class EapSessionConfigUtilsTest { 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); + } } From b8a6b4ef85c3ee4c9a4e407336e4c06fa43ad2a8 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Tue, 23 Feb 2021 22:39:32 -0800 Subject: [PATCH 8/8] Support converting IKE ID to/from PersistableBundle Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: I65dbc509f23334df2f22d6c4658e31acc9bef787 --- .../IkeIdentificationUtils.java | 143 ++++++++++++++++++ .../IkeIdentificationUtilsTest.java | 87 +++++++++++ 2 files changed, 230 insertions(+) create mode 100644 core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java create mode 100644 tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java 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 0000000000000..6acb34ebb78ee --- /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/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java new file mode 100644 index 0000000000000..4f3930f9b5afc --- /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"))); + } +}