Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f0f6b57 authored by Yan Yan's avatar Yan Yan
Browse files

Support converting IkeAuthConfig to/from PersistableBundle

Bug: 163604823
Test: FrameworksVcnTests(add new tests)
Change-Id: I97d9a7db423711dbccea412b96f069fe1dbd2779
parent 67b0387d
Loading
Loading
Loading
Loading
+103 −2
Original line number Diff line number Diff line
@@ -21,11 +21,14 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.NonNull;
import android.net.ipsec.ike.IkeSaProposal;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
import android.os.PersistableBundle;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.vcn.util.PersistableBundleUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

@@ -40,6 +43,8 @@ public final class IkeSessionParamsUtils {
    private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
    private static final String LOCAL_ID_KEY = "LOCAL_ID_KEY";
    private static final String REMOTE_ID_KEY = "REMOTE_ID_KEY";
    private static final String LOCAL_AUTH_KEY = "LOCAL_AUTH_KEY";
    private static final String REMOTE_AUTH_KEY = "REMOTE_AUTH_KEY";
    private static final String RETRANS_TIMEOUTS_KEY = "RETRANS_TIMEOUTS_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";
@@ -71,13 +76,18 @@ public final class IkeSessionParamsUtils {
                REMOTE_ID_KEY,
                IkeIdentificationUtils.toPersistableBundle(params.getRemoteIdentification()));

        result.putPersistableBundle(
                LOCAL_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getLocalAuthConfig()));
        result.putPersistableBundle(
                REMOTE_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getRemoteAuthConfig()));

        result.putIntArray(RETRANS_TIMEOUTS_KEY, params.getRetransmissionTimeoutsMillis());
        result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
        result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
        result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
        result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());

        // TODO: Handle authentication configuration, configuration requests and IKE options.
        // TODO: Handle configuration requests and IKE options.

        return result;
    }
