diff --git a/core/java/android/net/vcn/VcnControlPlaneConfig.java b/core/java/android/net/vcn/VcnControlPlaneConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..a49999ed01edf117f4019981ccb85a0401ca24fb --- /dev/null +++ b/core/java/android/net/vcn/VcnControlPlaneConfig.java @@ -0,0 +1,107 @@ +/* + * 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; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.PersistableBundle; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * This class represents a control plane configuration for a Virtual Carrier Network connection. + * + * <p>Each {@link VcnGatewayConnectionConfig} must have a {@link VcnControlPlaneConfig}, containing + * all connection, authentication and authorization parameters required to establish a Gateway + * Connection with a remote endpoint. + * + * <p>A {@link VcnControlPlaneConfig} object can be shared by multiple {@link + * VcnGatewayConnectionConfig}(s) if they will used for connecting with the same remote endpoint. + * + * @see VcnManager + * @see VcnGatewayConnectionConfig + * + * @hide + */ +public abstract class VcnControlPlaneConfig { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({CONFIG_TYPE_IKE}) + public @interface ConfigType {} + + /** @hide */ + public static final int CONFIG_TYPE_IKE = 1; + + private static final String CONFIG_TYPE_KEY = "mConfigType"; + @ConfigType private final int mConfigType; + + /** + * Package private constructor. + * + * @hide + */ + VcnControlPlaneConfig(int configType) { + mConfigType = configType; + } + + /** + * Constructs a VcnControlPlaneConfig object by deserializing a PersistableBundle. + * + * @param in the {@link PersistableBundle} containing an {@link VcnControlPlaneConfig} object + * @hide + */ + public static VcnControlPlaneConfig fromPersistableBundle(@NonNull PersistableBundle in) { + Objects.requireNonNull(in, "PersistableBundle was null"); + + int configType = in.getInt(CONFIG_TYPE_KEY); + switch (configType) { + case CONFIG_TYPE_IKE: + return new VcnControlPlaneIkeConfig(in); + default: + throw new IllegalStateException("Unrecognized configType: " + configType); + } + } + + /** + * Converts this VcnControlPlaneConfig to a PersistableBundle. + * + * @hide + */ + @NonNull + public PersistableBundle toPersistableBundle() { + final PersistableBundle result = new PersistableBundle(); + result.putInt(CONFIG_TYPE_KEY, mConfigType); + return result; + } + + /** @hide */ + @Override + public int hashCode() { + return Objects.hash(mConfigType); + } + + /** @hide */ + @Override + public boolean equals(Object o) { + if (!(o instanceof VcnControlPlaneConfig)) { + return false; + } + + return mConfigType == ((VcnControlPlaneConfig) o).mConfigType; + } +} diff --git a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..f85a502f78b6b6c6d6e222cbb94641566f3c86f0 --- /dev/null +++ b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java @@ -0,0 +1,142 @@ +/* + * 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; + +import static android.net.vcn.VcnControlPlaneConfig.CONFIG_TYPE_IKE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.TunnelModeChildSessionParams; +import android.os.PersistableBundle; +import android.util.ArraySet; + +import java.util.Objects; + +/** + * This class is an IKEv2 control plane configuration for a Virtual Carrier Network connection. + * + * <p>This class is an extension of the {@link VcnControlPlaneConfig}, containing IKEv2-specific + * configuration, authentication and authorization parameters. + * + * @see VcnControlPlaneConfig + * + * @hide + */ +public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig { + private static final String TAG = VcnControlPlaneIkeConfig.class.getSimpleName(); + + // STOPSHIP: b/163604823 Make mIkeParams and mChildParams @NonNull when it is supported to + // construct mIkeParams and mChildParams from PersistableBundles. + + private static final String IKE_PARAMS_KEY = "mIkeParams"; + @Nullable private final IkeSessionParams mIkeParams; + + private static final String CHILD_PARAMS_KEY = "mChildParams"; + @Nullable private final TunnelModeChildSessionParams mChildParams; + + private static final ArraySet<String> BUNDLE_KEY_SET = new ArraySet<>(); + + { + BUNDLE_KEY_SET.add(IKE_PARAMS_KEY); + BUNDLE_KEY_SET.add(CHILD_PARAMS_KEY); + } + + /** + * Constructs a VcnControlPlaneIkeConfig object. + * + * @param ikeParams the IKE Session negotiation parameters + * @param childParams the tunnel mode Child Session negotiation parameters + */ + public VcnControlPlaneIkeConfig( + @NonNull IkeSessionParams ikeParams, + @NonNull TunnelModeChildSessionParams childParams) { + super(CONFIG_TYPE_IKE); + mIkeParams = ikeParams; + mChildParams = childParams; + validate(); + } + + /** + * Constructs a VcnControlPlaneIkeConfig object by deserializing a PersistableBundle. + * + * @param in the {@link PersistableBundle} containing an {@link VcnControlPlaneIkeConfig} object + * @hide + */ + public VcnControlPlaneIkeConfig(@NonNull PersistableBundle in) { + super(CONFIG_TYPE_IKE); + final PersistableBundle ikeParamsBundle = in.getPersistableBundle(IKE_PARAMS_KEY); + final PersistableBundle childParamsBundle = in.getPersistableBundle(CHILD_PARAMS_KEY); + + // STOPSHIP: b/163604823 Support constructing mIkeParams and mChildParams from + // PersistableBundles. + + mIkeParams = null; + mChildParams = null; + } + + private void validate() { + Objects.requireNonNull(mIkeParams, "mIkeParams was null"); + Objects.requireNonNull(mChildParams, "mChildParams was null"); + } + + /** + * Converts this VcnControlPlaneConfig to a PersistableBundle. + * + * @hide + */ + @Override + @NonNull + public PersistableBundle toPersistableBundle() { + final PersistableBundle result = super.toPersistableBundle(); + + // STOPSHIP: b/163604823 Support converting mIkeParams and mChildParams to + // PersistableBundles. + return result; + } + + /** Retrieves the IKE Session configuration. */ + @NonNull + public IkeSessionParams getIkeSessionParams() { + return mIkeParams; + } + + /** Retrieves the tunnel mode Child Session configuration. */ + @NonNull + public TunnelModeChildSessionParams getChildSessionParams() { + return mChildParams; + } + + /** @hide */ + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), mIkeParams, mChildParams); + } + + /** @hide */ + @Override + public boolean equals(Object o) { + if (!(o instanceof VcnControlPlaneIkeConfig)) { + return false; + } + + VcnControlPlaneIkeConfig other = (VcnControlPlaneIkeConfig) o; + return super.equals(o) + && Objects.equals(mIkeParams, other.mIkeParams) + && Objects.equals(mChildParams, other.mChildParams); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java new file mode 100644 index 0000000000000000000000000000000000000000..36f5e41462e8634efe7fb48cef87922a143c0f10 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2020 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; + +import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.ipsec.ike.ChildSaProposal; +import android.net.ipsec.ike.IkeFqdnIdentification; +import android.net.ipsec.ike.IkeSaProposal; +import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.SaProposal; +import android.net.ipsec.ike.TunnelModeChildSessionParams; + +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 VcnControlPlaneIkeConfigTest { + private static final IkeSessionParams IKE_PARAMS; + private static final TunnelModeChildSessionParams CHILD_PARAMS; + + static { + IkeSaProposal ikeProposal = + new IkeSaProposal.Builder() + .addEncryptionAlgorithm( + ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128) + .addDhGroup(DH_GROUP_2048_BIT_MODP) + .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC) + .build(); + + Context mockContext = mock(Context.class); + ConnectivityManager mockConnectManager = mock(ConnectivityManager.class); + doReturn(mockConnectManager) + .when(mockContext) + .getSystemService(Context.CONNECTIVITY_SERVICE); + doReturn(mock(Network.class)).when(mockConnectManager).getActiveNetwork(); + + final String serverHostname = "192.0.2.100"; + final String testLocalId = "test.client.com"; + final String testRemoteId = "test.server.com"; + final byte[] psk = "psk".getBytes(); + + IKE_PARAMS = + new IkeSessionParams.Builder(mockContext) + .setServerHostname(serverHostname) + .addSaProposal(ikeProposal) + .setLocalIdentification(new IkeFqdnIdentification(testLocalId)) + .setRemoteIdentification(new IkeFqdnIdentification(testRemoteId)) + .setAuthPsk(psk) + .build(); + + ChildSaProposal childProposal = + new ChildSaProposal.Builder() + .addEncryptionAlgorithm( + ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128) + .build(); + CHILD_PARAMS = + new TunnelModeChildSessionParams.Builder().addSaProposal(childProposal).build(); + } + + // Package private for use in VcnGatewayConnectionConfigTest + static VcnControlPlaneIkeConfig buildTestConfig() { + return new VcnControlPlaneIkeConfig(IKE_PARAMS, CHILD_PARAMS); + } + + @Test + public void testGetters() { + final VcnControlPlaneIkeConfig config = buildTestConfig(); + assertEquals(IKE_PARAMS, config.getIkeSessionParams()); + assertEquals(CHILD_PARAMS, config.getChildSessionParams()); + } + + @Test + public void testConstructConfigWithoutIkeParams() { + try { + new VcnControlPlaneIkeConfig(null, CHILD_PARAMS); + fail("Expect to fail because ikeParams was null"); + } catch (NullPointerException expected) { + } + } + + @Test + public void testBuilderConfigWithoutChildParams() { + try { + new VcnControlPlaneIkeConfig(IKE_PARAMS, null); + fail("Expect to fail because childParams was null"); + } catch (NullPointerException expected) { + } + } +}