@@ -107,14 +117,105 @@ public final class IkeSessionParamsUtils {
                IkeIdentificationUtils.fromPersistableBundle(
                        in.getPersistableBundle(REMOTE_ID_KEY)));

        AuthConfigUtils.setBuilderByReadingPersistableBundle(
                in.getPersistableBundle(LOCAL_AUTH_KEY),
                in.getPersistableBundle(REMOTE_AUTH_KEY),
                builder);

        builder.setRetransmissionTimeoutsMillis(in.getIntArray(RETRANS_TIMEOUTS_KEY));
        builder.setLifetimeSeconds(
                in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
        builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
        builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));

        // TODO: Handle authentication configuration, configuration requests and IKE options.
        // TODO: Handle configuration requests and IKE options.

        return builder.build();
    }

    private static final class AuthConfigUtils {
        private static final int IKE_AUTH_METHOD_PSK = 1;
        private static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2;
        private static final int IKE_AUTH_METHOD_EAP = 3;

        private static final String AUTH_METHOD_KEY = "AUTH_METHOD_KEY";

        @NonNull
        public static PersistableBundle toPersistableBundle(@NonNull IkeAuthConfig authConfig) {
            if (authConfig instanceof IkeAuthPskConfig) {
                IkeAuthPskConfig config = (IkeAuthPskConfig) authConfig;
                return IkeAuthPskConfigUtils.toPersistableBundle(
                        config, createPersistableBundle(IKE_AUTH_METHOD_PSK));
            } else {
                throw new IllegalStateException("Invalid IkeAuthConfig subclass");
            }

            // TODO: Handle EAP auth and digital signature based auth.
        }

        private static PersistableBundle createPersistableBundle(int type) {
            final PersistableBundle result = new PersistableBundle();
            result.putInt(AUTH_METHOD_KEY, type);
            return result;
        }

        public static void setBuilderByReadingPersistableBundle(
                @NonNull PersistableBundle localAuthBundle,
                @NonNull PersistableBundle remoteAuthBundle,
                @NonNull IkeSessionParams.Builder builder) {
            Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
            Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");

            final int localMethodType = localAuthBundle.getInt(AUTH_METHOD_KEY);
            final int remoteMethodType = remoteAuthBundle.getInt(AUTH_METHOD_KEY);
            switch (localMethodType) {
                case IKE_AUTH_METHOD_PSK:
                    if (remoteMethodType != IKE_AUTH_METHOD_PSK) {
                        throw new IllegalArgumentException(
                                "Expect remote auth method to be PSK based, but was "
                                        + remoteMethodType);
                    }
                    IkeAuthPskConfigUtils.setBuilderByReadingPersistableBundle(
                            localAuthBundle, remoteAuthBundle, builder);
                    break;
                default:
                    throw new IllegalArgumentException(
                            "Invalid EAP method type " + localMethodType);
            }
            // TODO: Handle EAP auth and digital signature based auth.
        }
    }

    private static final class IkeAuthPskConfigUtils {
        private static final String PSK_KEY = "PSK_KEY";

        @NonNull
        public static PersistableBundle toPersistableBundle(
                @NonNull IkeAuthPskConfig config, @NonNull PersistableBundle result) {
            result.putPersistableBundle(
                    PSK_KEY, PersistableBundleUtils.fromByteArray(config.getPsk()));
            return result;
        }

        public static void setBuilderByReadingPersistableBundle(
                @NonNull PersistableBundle localAuthBundle,
                @NonNull PersistableBundle remoteAuthBundle,
                @NonNull IkeSessionParams.Builder builder) {
            Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
            Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");

            final PersistableBundle localPskBundle = localAuthBundle.getPersistableBundle(PSK_KEY);
            final PersistableBundle remotePskBundle =
                    remoteAuthBundle.getPersistableBundle(PSK_KEY);
            Objects.requireNonNull(localAuthBundle, "Local PSK was null");
            Objects.requireNonNull(remoteAuthBundle, "Remote PSK was null");

            final byte[] localPsk = PersistableBundleUtils.toByteArray(localPskBundle);
            final byte[] remotePsk = PersistableBundleUtils.toByteArray(remotePskBundle);
            if (!Arrays.equals(localPsk, remotePsk)) {
                throw new IllegalArgumentException("Local PSK and remote PSK are different");
            }
            builder.setAuthPsk(localPsk);
        }
    }
}
+98 −0
Original line number Diff line number Diff line
/*
 * 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.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSessionParams;
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.InetAddress;
import java.util.concurrent.TimeUnit;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeSessionParamsUtilsTest {
    private static IkeSessionParams.Builder createBuilderMinimum() {
        final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");

        return new IkeSessionParams.Builder()
                .setServerHostname(serverAddress.getHostAddress())
                .addSaProposal(SaProposalUtilsTest.buildTestIkeSaProposal())
                .setLocalIdentification(new IkeFqdnIdentification("client.test.android.net"))
                .setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net"))
                .setAuthPsk("psk".getBytes());
    }

    private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeSessionParams params) {
        final PersistableBundle bundle = IkeSessionParamsUtils.toPersistableBundle(params);
        final IkeSessionParams result = IkeSessionParamsUtils.fromPersistableBundle(bundle);

        assertEquals(result, params);
    }

    @Test
    public void testEncodeRecodeParamsWithLifetimes() throws Exception {
        final int hardLifetime = (int) TimeUnit.HOURS.toSeconds(20L);
        final int softLifetime = (int) TimeUnit.HOURS.toSeconds(10L);
        final IkeSessionParams params =
                createBuilderMinimum().setLifetimeSeconds(hardLifetime, softLifetime).build();
        verifyPersistableBundleEncodeDecodeIsLossless(params);
    }

    @Test
    public void testEncodeRecodeParamsWithDpdDelay() throws Exception {
        final int dpdDelay = (int) TimeUnit.MINUTES.toSeconds(10L);
        final IkeSessionParams params = createBuilderMinimum().setDpdDelaySeconds(dpdDelay).build();

        verifyPersistableBundleEncodeDecodeIsLossless(params);
    }

    @Test
    public void testEncodeRecodeParamsWithNattKeepalive() throws Exception {
        final int nattKeepAliveDelay = (int) TimeUnit.MINUTES.toSeconds(5L);
        final IkeSessionParams params =
                createBuilderMinimum().setNattKeepAliveDelaySeconds(nattKeepAliveDelay).build();

        verifyPersistableBundleEncodeDecodeIsLossless(params);
    }

    @Test
    public void testEncodeRecodeParamsWithRetransmissionTimeouts() throws Exception {
        final int[] retransmissionTimeout = new int[] {500, 500, 500, 500, 500, 500};
        final IkeSessionParams params =
                createBuilderMinimum()
                        .setRetransmissionTimeoutsMillis(retransmissionTimeout)
                        .build();

        verifyPersistableBundleEncodeDecodeIsLossless(params);
    }

    @Test
    public void testEncodeRecodeParamsWithAuthPsk() throws Exception {
        final IkeSessionParams params = createBuilderMinimum().setAuthPsk("psk".getBytes()).build();
        verifyPersistableBundleEncodeDecodeIsLossless(params);
    }
}
+17 −13
Original line number Diff line number Diff line
@@ -32,10 +32,9 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SaProposalUtilsTest {
    @Test
    public void testPersistableBundleEncodeDecodeIsLosslessIkeProposal() throws Exception {
        final IkeSaProposal proposal =
                new IkeSaProposal.Builder()
    /** Package private so that IkeSessionParamsUtilsTest can use it */
    static IkeSaProposal buildTestIkeSaProposal() {
        return new IkeSaProposal.Builder()
                .addEncryptionAlgorithm(
                        SaProposal.ENCRYPTION_ALGORITHM_3DES, SaProposal.KEY_LEN_UNUSED)
                .addEncryptionAlgorithm(
@@ -47,6 +46,11 @@ public class SaProposalUtilsTest {
                .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
                .addDhGroup(SaProposal.DH_GROUP_3072_BIT_MODP)
                .build();
    }

    @Test
    public void testPersistableBundleEncodeDecodeIsLosslessIkeProposal() throws Exception {
        final IkeSaProposal proposal = buildTestIkeSaProposal();

        final PersistableBundle bundle = IkeSaProposalUtils.toPersistableBundle(proposal);
        final SaProposal resultProposal = IkeSaProposalUtils.fromPersistableBundle(bundle);