diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-04-28 20:21:33 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-04-28 20:21:33 +0000 |
commit | 36e2a5887fe658e57741eb887194197e19d22242 (patch) | |
tree | dcd2a4ebf55e7c6f692cbed60509054eeaa46d98 | |
parent | db74d7937f3a9503f0e2590ec24c3bcfbf259ce0 (diff) | |
parent | c0736f6c497f1682a654a8bc1d72ff506afea72e (diff) | |
download | ike-q_tzdata_aml_297100000.tar.gz |
Snap for 6439596 from c0736f6c497f1682a654a8bc1d72ff506afea72e to qt-aml-tzdata-releaseq_tzdata_aml_297100400q_tzdata_aml_297100300q_tzdata_aml_297100000q_tzdata_aml_296200000q_tzdata_aml_295600118q_tzdata_aml_295600110q_tzdata_aml_295500002q_tzdata_aml_295500001q_tzdata_aml_294400310android-mainline-12.0.0_r54android-mainline-12.0.0_r111android-mainline-10.0.0_r13android-mainline-10.0.0_r12android-mainline-10.0.0_r11q_tzdata_aml_297100000android12-mainline-tzdata-releaseandroid10-mainline-tzdata-releaseandroid10-android13-mainline-tzdata-release
Change-Id: I9e05059edc6da35c519bdf4931a51c742d377b9e
288 files changed, 6245 insertions, 49554 deletions
diff --git a/Android.bp b/Android.bp deleted file mode 100644 index 8d2d16ff..00000000 --- a/Android.bp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 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. - -java_library { - name: "ike", - installable: true, - - aidl: { - local_include_dirs: ["src/java"], - }, - srcs: ["src/java/**/*.java"], -} diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..14e9e9ee --- /dev/null +++ b/Android.mk @@ -0,0 +1,30 @@ +# Copyright 2018 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src/java +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src/java) + +LOCAL_JAVA_LIBRARIES := bouncycastle NetworkStackBase + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := ike + +include $(BUILD_JAVA_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) @@ -1,5 +1,5 @@ benedictwong@google.com -ckesting@google.com +ek@google.com evitayan@google.com jchalard@google.com lorenzo@google.com diff --git a/TEST_MAPPING b/TEST_MAPPING index 71d96ff1..6534d884 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -3,10 +3,5 @@ { "name": "FrameworksIkeTests" } - ], - "postsubmit": [ - { - "name": "FrameworksIkeTests" - } ] }
\ No newline at end of file diff --git a/src/java/android/net/eap/EapSessionConfig.java b/src/java/android/net/eap/EapSessionConfig.java deleted file mode 100644 index a31a0fdf..00000000 --- a/src/java/android/net/eap/EapSessionConfig.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2019 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.eap; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; - -import android.telephony.TelephonyManager.UiccAppType; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.message.EapData.EapMethod; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * EapSessionConfig represents a container for EAP method configs to be used within an IKEv2 - * session. - * - * <p>The EAP authentication server decides which EAP method is used, so clients are encouraged to - * provide configs for several EAP methods. - */ -public final class EapSessionConfig { - @VisibleForTesting - static final byte[] DEFAULT_IDENTITY = new byte[0]; - - // IANA -> EapMethodConfig for that method - public final Map<Integer, EapMethodConfig> eapConfigs; - public final byte[] eapIdentity; - - @VisibleForTesting - public EapSessionConfig(Map<Integer, EapMethodConfig> eapConfigs, byte[] eapIdentity) { - this.eapConfigs = Collections.unmodifiableMap(eapConfigs); - this.eapIdentity = eapIdentity; - } - - /** This class can be used to incrementally construct an EapSessionConfig. */ - public static final class Builder { - private final Map<Integer, EapMethodConfig> mEapConfigs; - private byte[] mEapIdentity; - - /** - * Constructs and returns a new Builder for constructing an EapSessionConfig. - */ - public Builder() { - mEapConfigs = new HashMap<>(); - mEapIdentity = DEFAULT_IDENTITY; - } - - /** - * Sets the client's EAP Identity. - * - * @param eapIdentity byte[] representing the client's EAP Identity - * @return Builder this, to facilitate chaining. - */ - public Builder setEapIdentity(byte[] eapIdentity) { - this.mEapIdentity = eapIdentity.clone(); - return this; - } - - /** - * Sets the configuration for EAP SIM. - * - * @param subId int the client's subId to be authenticated - * @param apptype the {@link UiccAppType} apptype to be used for authentication - * @return Builder this, to facilitate chaining. - */ - public Builder setEapSimConfig(int subId, @UiccAppType int apptype) { - mEapConfigs.put(EAP_TYPE_SIM, new EapSimConfig(subId, apptype)); - return this; - } - - /** - * Sets the configuration for EAP AKA. - * - * @param subId int the client's subId to be authenticated - * @param apptype the {@link UiccAppType} apptype to be used for authentication - * @return Builder this, to facilitate chaining - */ - public Builder setEapAkaConfig(int subId, @UiccAppType int apptype) { - mEapConfigs.put(EAP_TYPE_AKA, new EapAkaConfig(subId, apptype)); - return this; - } - - /** - * Sets the configuration for EAP AKA'. - * - * @param subId int the client's subId to be authenticated - * @param apptype the {@link UiccAppType} apptype to be used for authentication - * @param networkName String the network name to be used for authentication. The String must - * be a UTF-8 String value - * @param allowMismatchedNetworkNames indicates whether the EAP library can ignore potential - * mismatches between the given network name and that received in an EAP-AKA' session. - * If false, mismatched network names will be handled as an Authentication Reject - * message. - * @return Builder this, to facilitate chaining - */ - public Builder setEapAkaPrimeConfig( - int subId, - @UiccAppType int apptype, - String networkName, - boolean allowMismatchedNetworkNames) { - mEapConfigs.put( - EAP_TYPE_AKA_PRIME, - new EapAkaPrimeConfig( - subId, apptype, networkName, allowMismatchedNetworkNames)); - return this; - } - - /** - * Sets the configuration for EAP MSCHAPv2. - * - * @param username String the client account's username to be authenticated - * @param password String the client account's password to be authenticated - * @return Builder this, to faciliate chaining - */ - public Builder setEapMsChapV2Config(String username, String password) { - mEapConfigs.put(EAP_TYPE_MSCHAP_V2, new EapMsChapV2Config(username, password)); - return this; - } - - /** - * Constructs and returns an EapSessionConfig with the configurations applied to this - * Builder. - * - * @return the EapSessionConfig constructed by this Builder - * @throws IllegalStateException iff no EAP methods have been configured - */ - public EapSessionConfig build() { - if (mEapConfigs.isEmpty()) { - throw new IllegalStateException("Must have at least one EAP method configured"); - } - - return new EapSessionConfig(mEapConfigs, mEapIdentity); - } - } - - /** EapMethodConfig represents a generic EAP method configuration. */ - public abstract static class EapMethodConfig { - @EapMethod public final int methodType; - - protected EapMethodConfig(@EapMethod int methodType) { - this.methodType = methodType; - } - } - - /** - * EapUiccConfig represents the configs needed for EAP methods that rely on UICC cards for - * authentication. - */ - public abstract static class EapUiccConfig extends EapMethodConfig { - public final int subId; - public final int apptype; - - private EapUiccConfig(@EapMethod int methodType, int subId, @UiccAppType int apptype) { - super(methodType); - this.subId = subId; - this.apptype = apptype; - } - } - - /** - * EapSimConfig represents the configs needed for an EAP SIM session. - */ - public static class EapSimConfig extends EapUiccConfig { - @VisibleForTesting - public EapSimConfig(int subId, @UiccAppType int apptype) { - super(EAP_TYPE_SIM, subId, apptype); - } - } - - /** - * EapAkaConfig represents the configs needed for an EAP AKA session. - */ - public static class EapAkaConfig extends EapUiccConfig { - @VisibleForTesting - public EapAkaConfig(int subId, @UiccAppType int apptype) { - super(EAP_TYPE_AKA, subId, apptype); - } - } - - /** - * EapAkaPrimeConfig represents the configs needed for an EAP-AKA' session. - */ - public static class EapAkaPrimeConfig extends EapAkaConfig { - public final String networkName; - public final boolean allowMismatchedNetworkNames; - - @VisibleForTesting - public EapAkaPrimeConfig( - int subId, - @UiccAppType int apptype, - String networkName, - boolean allowMismatchedNetworkNames) { - super(subId, apptype); - - this.networkName = networkName; - this.allowMismatchedNetworkNames = allowMismatchedNetworkNames; - } - } - - /** - * EapMsChapV2Config represents the configs needed for an EAP MSCHAPv2 session. - */ - public static class EapMsChapV2Config extends EapMethodConfig { - public final String username; - public final String password; - - @VisibleForTesting - public EapMsChapV2Config(String username, String password) { - super(EAP_TYPE_MSCHAP_V2); - - this.username = username; - this.password = password; - } - } -} diff --git a/src/java/android/net/ipsec/ike/ChildSaProposal.java b/src/java/android/net/ipsec/ike/ChildSaProposal.java deleted file mode 100644 index c8851a71..00000000 --- a/src/java/android/net/ipsec/ike/ChildSaProposal.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EsnTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform; - -import java.util.Arrays; -import java.util.List; - -/** - * ChildSaProposal represents a user configured set contains cryptograhic algorithms and key - * generating materials for negotiating an Child SA. - * - * <p>User must provide at least a valid ChildSaProposal when they are creating a new Child SA. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class ChildSaProposal extends SaProposal { - private final EsnTransform[] mEsns; - - /** - * Construct an instance of ChildSaProposal. - * - * <p>This constructor is either called by ChildSaPayload for building an inbound proposal from - * a decoded packet, or called by the inner Builder to build an outbound proposal from user - * provided parameters - * - * @param encryptionAlgos encryption algorithms - * @param integrityAlgos integrity algorithms - * @param dhGroups Diffie-Hellman Groups - * @param esns ESN policies - */ - public ChildSaProposal( - EncryptionTransform[] encryptionAlgos, - IntegrityTransform[] integrityAlgos, - DhGroupTransform[] dhGroups, - EsnTransform[] esns) { - super(IkePayload.PROTOCOL_ID_ESP, encryptionAlgos, integrityAlgos, dhGroups); - mEsns = esns; - } - - /** Gets all ESN policies. */ - public EsnTransform[] getEsnTransforms() { - return mEsns; - } - - /** - * Gets a copy of proposal without all proposed DH groups. - * - * <p>This is used to avoid negotiating DH Group for negotiating first Child SA. - */ - public ChildSaProposal getCopyWithoutDhTransform() { - return new ChildSaProposal( - getEncryptionTransforms(), - getIntegrityTransforms(), - new DhGroupTransform[0], - getEsnTransforms()); - } - - @Override - public Transform[] getAllTransforms() { - List<Transform> transformList = getAllTransformsAsList(); - transformList.addAll(Arrays.asList(mEsns)); - - return transformList.toArray(new Transform[transformList.size()]); - } - - @Override - public boolean isNegotiatedFrom(SaProposal reqProposal) { - return super.isNegotiatedFrom(reqProposal) - && isTransformSelectedFrom(mEsns, ((ChildSaProposal) reqProposal).mEsns); - } - - /** - * This class can be used to incrementally construct a ChildSaProposal. ChildSaProposal - * instances are immutable once built. - * - * <p>TODO: Support users to add algorithms from most preferred to least preferred. - */ - public static final class Builder extends SaProposal.Builder { - /** - * Adds an encryption algorithm with specific key length to SA proposal being built. - * - * @param algorithm encryption algorithm to add to ChildSaProposal. - * @param keyLength key length of algorithm. For algorithm that has fixed key length (e.g. - * 3DES) only KEY_LEN_UNUSED is allowed. - * @return Builder of ChildSaProposal. - * @throws IllegalArgumentException if AEAD and non-combined mode algorithms are mixed. - */ - public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) { - validateAndAddEncryptAlgo(algorithm, keyLength); - return this; - } - - /** - * Adds an integrity algorithm to SA proposal being built. - * - * @param algorithm integrity algorithm to add to ChildSaProposal. - * @return Builder of ChildSaProposal. - */ - public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) { - addIntegrityAlgo(algorithm); - return this; - } - - /** - * Adds a Diffie-Hellman Group to SA proposal being built. - * - * @param dhGroup to add to ChildSaProposal. - * @return Builder of ChildSaProposal. - */ - public Builder addDhGroup(@DhGroup int dhGroup) { - addDh(dhGroup); - return this; - } - - private IntegrityTransform[] buildIntegAlgosOrThrow() { - // When building Child SA Proposal with normal-mode ciphers, there is no contraint on - // integrity algorithm. When building Child SA Proposal with combined-mode ciphers, - // mProposedIntegrityAlgos must be either empty or only have INTEGRITY_ALGORITHM_NONE. - for (IntegrityTransform transform : mProposedIntegrityAlgos) { - if (transform.id != INTEGRITY_ALGORITHM_NONE && mHasAead) { - throw new IllegalArgumentException( - ERROR_TAG - + "Only INTEGRITY_ALGORITHM_NONE can be" - + " proposed with combined-mode ciphers in any proposal."); - } - } - - return mProposedIntegrityAlgos.toArray( - new IntegrityTransform[mProposedIntegrityAlgos.size()]); - } - - /** - * Validates, builds and returns the ChildSaProposal - * - * @return the validated ChildSaProposal. - * @throws IllegalArgumentException if ChildSaProposal is invalid. - */ - public ChildSaProposal build() { - EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow(); - IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow(); - - return new ChildSaProposal( - encryptionTransforms, - integrityTransforms, - mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]), - new EsnTransform[] {new EsnTransform()}); - } - } -} diff --git a/src/java/android/net/ipsec/ike/ChildSessionCallback.java b/src/java/android/net/ipsec/ike/ChildSessionCallback.java deleted file mode 100644 index ec8722e6..00000000 --- a/src/java/android/net/ipsec/ike/ChildSessionCallback.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.net.IpSecManager.PolicyDirection; -import android.net.IpSecTransform; -import android.net.ipsec.ike.exceptions.IkeException; - -/** Callback interface for receiving state changes of a Child Session. */ -public interface ChildSessionCallback { - /** - * Called when Child Session setup succeeds. - * - * @param sessionConfiguration the configuration information of Child Session negotiated during - * Child creation. - */ - void onOpened(ChildSessionConfiguration sessionConfiguration); - - /** - * Called when either side has decided to close this Session and the deletion exchange - * finishes. - * - * <p>This method will not be fired if this deletion is caused by a fatal error. - */ - void onClosed(); - - /** - * Called if Child Session setup fails or Child Session is closed because of a fatal error. - * - * @param exception the detailed error. - */ - void onClosedExceptionally(IkeException exception); - - /** - * Called when a new {@link IpSecTransform} is created for this Child Session. - * - * @param ipSecTransform the created {@link IpSecTransform} - * @param direction the direction of this {@link IpSecTransform} - */ - void onIpSecTransformCreated(IpSecTransform ipSecTransform, @PolicyDirection int direction); - - /** - * Called when a new {@link IpSecTransform} is deleted for this Child Session. - * - * <p>Users MUST remove the transform from the socket or interface. Otherwise the communication - * on that socket or interface will fail. - * - * @param ipSecTransform the deleted {@link IpSecTransform} - * @param direction the direction of this {@link IpSecTransform} - */ - void onIpSecTransformDeleted(IpSecTransform ipSecTransform, @PolicyDirection int direction); -} diff --git a/src/java/android/net/ipsec/ike/ChildSessionConfiguration.java b/src/java/android/net/ipsec/ike/ChildSessionConfiguration.java deleted file mode 100644 index 2ed9de2f..00000000 --- a/src/java/android/net/ipsec/ike/ChildSessionConfiguration.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_ADDRESS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_NETMASK; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_ADDRESS; - -import android.net.LinkAddress; - -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Address; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** ChildSessionConfiguration represents the negotiated configuration for a Child Session. */ -public final class ChildSessionConfiguration { - private static final int IPv4_DEFAULT_PREFIX_LEN = 32; - - private final List<IkeTrafficSelector> mInboundTs; - private final List<IkeTrafficSelector> mOutboundTs; - private final List<LinkAddress> mInternalAddressList; - - /** - * Construct an instance of {@link ChildSessionConfiguration}. - * - * <p>It is only supported to build a {@link ChildSessionConfiguration} with a Configure(Reply) - * Payload. - */ - public ChildSessionConfiguration( - List<IkeTrafficSelector> inTs, - List<IkeTrafficSelector> outTs, - IkeConfigPayload configPayload) { - this(inTs, outTs); - - if (configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) { - throw new IllegalArgumentException( - "Cannot build ChildSessionConfiguration with configuration type: " - + configPayload.configType); - } - - // It is validated in IkeConfigPayload that a config reply only has at most one non-empty - // netmask and netmask exists only when IPv4 internal address exists. - ConfigAttributeIpv4Netmask netmaskAttr = null; - for (ConfigAttribute att : configPayload.recognizedAttributeList) { - if (att.attributeType == CONFIG_ATTR_INTERNAL_IP4_NETMASK && !att.isEmptyValue()) { - netmaskAttr = (ConfigAttributeIpv4Netmask) att; - } - } - - for (ConfigAttribute att : configPayload.recognizedAttributeList) { - if (att.isEmptyValue()) continue; - switch (att.attributeType) { - case CONFIG_ATTR_INTERNAL_IP4_ADDRESS: - ConfigAttributeIpv4Address addressAttr = (ConfigAttributeIpv4Address) att; - if (netmaskAttr != null) { - mInternalAddressList.add( - new LinkAddress(addressAttr.address, netmaskAttr.getPrefixLen())); - } else { - mInternalAddressList.add( - new LinkAddress(addressAttr.address, IPv4_DEFAULT_PREFIX_LEN)); - } - break; - case CONFIG_ATTR_INTERNAL_IP4_NETMASK: - // No action. - break; - case CONFIG_ATTR_INTERNAL_IP6_ADDRESS: - mInternalAddressList.add(((ConfigAttributeIpv6Address) att).linkAddress); - break; - default: - // TODO: Support DNS,Subnet and Dhcp4 attributes - } - } - } - - /** Construct an instance of {@link ChildSessionConfiguration}. */ - public ChildSessionConfiguration( - List<IkeTrafficSelector> inTs, List<IkeTrafficSelector> outTs) { - mInboundTs = Collections.unmodifiableList(inTs); - mOutboundTs = Collections.unmodifiableList(outTs); - mInternalAddressList = new LinkedList<>(); - } - - /** - * Returns the negotiated inbound traffic selectors. - * - * @return the inbound traffic selector. - */ - public List<IkeTrafficSelector> getInboundTrafficSelectors() { - return mInboundTs; - } - - /** - * Returns the negotiated outbound traffic selectors. - * - * @return the outbound traffic selector. - */ - public List<IkeTrafficSelector> getOutboundTrafficSelectors() { - return mOutboundTs; - } - - /** - * Returns the assigned internal addresses. - * - * @return assigned internal addresses, or empty list when no addresses are assigned by the - * remote IKE server. - */ - public List<LinkAddress> getInternalAddressList() { - return mInternalAddressList; - } -} diff --git a/src/java/android/net/ipsec/ike/ChildSessionOptions.java b/src/java/android/net/ipsec/ike/ChildSessionOptions.java deleted file mode 100644 index 90a30053..00000000 --- a/src/java/android/net/ipsec/ike/ChildSessionOptions.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import libcore.net.InetAddressUtils; - -import java.net.InetAddress; -import java.util.LinkedList; -import java.util.List; - -/** - * This abstract class is the superclass of all classes representing a set of user configurations - * for Child Session negotiation. - */ -public abstract class ChildSessionOptions { - private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV4; - // TODO: b/130765172 Add TRAFFIC_SELECTOR_IPV6 and instantiate it. - - static { - DEFAULT_TRAFFIC_SELECTOR_IPV4 = - buildDefaultTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE); - } - - private final IkeTrafficSelector[] mLocalTrafficSelectors; - private final IkeTrafficSelector[] mRemoteTrafficSelectors; - private final ChildSaProposal[] mSaProposals; - private final boolean mIsTransport; - - protected ChildSessionOptions( - IkeTrafficSelector[] localTs, - IkeTrafficSelector[] remoteTs, - ChildSaProposal[] proposals, - boolean isTransport) { - mLocalTrafficSelectors = localTs; - mRemoteTrafficSelectors = remoteTs; - mSaProposals = proposals; - mIsTransport = isTransport; - } - - public IkeTrafficSelector[] getLocalTrafficSelectors() { - return mLocalTrafficSelectors; - } - - public IkeTrafficSelector[] getRemoteTrafficSelectors() { - return mRemoteTrafficSelectors; - } - - public ChildSaProposal[] getSaProposals() { - return mSaProposals; - } - - public boolean isTransportMode() { - return mIsTransport; - } - - /** This class represents common information for Child Sesison Options Builders. */ - protected abstract static class Builder { - protected final List<IkeTrafficSelector> mLocalTsList = new LinkedList<>(); - protected final List<IkeTrafficSelector> mRemoteTsList = new LinkedList<>(); - protected final List<SaProposal> mSaProposalList = new LinkedList<>(); - - protected Builder() { - // Currently IKE library only accepts setting up Child SA that all ports and all - // addresses are allowed on both sides. The protected traffic range is determined by the - // socket or interface that the {@link IpSecTransform} is applied to. - // TODO: b/130756765 Validate the current TS negotiation strategy. - mLocalTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4); - mRemoteTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4); - // TODO: add IPv6 TS to ChildSessionOptions. - } - - protected void validateAndAddSaProposal(ChildSaProposal proposal) { - mSaProposalList.add(proposal); - } - - protected void validateOrThrow() { - if (mSaProposalList.isEmpty()) { - throw new IllegalArgumentException( - "ChildSessionOptions requires at least one Child SA proposal."); - } - } - } - - private static IkeTrafficSelector buildDefaultTrafficSelector( - @IkeTrafficSelector.TrafficSelectorType int tsType) { - int startPort = IkeTrafficSelector.PORT_NUMBER_MIN; - int endPort = IkeTrafficSelector.PORT_NUMBER_MAX; - InetAddress startAddress = null; - InetAddress endAddress = null; - switch (tsType) { - case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: - startAddress = InetAddressUtils.parseNumericAddress("0.0.0.0"); - endAddress = InetAddressUtils.parseNumericAddress("255.255.255.255"); - break; - case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: - // TODO: Support it. - throw new UnsupportedOperationException("Do not support IPv6."); - default: - throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType); - } - - return new IkeTrafficSelector(tsType, startPort, endPort, startAddress, endAddress); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeFqdnIdentification.java b/src/java/android/net/ipsec/ike/IkeFqdnIdentification.java deleted file mode 100644 index 393a0723..00000000 --- a/src/java/android/net/ipsec/ike/IkeFqdnIdentification.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; - -import java.nio.charset.Charset; -import java.util.Objects; - -/** IkeFqdnIdentification represents ID information using a fully-qualified domain name (FQDN) */ -public class IkeFqdnIdentification extends IkeIdentification { - private static final Charset ASCII = Charset.forName("US-ASCII"); - - public final String fqdn; - - /** - * Construct an instance of IkeFqdnIdentification from a decoded inbound packet. - * - * <p>All characters in the FQDN are ASCII. - * - * @param fqdnBytes FQDN in byte array. - */ - public IkeFqdnIdentification(byte[] fqdnBytes) { - super(ID_TYPE_FQDN); - fqdn = new String(fqdnBytes, ASCII); - } - - /** - * Construct an instance of IkeFqdnIdentification with user provided fully-qualified domain name - * (FQDN) for building outbound packet. - * - * <p>FQDN will be formatted as US-ASCII. - * - * @param fqdn user provided fully-qualified domain name (FQDN) - */ - public IkeFqdnIdentification(@NonNull String fqdn) { - super(ID_TYPE_FQDN); - this.fqdn = fqdn; - } - - @Override - public int hashCode() { - return Objects.hash(idType, fqdn); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IkeFqdnIdentification)) return false; - - return fqdn.equals(((IkeFqdnIdentification) o).fqdn); - } - - /** - * Retrieve the byte-representation of the FQDN. - * - * @return the byte-representation of the FQDN. - */ - @Override - public byte[] getEncodedIdData() { - return fqdn.getBytes(ASCII); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeIdentification.java b/src/java/android/net/ipsec/ike/IkeIdentification.java deleted file mode 100644 index 737df826..00000000 --- a/src/java/android/net/ipsec/ike/IkeIdentification.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 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.ipsec.ike; - -import android.annotation.IntDef; -import android.util.ArraySet; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Set; - -/** - * IkeIdentification is abstract base class that represents the common information for all types of - * IKE entity identification. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public abstract class IkeIdentification { - // Set of supported ID types. - private static final Set<Integer> SUPPORTED_ID_TYPES; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - ID_TYPE_IPV4_ADDR, - ID_TYPE_FQDN, - ID_TYPE_RFC822_ADDR, - ID_TYPE_IPV6_ADDR, - ID_TYPE_DER_ASN1_DN, - ID_TYPE_DER_ASN1_GN, - ID_TYPE_KEY_ID - }) - public @interface IdType {} - - public static final int ID_TYPE_IPV4_ADDR = 1; - public static final int ID_TYPE_FQDN = 2; - public static final int ID_TYPE_RFC822_ADDR = 3; - public static final int ID_TYPE_IPV6_ADDR = 5; - public static final int ID_TYPE_DER_ASN1_DN = 9; - public static final int ID_TYPE_DER_ASN1_GN = 10; - public static final int ID_TYPE_KEY_ID = 11; - - static { - SUPPORTED_ID_TYPES = new ArraySet(); - SUPPORTED_ID_TYPES.add(ID_TYPE_IPV4_ADDR); - SUPPORTED_ID_TYPES.add(ID_TYPE_FQDN); - SUPPORTED_ID_TYPES.add(ID_TYPE_RFC822_ADDR); - SUPPORTED_ID_TYPES.add(ID_TYPE_IPV6_ADDR); - SUPPORTED_ID_TYPES.add(ID_TYPE_DER_ASN1_DN); - SUPPORTED_ID_TYPES.add(ID_TYPE_DER_ASN1_GN); - SUPPORTED_ID_TYPES.add(ID_TYPE_KEY_ID); - } - - public final int idType; - - protected IkeIdentification(@IdType int type) { - idType = type; - } - - /** - * Return the encoded identification data in a byte array. - * - * @return the encoded identification data. - */ - public abstract byte[] getEncodedIdData(); -} diff --git a/src/java/android/net/ipsec/ike/IkeIpv4AddrIdentification.java b/src/java/android/net/ipsec/ike/IkeIpv4AddrIdentification.java deleted file mode 100644 index 350187ed..00000000 --- a/src/java/android/net/ipsec/ike/IkeIpv4AddrIdentification.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; - -import java.net.Inet4Address; -import java.net.UnknownHostException; -import java.util.Objects; - -/** IkeIpv4AddrIdentification represents ID information in IPv4 address ID type. */ -public final class IkeIpv4AddrIdentification extends IkeIdentification { - public final Inet4Address ipv4Address; - - /** - * Construct an instance of IkeIpv4AddrIdentification from a decoded inbound packet. - * - * @param ipv4AddrBytes IPv4 address in byte array. - * @throws AuthenticationFailedException for decoding bytes error. - */ - public IkeIpv4AddrIdentification(byte[] ipv4AddrBytes) throws AuthenticationFailedException { - super(ID_TYPE_IPV4_ADDR); - try { - ipv4Address = (Inet4Address) (Inet4Address.getByAddress(ipv4AddrBytes)); - } catch (ClassCastException | UnknownHostException e) { - throw new AuthenticationFailedException(e); - } - } - - /** - * Construct an instance of IkeIpv4AddrIdentification with user provided IPv4 address for - * building outbound packet. - * - * @param address user provided IPv4 address - */ - public IkeIpv4AddrIdentification(@NonNull Inet4Address address) { - super(ID_TYPE_IPV4_ADDR); - ipv4Address = address; - } - - @Override - public int hashCode() { - return Objects.hash(idType, ipv4Address); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IkeIpv4AddrIdentification)) return false; - - return ipv4Address.equals(((IkeIpv4AddrIdentification) o).ipv4Address); - } - - /** - * Retrieve the byte-representation of the IPv4 address. - * - * @return the byte-representation of the IPv4 address. - */ - @Override - public byte[] getEncodedIdData() { - return ipv4Address.getAddress(); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeIpv6AddrIdentification.java b/src/java/android/net/ipsec/ike/IkeIpv6AddrIdentification.java deleted file mode 100644 index dfc7d5e3..00000000 --- a/src/java/android/net/ipsec/ike/IkeIpv6AddrIdentification.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; - -import java.net.Inet6Address; -import java.net.UnknownHostException; -import java.util.Objects; - -/** IkeIpv6AddrIdentification represents ID information in IPv6 address ID type. */ -public class IkeIpv6AddrIdentification extends IkeIdentification { - public final Inet6Address ipv6Address; - - /** - * Construct an instance of IkeIpv6AddrIdentification from a decoded inbound packet. - * - * @param ipv6AddrBytes IPv6 address in byte array. - * @throws AuthenticationFailedException for decoding bytes error. - */ - public IkeIpv6AddrIdentification(byte[] ipv6AddrBytes) throws AuthenticationFailedException { - super(ID_TYPE_IPV6_ADDR); - try { - ipv6Address = (Inet6Address) (Inet6Address.getByAddress(ipv6AddrBytes)); - } catch (ClassCastException | UnknownHostException e) { - throw new AuthenticationFailedException(e); - } - } - - /** - * Construct an instance of IkeIpv6AddrIdentification with user provided IPv6 address for - * building outbound packet. - * - * @param address user provided IPv6 address - */ - public IkeIpv6AddrIdentification(@NonNull Inet6Address address) { - super(ID_TYPE_IPV6_ADDR); - ipv6Address = address; - } - - @Override - public int hashCode() { - return Objects.hash(idType, ipv6Address); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IkeIpv6AddrIdentification)) return false; - - return ipv6Address.equals(((IkeIpv6AddrIdentification) o).ipv6Address); - } - - /** - * Retrieve the byte-representation of the IPv6 address. - * - * @return the byte-representation of the IPv6 address. - */ - @Override - public byte[] getEncodedIdData() { - return ipv6Address.getAddress(); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeKeyIdIdentification.java b/src/java/android/net/ipsec/ike/IkeKeyIdIdentification.java deleted file mode 100644 index c229b9da..00000000 --- a/src/java/android/net/ipsec/ike/IkeKeyIdIdentification.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; - -import java.util.Objects; - -/** - * This class represents IKE ID information in Key ID type. - * - * <p>This is an octet stream that may be used to pass vendor-specific information necessary to do - * certain proprietary types of identification. - */ -public final class IkeKeyIdIdentification extends IkeIdentification { - public final byte[] keyId; - - /** - * Construct an instance of IkeKeyIdIdentification with provided Key ID - * - * @param keyId Key ID in bytes - */ - public IkeKeyIdIdentification(@NonNull byte[] keyId) { - super(ID_TYPE_KEY_ID); - this.keyId = keyId; - } - - @Override - public int hashCode() { - return Objects.hash(idType, keyId); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IkeKeyIdIdentification)) return false; - - return keyId.equals(((IkeKeyIdIdentification) o).keyId); - } - - /** - * Retrieve the byte-representation of the FQDN. - * - * @return the byte-representation of the FQDN. - */ - @Override - public byte[] getEncodedIdData() { - return keyId; - } -} diff --git a/src/java/android/net/ipsec/ike/IkeManager.java b/src/java/android/net/ipsec/ike/IkeManager.java deleted file mode 100644 index ef8b893b..00000000 --- a/src/java/android/net/ipsec/ike/IkeManager.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.content.Context; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.utils.Log; - -import java.util.concurrent.Executor; - -/** This class contains methods for managing IKE sessions. */ -public final class IkeManager { - private static final String IKE_TAG = "IKE"; - private static final boolean LOG_SENSITIVE = false; - - private static Log sIkeLog = new Log(IKE_TAG, LOG_SENSITIVE); - - private final Context mContext; - - /** - * Construct an instance of {@link IkeManager} - * - * @param context the application context. - */ - public IkeManager(Context context) { - mContext = context; - } - - /** - * Construct an instance of {@link IkeSession} and start the IKE Session setup process. - * - * <p>This method will immediately return a management object {@link IkeSession} and - * asynchronously initiate the IKE Session setup process. Users will be notified of the IKE - * Session and Child Session negotiation results on the callback arguments. - * - * @param ikeSessionOptions the {@link IkeSessionOptions} that contains acceptable IKE Session - * configurations. - * @param firstChildSessionOptions the {@link ChildSessionOptions} that contains acceptable - * first Child Session configurations. - * @param userCbExecutor the {@link Executor} upon which all callbacks will be posted. For - * security and consistency, the callbacks posted to this executor MUST be executed - * serially, in the order they were posted. - * @param ikeSessionCallback the {@link IkeSessionCallback} interface to notify users the state - * changes of the IKE Session. - * @param firstChildSessionCallback the {@link ChildSessionCallback} interface to notify users - * the state changes of the Child Session. - * @return an instance of {@link IkeSession} - */ - public IkeSession openIkeSession( - IkeSessionOptions ikeSessionOptions, - ChildSessionOptions firstChildSessionOptions, - Executor userCbExecutor, - IkeSessionCallback ikeSessionCallback, - ChildSessionCallback firstChildSessionCallback) { - return new IkeSession( - mContext, - ikeSessionOptions, - firstChildSessionOptions, - userCbExecutor, - ikeSessionCallback, - firstChildSessionCallback); - } - - /** Returns IKE logger. */ - public static Log getIkeLog() { - return sIkeLog; - } - - /** Injects IKE logger for testing. */ - @VisibleForTesting - public static void setIkeLog(Log log) { - sIkeLog = log; - } - - /** Resets IKE logger. */ - @VisibleForTesting - public static void resetIkeLog() { - sIkeLog = new Log(IKE_TAG, LOG_SENSITIVE); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeRfc822AddrIdentification.java b/src/java/android/net/ipsec/ike/IkeRfc822AddrIdentification.java deleted file mode 100644 index 2a93bea1..00000000 --- a/src/java/android/net/ipsec/ike/IkeRfc822AddrIdentification.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; - -import java.nio.charset.Charset; -import java.util.Objects; - -/** This class represents IKE ID information in fully-qualified RFC 822 email address ID type. */ -public final class IkeRfc822AddrIdentification extends IkeIdentification { - private static final Charset UTF8 = Charset.forName("UTF-8"); - - public final String rfc822Name; - - /** - * Construct an instance of IkeRfc822AddrIdentification from a decoded inbound packet. - * - * <p>All characters in the RFC 822 email address are UTF-8. - * - * @param rfc822NameBytes fully-qualified RFC 822 email address in byte array. - */ - public IkeRfc822AddrIdentification(byte[] rfc822NameBytes) { - super(ID_TYPE_RFC822_ADDR); - rfc822Name = new String(rfc822NameBytes, UTF8); - } - - /** - * Construct an instance of IkeRfc822AddrIdentification with user provided fully-qualified RFC - * 822 email address for building outbound packet. - * - * <p>rfc822Name will be formatted as UTF-8. - * - * @param rfc822Name user provided fully-qualified RFC 822 email address. - */ - public IkeRfc822AddrIdentification(@NonNull String rfc822Name) { - super(ID_TYPE_RFC822_ADDR); - this.rfc822Name = rfc822Name; - } - - @Override - public int hashCode() { - return Objects.hash(idType, rfc822Name); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IkeRfc822AddrIdentification)) return false; - - return rfc822Name.equals(((IkeRfc822AddrIdentification) o).rfc822Name); - } - - /** - * Retrieve the byte-representation of the the RFC 822 email address. - * - * @return the byte-representation of the RFC 822 email address. - */ - @Override - public byte[] getEncodedIdData() { - return rfc822Name.getBytes(UTF8); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeSaProposal.java b/src/java/android/net/ipsec/ike/IkeSaProposal.java deleted file mode 100644 index 1494f9d4..00000000 --- a/src/java/android/net/ipsec/ike/IkeSaProposal.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.util.ArraySet; - -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform; - -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -/** - * IkeSaProposal represents a user configured set contains cryptograhic algorithms and key - * generating materials for negotiating an IKE SA. - * - * <p>User must provide at least a valid IkeSaProposal when they are creating a new IKE SA. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class IkeSaProposal extends SaProposal { - private final PrfTransform[] mPseudorandomFunctions; - - /** - * Construct an instance of IkeSaProposal. - * - * <p>This constructor is either called by IkeSaPayload for building an inbound proposal from a - * decoded packet, or called by the inner Builder to build an outbound proposal from user - * provided parameters - * - * @param encryptionAlgos encryption algorithms - * @param prfs pseudorandom functions - * @param integrityAlgos integrity algorithms - * @param dhGroups Diffie-Hellman Groups - */ - public IkeSaProposal( - EncryptionTransform[] encryptionAlgos, - PrfTransform[] prfs, - IntegrityTransform[] integrityAlgos, - DhGroupTransform[] dhGroups) { - super(IkePayload.PROTOCOL_ID_IKE, encryptionAlgos, integrityAlgos, dhGroups); - mPseudorandomFunctions = prfs; - } - - /** Gets all PRFs. */ - public PrfTransform[] getPrfTransforms() { - return mPseudorandomFunctions; - } - - @Override - public Transform[] getAllTransforms() { - List<Transform> transformList = getAllTransformsAsList(); - transformList.addAll(Arrays.asList(mPseudorandomFunctions)); - - return transformList.toArray(new Transform[transformList.size()]); - } - - @Override - public boolean isNegotiatedFrom(SaProposal reqProposal) { - return super.isNegotiatedFrom(reqProposal) - && isTransformSelectedFrom( - mPseudorandomFunctions, - ((IkeSaProposal) reqProposal).mPseudorandomFunctions); - } - - /** - * This class can be used to incrementally construct a IkeSaProposal. IkeSaProposal instances - * are immutable once built. - * - * <p>TODO: Support users to add algorithms from most preferred to least preferred. - */ - public static final class Builder extends SaProposal.Builder { - // Use set to avoid adding repeated algorithms. - private final Set<PrfTransform> mProposedPrfs = new ArraySet<>(); - - /** - * Adds an encryption algorithm with specific key length to SA proposal being built. - * - * @param algorithm encryption algorithm to add to IkeSaProposal. - * @param keyLength key length of algorithm. For algorithm that has fixed key length (e.g. - * 3DES) only KEY_LEN_UNUSED is allowed. - * @return Builder of IkeSaProposal. - * @throws IllegalArgumentException if AEAD and non-combined mode algorithms are mixed. - */ - public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) { - validateAndAddEncryptAlgo(algorithm, keyLength); - return this; - } - - /** - * Adds an integrity algorithm to SA proposal being built. - * - * @param algorithm integrity algorithm to add to IkeSaProposal. - * @return Builder of IkeSaProposal. - */ - public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) { - addIntegrityAlgo(algorithm); - return this; - } - - /** - * Adds a Diffie-Hellman Group to SA proposal being built. - * - * @param dhGroup to add to IkeSaProposal. - * @return Builder of IkeSaProposal. - */ - public Builder addDhGroup(@DhGroup int dhGroup) { - addDh(dhGroup); - return this; - } - - /** - * Adds a pseudorandom function to SA proposal being built. - * - * @param algorithm pseudorandom function to add to IkeSaProposal. - * @return Builder of IkeSaProposal. - */ - public Builder addPseudorandomFunction(@PseudorandomFunction int algorithm) { - // Construct PrfTransform and validate proposed algorithm during construction. - mProposedPrfs.add(new PrfTransform(algorithm)); - return this; - } - - private IntegrityTransform[] buildIntegAlgosOrThrow() { - // When building IKE SA Proposal with normal-mode ciphers, mProposedIntegrityAlgos must - // not be empty and must not have INTEGRITY_ALGORITHM_NONE. When building IKE SA - // Proposal with combined-mode ciphers, mProposedIntegrityAlgos must be either empty or - // only have INTEGRITY_ALGORITHM_NONE. - if (mProposedIntegrityAlgos.isEmpty() && !mHasAead) { - throw new IllegalArgumentException( - ERROR_TAG - + "Integrity algorithm " - + "must be proposed with normal ciphers in IKE proposal."); - } - - for (IntegrityTransform transform : mProposedIntegrityAlgos) { - if ((transform.id == INTEGRITY_ALGORITHM_NONE) != mHasAead) { - throw new IllegalArgumentException( - ERROR_TAG - + "Invalid integrity algorithm configuration" - + " for this SA Proposal"); - } - } - - return mProposedIntegrityAlgos.toArray( - new IntegrityTransform[mProposedIntegrityAlgos.size()]); - } - - private DhGroupTransform[] buildDhGroupsOrThrow() { - if (mProposedDhGroups.isEmpty()) { - throw new IllegalArgumentException( - ERROR_TAG + "DH group must be proposed in IKE SA proposal."); - } - - for (DhGroupTransform transform : mProposedDhGroups) { - if (transform.id == DH_GROUP_NONE) { - throw new IllegalArgumentException( - ERROR_TAG + "None-value DH group invalid in IKE SA proposal"); - } - } - - return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]); - } - - private PrfTransform[] buildPrfsOrThrow() { - if (mProposedPrfs.isEmpty()) { - throw new IllegalArgumentException( - ERROR_TAG + "PRF must be proposed in IKE SA proposal."); - } - return mProposedPrfs.toArray(new PrfTransform[mProposedPrfs.size()]); - } - - /** - * Validates, builds and returns the IkeSaProposal - * - * @return the validated IkeSaProposal. - * @throws IllegalArgumentException if IkeSaProposal is invalid. - */ - public IkeSaProposal build() { - EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow(); - PrfTransform[] prfTransforms = buildPrfsOrThrow(); - IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow(); - DhGroupTransform[] dhGroupTransforms = buildDhGroupsOrThrow(); - - return new IkeSaProposal( - encryptionTransforms, prfTransforms, integrityTransforms, dhGroupTransforms); - } - } -} diff --git a/src/java/android/net/ipsec/ike/IkeSession.java b/src/java/android/net/ipsec/ike/IkeSession.java deleted file mode 100644 index 97fe061e..00000000 --- a/src/java/android/net/ipsec/ike/IkeSession.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.content.Context; -import android.net.IpSecManager; -import android.os.HandlerThread; -import android.os.Looper; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine; - -import dalvik.system.CloseGuard; - -import java.util.concurrent.Executor; - -/** - * This class represents an IKE Session management object that allows for keying and management of - * {@link IpSecTransform}s. - * - * <p>An IKE/Child Session represents an IKE/Child SA as well as its rekeyed successors. A Child - * Session is bounded by the lifecycle of the IKE Session under which it is set up. Closing an IKE - * Session implicitly closes any remaining Child Sessions under it. - * - * <p>An IKE procedure is one or multiple IKE message exchanges that are used to create, delete or - * rekey an IKE Session or Child Session. - * - * <p>This class provides methods for user to initiate IKE procedures, such as the Creation and - * Deletion of a Child Session, or the Deletion of the IKE session. All procedures (except for IKE - * deletion) will be initiated sequentially after IKE Session is set up. - * - * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol - * Version 2 (IKEv2)</a> - */ -public final class IkeSession implements AutoCloseable { - private final CloseGuard mCloseGuard = CloseGuard.get(); - - @VisibleForTesting final IkeSessionStateMachine mIkeSessionStateMachine; - - /** Package private */ - IkeSession( - Context context, - IkeSessionOptions ikeSessionOptions, - ChildSessionOptions firstChildSessionOptions, - Executor userCbExecutor, - IkeSessionCallback ikeSessionCallback, - ChildSessionCallback firstChildSessionCallback) { - this( - IkeThreadHolder.IKE_WORKER_THREAD.getLooper(), - context, - (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE), - ikeSessionOptions, - firstChildSessionOptions, - userCbExecutor, - ikeSessionCallback, - firstChildSessionCallback); - } - - /** Package private */ - @VisibleForTesting - IkeSession( - Looper looper, - Context context, - IpSecManager ipSecManager, - IkeSessionOptions ikeSessionOptions, - ChildSessionOptions firstChildSessionOptions, - Executor userCbExecutor, - IkeSessionCallback ikeSessionCallback, - ChildSessionCallback firstChildSessionCallback) { - mIkeSessionStateMachine = - new IkeSessionStateMachine( - looper, - context, - ipSecManager, - ikeSessionOptions, - firstChildSessionOptions, - userCbExecutor, - ikeSessionCallback, - firstChildSessionCallback); - mIkeSessionStateMachine.openSession(); - - mCloseGuard.open("open"); - } - - @Override - public void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - } - - /** Initialization-on-demand holder */ - private static class IkeThreadHolder { - static final HandlerThread IKE_WORKER_THREAD; - - static { - IKE_WORKER_THREAD = new HandlerThread("IkeWorkerThread"); - IKE_WORKER_THREAD.start(); - } - } - - // TODO: b/133340675 Destroy the worker thread when there is no more alive {@link IkeSession}. - - /** - * Asynchronously request a new Child Session. - * - * <p>Users MUST provide a unique {@link ChildSessionCallback} instance for each new Child - * Session. - * - * <p>Upon setup, the {@link ChildSessionCallback#onOpened(ChildSessionConfiguration)} will be - * fired. - * - * @param childSessionOptions the {@link ChildSessionOptions} that contains the Child Session - * configurations to negotiate. - * @param childSessionCallback the {@link ChildSessionCallback} interface to notify users the - * state changes of the Child Session. - * @throws IllegalArgumentException if the ChildSessionCallback is already in use. - */ - public void openChildSession( - ChildSessionOptions childSessionOptions, ChildSessionCallback childSessionCallback) { - mIkeSessionStateMachine.openChildSession(childSessionOptions, childSessionCallback); - } - - /** - * Asynchronously delete a Child Session. - * - * <p>Upon closing, the {@link ChildSessionCallback#onClosed()} will be fired. - * - * @param childSessionCallback The {@link ChildSessionCallback} instance that uniquely identify - * the Child Session. - * @throws IllegalArgumentException if no Child Session found bound with this callback. - */ - public void closeChildSession(ChildSessionCallback childSessionCallback) { - mIkeSessionStateMachine.closeChildSession(childSessionCallback); - } - - /** - * Close the IKE session gracefully. - * - * <p>Implements {@link AutoCloseable#close()} - * - * <p>Upon closing, the {@link IkeSessionCallback#onClosed()} will be fired. - * - * <p>Closing an IKE Session implicitly closes any remaining Child Sessions negotiated under it. - * Users SHOULD stop all outbound traffic that uses these Child Sessions({@link IpSecTransform} - * pairs) before calling this method. Otherwise IPsec packets will be dropped due to the lack of - * a valid {@link IpSecTransform}. - * - * <p>Closure of an IKE session will take priority over, and cancel other procedures waiting in - * the queue (but will wait for ongoing locally initiated procedures to complete). After sending - * the Delete request, the IKE library will wait until a Delete response is received or - * retransmission timeout occurs. - */ - @Override - public void close() throws Exception { - mCloseGuard.close(); - mIkeSessionStateMachine.closeSession(); - } - - /** - * Terminate (forcibly close) the IKE session. - * - * <p>Upon closing, the {@link IkeSessionCallback#onClosed()} will be fired. - * - * <p>Closing an IKE Session implicitly closes any remaining Child Sessions negotiated under it. - * Users SHOULD stop all outbound traffic that uses these Child Sessions({@link IpSecTransform} - * pairs) before calling this method. Otherwise IPsec packets will be dropped due to the lack of - * a valid {@link IpSecTransform}. - * - * <p>Forcible closure of an IKE session will take priority over, and cancel other procedures - * waiting in the queue. It will also interrupt any ongoing locally initiated procedure. - */ - public void kill() throws Exception { - mCloseGuard.close(); - mIkeSessionStateMachine.killSession(); - } -} diff --git a/src/java/android/net/ipsec/ike/IkeSessionCallback.java b/src/java/android/net/ipsec/ike/IkeSessionCallback.java deleted file mode 100644 index c2121e2d..00000000 --- a/src/java/android/net/ipsec/ike/IkeSessionCallback.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; -import android.net.ipsec.ike.exceptions.IkeException; - -/** Callback interface for receiving state changes of an IKE Session. */ -public interface IkeSessionCallback { - /** - * Called when negotiation and authentication for this new IKE Session succeeds. - * - * @param sessionConfiguration the configuration information of IKE Session negotiated during - * IKE setup. - */ - void onOpened(@NonNull IkeSessionConfiguration sessionConfiguration); - - /** - * Called when either side has decided to close this Session and the deletion exchange - * finishes. - * - * <p>This method will not be fired if this deletion is caused by a fatal error. - */ - void onClosed(); - - /** - * Called if IKE Session negotiation fails or IKE Session is closed because of a fatal error. - * - * @param exception the detailed error. - */ - void onClosedExceptionally(IkeException exception); - - /** - * Called if a recoverable error is encountered in an established IKE Session. - * - * <p>A potential risk is usually detected when IKE library receives a non-protected error - * notification (e.g. INVALID_IKE_SPI) or a non-fatal error notification (e.g. - * INVALID_MESSAGE_ID). - * - * @param exception the detailed error. - */ - void onError(IkeException exception); -} diff --git a/src/java/android/net/ipsec/ike/IkeSessionConfiguration.java b/src/java/android/net/ipsec/ike/IkeSessionConfiguration.java deleted file mode 100644 index 6bbef7d0..00000000 --- a/src/java/android/net/ipsec/ike/IkeSessionConfiguration.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.IntDef; -import android.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** IkeSessionConfiguration represents the negotiated configuration for a IKE Session. */ -public final class IkeSessionConfiguration { - @Retention(RetentionPolicy.SOURCE) - @IntDef({EXTENSION_TYPE_FRAGMENTATION, EXTENSION_TYPE_MOBIKE}) - public @interface ExtensionType {} - - public static final int EXTENSION_TYPE_FRAGMENTATION = 1; - public static final int EXTENSION_TYPE_MOBIKE = 2; - - /** - * Gets remote(server) version information. - * - * @return application version of the remote server, or empty string if the remote server did - * not provide the application version - */ - @NonNull - public String getRemoteApplicationVersion() { - return ""; - } - - /** - * Checks if an IKE extension is enabled. - * - * <p>An IKE extension is enabled when both sides can support it. This negotiation always - * happens in IKE initial changes(IKE INIT and IKE AUTH). - * - * @param extensionType the extension type - * @return {@code true} if this extension is enabled - */ - public boolean isIkeExtensionEnabled(@ExtensionType int extensionType) { - return false; - } - - // TODO: Implement IkeSessionConfiguration. -} diff --git a/src/java/android/net/ipsec/ike/IkeSessionOptions.java b/src/java/android/net/ipsec/ike/IkeSessionOptions.java deleted file mode 100644 index 020a8888..00000000 --- a/src/java/android/net/ipsec/ike/IkeSessionOptions.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.eap.EapSessionConfig; - -import com.android.internal.net.ipsec.ike.message.IkePayload; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.InetAddress; -import java.security.PrivateKey; -import java.security.cert.TrustAnchor; -import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPrivateKey; -import java.util.LinkedList; -import java.util.List; - -/** - * IkeSessionOptions contains all user provided configurations for negotiating an IKE SA. - * - * <p>TODO: Make this doc more user-friendly. - */ -public final class IkeSessionOptions { - @Retention(RetentionPolicy.SOURCE) - @IntDef({IKE_AUTH_METHOD_PSK, IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, IKE_AUTH_METHOD_EAP}) - public @interface IkeAuthMethod {} - - // Constants to describe user configured authentication methods. - public static final int IKE_AUTH_METHOD_PSK = 1; - public static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2; - public static final int IKE_AUTH_METHOD_EAP = 3; - - private final InetAddress mServerAddress; - private final UdpEncapsulationSocket mUdpEncapSocket; - private final IkeSaProposal[] mSaProposals; - - private final IkeIdentification mLocalIdentification; - private final IkeIdentification mRemoteIdentification; - - private final IkeAuthConfig mLocalAuthConfig; - private final IkeAuthConfig mRemoteAuthConfig; - - private final boolean mIsIkeFragmentationSupported; - - private IkeSessionOptions( - InetAddress serverAddress, - UdpEncapsulationSocket udpEncapsulationSocket, - IkeSaProposal[] proposals, - IkeIdentification localIdentification, - IkeIdentification remoteIdentification, - IkeAuthConfig localAuthConfig, - IkeAuthConfig remoteAuthConfig, - boolean isIkeFragmentationSupported) { - mServerAddress = serverAddress; - mUdpEncapSocket = udpEncapsulationSocket; - mSaProposals = proposals; - - mLocalIdentification = localIdentification; - mRemoteIdentification = remoteIdentification; - - mLocalAuthConfig = localAuthConfig; - mRemoteAuthConfig = remoteAuthConfig; - - mIsIkeFragmentationSupported = isIkeFragmentationSupported; - } - - public InetAddress getServerAddress() { - return mServerAddress; - } - - public UdpEncapsulationSocket getUdpEncapsulationSocket() { - return mUdpEncapSocket; - } - - public IkeSaProposal[] getSaProposals() { - return mSaProposals; - } - - public IkeIdentification getLocalIdentification() { - return mLocalIdentification; - } - - public IkeIdentification getRemoteIdentification() { - return mRemoteIdentification; - } - - public IkeAuthConfig getLocalAuthConfig() { - return mLocalAuthConfig; - } - - public IkeAuthConfig getRemoteAuthConfig() { - return mRemoteAuthConfig; - } - - public boolean isIkeFragmentationSupported() { - return mIsIkeFragmentationSupported; - } - /** This class contains common information of an IKEv2 authentication configuration. */ - public abstract static class IkeAuthConfig { - @IkeAuthMethod public final int mAuthMethod; - - protected IkeAuthConfig(@IkeAuthMethod int authMethod) { - mAuthMethod = authMethod; - } - } - - /** - * This class represents the configuration to support IKEv2 pre-shared-key-based authentication - * of local or remote side. - */ - public static class IkeAuthPskConfig extends IkeAuthConfig { - public final byte[] mPsk; - - private IkeAuthPskConfig(byte[] psk) { - super(IKE_AUTH_METHOD_PSK); - mPsk = psk; - } - } - - /** - * This class represents the configuration to support IKEv2 public-key-signature-based - * authentication of the remote side. - */ - public static class IkeAuthDigitalSignRemoteConfig extends IkeAuthConfig { - public final TrustAnchor mTrustAnchor; - - private IkeAuthDigitalSignRemoteConfig(TrustAnchor trustAnchor) { - super(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE); - mTrustAnchor = trustAnchor; - } - } - - /** - * This class represents the configuration to support IKEv2 public-key-signature-based - * authentication of the local side. - */ - public static class IkeAuthDigitalSignLocalConfig extends IkeAuthConfig { - public final X509Certificate mEndCert; - public final List<X509Certificate> mIntermediateCerts; - public final PrivateKey mPrivateKey; - - private IkeAuthDigitalSignLocalConfig( - X509Certificate clientEndCert, - List<X509Certificate> clientIntermediateCerts, - PrivateKey privateKey) { - super(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE); - mEndCert = clientEndCert; - mIntermediateCerts = clientIntermediateCerts; - mPrivateKey = privateKey; - } - } - - /** - * This class represents the configuration to support EAP authentication of the local side. - * - * <p>EAP MUST be used with IKEv2 public-key-based authentication of the responder to the - * initiator. Currently IKE library does not support the IKEv2 protocol extension(RFC 5998) - * which allows EAP methods that provide mutual authentication and key agreement to be used to - * provide extensible responder authentication for IKEv2 based on methods other than public key - * signatures. - * - * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998, An Extension for EAP-Only - * Authentication in IKEv2</a> - */ - public static class IkeAuthEapConfig extends IkeAuthConfig { - public final EapSessionConfig mEapConfig; - - private IkeAuthEapConfig(EapSessionConfig eapConfig) { - super(IKE_AUTH_METHOD_EAP); - - mEapConfig = eapConfig; - } - } - - /** This class can be used to incrementally construct a IkeSessionOptions. */ - public static final class Builder { - private final List<IkeSaProposal> mSaProposalList = new LinkedList<>(); - - private InetAddress mServerAddress; - private UdpEncapsulationSocket mUdpEncapSocket; - - private IkeIdentification mLocalIdentification; - private IkeIdentification mRemoteIdentification; - - private IkeAuthConfig mLocalAuthConfig; - private IkeAuthConfig mRemoteAuthConfig; - - private boolean mIsIkeFragmentationSupported = false; - - /** - * Sets server address - * - * @param serverAddress IP address of remote IKE server. - * @return Builder this, to facilitate chaining. - */ - public Builder setServerAddress(@NonNull InetAddress serverAddress) { - mServerAddress = serverAddress; - return this; - } - - /** - * Sets UDP-Encapsulated socket - * - * @param udpEncapsulationSocket {@link IpSecManager.UdpEncapsulationSocket} for sending and - * receiving IKE message. - * @return Builder this, to facilitate chaining. - */ - public Builder setUdpEncapsulationSocket( - @NonNull UdpEncapsulationSocket udpEncapsulationSocket) { - mUdpEncapSocket = udpEncapsulationSocket; - return this; - } - - /** - * Sets local IKE identification. - * - * @param identification the local IKE identification. - * @return Builder this, to facilitate chaining. - */ - public Builder setLocalIdentification(IkeIdentification identification) { - mLocalIdentification = identification; - return this; - } - - /** - * Sets remote IKE identification. - * - * @param identification the remote IKE identification. - * @return Builder this, to facilitate chaining. - */ - public Builder setRemoteIdentification(IkeIdentification identification) { - mRemoteIdentification = identification; - return this; - } - - /** - * Adds an IKE SA proposal to IkeSessionOptions being built. - * - * @param proposal IKE SA proposal. - * @return Builder this, to facilitate chaining. - * @throws IllegalArgumentException if input proposal is not IKE SA proposal. - */ - public Builder addSaProposal(IkeSaProposal proposal) { - if (proposal.getProtocolId() != IkePayload.PROTOCOL_ID_IKE) { - throw new IllegalArgumentException( - "Expected IKE SA Proposal but received Child SA proposal"); - } - mSaProposalList.add(proposal); - return this; - } - - /** - * Uses pre-shared key to do IKE authentication. - * - * <p>Both client and server MUST be authenticated using the provided shared key. IKE - * authentication will fail if the remote peer tries to use other authentication methods. - * - * <p>Users MUST declare only one authentication method. Calling this function will override - * the previously set authentication configuration. - * - * @param sharedKey the shared key. - * @return Builder this, to facilitate chaining. - */ - public Builder setAuthPsk(@NonNull byte[] sharedKey) { - mLocalAuthConfig = new IkeAuthPskConfig(sharedKey); - mRemoteAuthConfig = new IkeAuthPskConfig(sharedKey); - return this; - } - - /** - * Uses EAP to do IKE authentication. - * - * <p>EAP are typically used to authenticate the IKE client to the server. It MUST be used - * in conjunction with a public-key-signature-based authentication of the server to the - * client. - * - * <p>Users MUST declare only one authentication method. Calling this function will override - * the previously set authentication configuration. - * - * @see <a href="https://tools.ietf.org/html/rfc5280">RFC 5280, Internet X.509 Public Key - * Infrastructure Certificate and Certificate Revocation List (CRL) Profile</a> - * @param serverCaCert the CA certificate for validating the received server certificate(s). - * @return Builder this, to facilitate chaining. - */ - public Builder setAuthEap( - @NonNull X509Certificate serverCaCert, @NonNull EapSessionConfig eapConfig) { - mLocalAuthConfig = new IkeAuthEapConfig(eapConfig); - - // The name constraints extension, defined in RFC 5280, indicates a name space within - // which all subject names in subsequent certificates in a certification path MUST be - // located. - mRemoteAuthConfig = - new IkeAuthDigitalSignRemoteConfig( - new TrustAnchor(serverCaCert, null /*nameConstraints*/)); - - // TODO: Investigate if we need to support the name constraints extension. - - return this; - } - - /** - * Uses certificate and digital signature to do IKE authentication. - * - * <p>The public key included by the client end certificate and the signature private key - * MUST come from the same key pair. - * - * <p>The IKE library will use the strongest signature algorithm supported by both sides. - * - * <p>Currenly only RSA digital signature is supported. - * - * @param serverCaCert the CA certificate for validating the received server certificate(s). - * @param clientEndCert the end certificate for remote server to verify the locally - * generated signature. - * @param clientPrivateKey private key to generate outbound digital signature. Only {@link - * RSAPrivateKey} is supported. - * @return Builder this, to facilitate chaining. - */ - public Builder setAuthDigitalSignature( - @NonNull X509Certificate serverCaCert, - @NonNull X509Certificate clientEndCert, - @NonNull PrivateKey clientPrivateKey) { - return setAuthDigitalSignature( - serverCaCert, - clientEndCert, - new LinkedList<X509Certificate>(), - clientPrivateKey); - } - - /** - * Uses certificate and digital signature to do IKE authentication. - * - * <p>The public key included by the client end certificate and the signature private key - * MUST come from the same key pair. - * - * <p>The IKE library will use the strongest signature algorithm supported by both sides. - * - * <p>Currenly only RSA digital signature is supported. - * - * @param serverCaCert the CA certificate for validating the received server certificate(s). - * @param clientEndCert the end certificate for remote server to verify locally generated - * signature. - * @param clientIntermediateCerts intermediate certificates for the remote server to - * validate the end certificate. - * @param clientPrivateKey private key to generate outbound digital signature. Only {@link - * RSAPrivateKey} is supported. - * @return Builder this, to facilitate chaining. - */ - public Builder setAuthDigitalSignature( - @NonNull X509Certificate serverCaCert, - @NonNull X509Certificate clientEndCert, - @NonNull List<X509Certificate> clientIntermediateCerts, - @NonNull PrivateKey clientPrivateKey) { - if (!(clientPrivateKey instanceof RSAPrivateKey)) { - throw new IllegalArgumentException("Unsupported private key type"); - } - - mLocalAuthConfig = - new IkeAuthDigitalSignLocalConfig( - clientEndCert, clientIntermediateCerts, clientPrivateKey); - mRemoteAuthConfig = - new IkeAuthDigitalSignRemoteConfig( - new TrustAnchor(serverCaCert, null /*nameConstraints*/)); - return this; - } - - /** - * Validates, builds and returns the IkeSessionOptions - * - * @return IkeSessionOptions the validated IkeSessionOptions - * @throws IllegalArgumentException if no IKE SA proposal is provided - */ - public IkeSessionOptions build() { - if (mSaProposalList.isEmpty()) { - throw new IllegalArgumentException("IKE SA proposal not found"); - } - if (mServerAddress == null - || mUdpEncapSocket == null - || mLocalIdentification == null - || mRemoteIdentification == null - || mLocalAuthConfig == null - || mRemoteAuthConfig == null) { - throw new IllegalArgumentException("Necessary parameter missing."); - } - - return new IkeSessionOptions( - mServerAddress, - mUdpEncapSocket, - mSaProposalList.toArray(new IkeSaProposal[mSaProposalList.size()]), - mLocalIdentification, - mRemoteIdentification, - mLocalAuthConfig, - mRemoteAuthConfig, - mIsIkeFragmentationSupported); - } - - // TODO: add methods for supporting IKE fragmentation. - } -} diff --git a/src/java/android/net/ipsec/ike/SaProposal.java b/src/java/android/net/ipsec/ike/SaProposal.java deleted file mode 100644 index 1e0b032e..00000000 --- a/src/java/android/net/ipsec/ike/SaProposal.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (C) 2018 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.ipsec.ike; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.util.ArraySet; -import android.util.SparseArray; - -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -/** - * SaProposal represents a user configured set contains cryptograhic algorithms and key generating - * materials for negotiating an IKE or Child SA. - * - * <p>User must provide at least a valid SaProposal when they are creating a new IKE SA or Child SA. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public abstract class SaProposal { - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - ENCRYPTION_ALGORITHM_3DES, - ENCRYPTION_ALGORITHM_AES_CBC, - ENCRYPTION_ALGORITHM_AES_GCM_8, - ENCRYPTION_ALGORITHM_AES_GCM_12, - ENCRYPTION_ALGORITHM_AES_GCM_16 - }) - public @interface EncryptionAlgorithm {} - - public static final int ENCRYPTION_ALGORITHM_3DES = 3; - public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; - public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; - public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; - public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; - - private static final SparseArray<String> SUPPORTED_ENCRYPTION_ALGO_TO_STR; - - static { - SUPPORTED_ENCRYPTION_ALGO_TO_STR = new SparseArray<>(); - SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_3DES, "ENCR_3DES"); - SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CBC, "ENCR_AES_CBC"); - SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_8, "ENCR_AES_GCM_8"); - SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_12, "ENCR_AES_GCM_12"); - SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_16, "ENCR_AES_GCM_16"); - } - - public static final int KEY_LEN_UNUSED = 0; - public static final int KEY_LEN_AES_128 = 128; - public static final int KEY_LEN_AES_192 = 192; - public static final int KEY_LEN_AES_256 = 256; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({PSEUDORANDOM_FUNCTION_HMAC_SHA1, PSEUDORANDOM_FUNCTION_AES128_XCBC}) - public @interface PseudorandomFunction {} - - public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; - public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; - - private static final SparseArray<String> SUPPORTED_PRF_TO_STR; - - static { - SUPPORTED_PRF_TO_STR = new SparseArray<>(); - SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_HMAC_SHA1, "PRF_HMAC_SHA1"); - SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_XCBC, "PRF_AES128_XCBC"); - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - INTEGRITY_ALGORITHM_NONE, - INTEGRITY_ALGORITHM_HMAC_SHA1_96, - INTEGRITY_ALGORITHM_AES_XCBC_96, - INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, - INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, - INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 - }) - public @interface IntegrityAlgorithm {} - - public static final int INTEGRITY_ALGORITHM_NONE = 0; - public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; - public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; - public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; - public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; - public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; - - private static final SparseArray<String> SUPPORTED_INTEGRITY_ALGO_TO_STR; - - static { - SUPPORTED_INTEGRITY_ALGO_TO_STR = new SparseArray<>(); - SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_NONE, "AUTH_NONE"); - SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96"); - SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_XCBC_96, "AUTH_AES_XCBC_96"); - SUPPORTED_INTEGRITY_ALGO_TO_STR.put( - INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, "AUTH_HMAC_SHA2_256_128"); - SUPPORTED_INTEGRITY_ALGO_TO_STR.put( - INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, "AUTH_HMAC_SHA2_384_192"); - SUPPORTED_INTEGRITY_ALGO_TO_STR.put( - INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, "AUTH_HMAC_SHA2_512_256"); - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({DH_GROUP_NONE, DH_GROUP_1024_BIT_MODP, DH_GROUP_2048_BIT_MODP}) - public @interface DhGroup {} - - public static final int DH_GROUP_NONE = 0; - public static final int DH_GROUP_1024_BIT_MODP = 2; - public static final int DH_GROUP_2048_BIT_MODP = 14; - - private static final SparseArray<String> SUPPORTED_DH_GROUP_TO_STR; - - static { - SUPPORTED_DH_GROUP_TO_STR = new SparseArray<>(); - SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_NONE, "DH_NONE"); - SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1024_BIT_MODP, "DH_1024_BIT_MODP"); - SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_2048_BIT_MODP, "DH_2048_BIT_MODP"); - } - - @IkePayload.ProtocolId private final int mProtocolId; - private final EncryptionTransform[] mEncryptionAlgorithms; - private final IntegrityTransform[] mIntegrityAlgorithms; - private final DhGroupTransform[] mDhGroups; - - protected SaProposal( - @IkePayload.ProtocolId int protocol, - EncryptionTransform[] encryptionAlgos, - IntegrityTransform[] integrityAlgos, - DhGroupTransform[] dhGroups) { - mProtocolId = protocol; - mEncryptionAlgorithms = encryptionAlgos; - mIntegrityAlgorithms = integrityAlgos; - mDhGroups = dhGroups; - } - - /** - * Check if the current SaProposal from the SA responder is consistent with the selected - * reqProposal from the SA initiator. - * - * @param reqProposal selected SaProposal from SA initiator - * @return if current SaProposal from SA responder is consistent with the selected reqProposal - * from SA initiator. - */ - public boolean isNegotiatedFrom(SaProposal reqProposal) { - return this.mProtocolId == reqProposal.mProtocolId - && isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms) - && isTransformSelectedFrom(mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms) - && isTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups); - } - - /** Package private */ - static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) { - // If the selected proposal has multiple transforms with the same type, the responder MUST - // choose a single one. - if ((selected.length > 1) || (selected.length == 0) != (selectFrom.length == 0)) { - return false; - } - - if (selected.length == 0) return true; - - return Arrays.asList(selectFrom).contains(selected[0]); - } - - @IkePayload.ProtocolId - public int getProtocolId() { - return mProtocolId; - } - - public EncryptionTransform[] getEncryptionTransforms() { - return mEncryptionAlgorithms; - } - - public IntegrityTransform[] getIntegrityTransforms() { - return mIntegrityAlgorithms; - } - - public DhGroupTransform[] getDhGroupTransforms() { - return mDhGroups; - } - - protected List<Transform> getAllTransformsAsList() { - List<Transform> transformList = new LinkedList<>(); - - transformList.addAll(Arrays.asList(mEncryptionAlgorithms)); - transformList.addAll(Arrays.asList(mIntegrityAlgorithms)); - transformList.addAll(Arrays.asList(mDhGroups)); - - return transformList; - } - - /** - * Return all SA Transforms in this SaProposal to be encoded for building an outbound IKE - * message. - * - * <p>This method should be called by only IKE library. - * - * @return Array of Transforms to be encoded. - */ - public abstract Transform[] getAllTransforms(); - - /** This class is an abstract Builder for building a SaProposal */ - protected abstract static class Builder { - protected static final String ERROR_TAG = "Invalid SA Proposal: "; - - // Use set to avoid adding repeated algorithms. - protected final Set<EncryptionTransform> mProposedEncryptAlgos = new ArraySet<>(); - protected final Set<PrfTransform> mProposedPrfs = new ArraySet<>(); - protected final Set<IntegrityTransform> mProposedIntegrityAlgos = new ArraySet<>(); - protected final Set<DhGroupTransform> mProposedDhGroups = new ArraySet<>(); - - protected boolean mHasAead = false; - - protected static boolean isAead(@EncryptionAlgorithm int algorithm) { - switch (algorithm) { - case ENCRYPTION_ALGORITHM_3DES: - // Fall through - case ENCRYPTION_ALGORITHM_AES_CBC: - return false; - case ENCRYPTION_ALGORITHM_AES_GCM_8: - // Fall through - case ENCRYPTION_ALGORITHM_AES_GCM_12: - // Fall through - case ENCRYPTION_ALGORITHM_AES_GCM_16: - return true; - default: - // Won't hit here. - throw new IllegalArgumentException("Unsupported Encryption Algorithm."); - } - } - - protected EncryptionTransform[] buildEncryptAlgosOrThrow() { - if (mProposedEncryptAlgos.isEmpty()) { - throw new IllegalArgumentException( - ERROR_TAG + "Encryption algorithm must be proposed."); - } - - return mProposedEncryptAlgos.toArray( - new EncryptionTransform[mProposedEncryptAlgos.size()]); - } - - protected void validateAndAddEncryptAlgo( - @EncryptionAlgorithm int algorithm, int keyLength) { - // Construct EncryptionTransform and validate proposed algorithm during - // construction. - EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength); - - // Validate that only one mode encryption algorithm has been proposed. - boolean isCurrentAead = isAead(algorithm); - if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) { - throw new IllegalArgumentException( - ERROR_TAG - + "Proposal cannot has both normal ciphers " - + "and combined-mode ciphers."); - } - if (isCurrentAead) mHasAead = true; - - mProposedEncryptAlgos.add(encryptionTransform); - } - - protected void addIntegrityAlgo(@IntegrityAlgorithm int algorithm) { - // Construct IntegrityTransform and validate proposed algorithm during - // construction. - mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm)); - } - - protected void addDh(@DhGroup int dhGroup) { - // Construct DhGroupTransform and validate proposed dhGroup during - // construction. - mProposedDhGroups.add(new DhGroupTransform(dhGroup)); - } - } - - @Override - @NonNull - public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append(IkePayload.getProtocolTypeString(mProtocolId)).append(": "); - - int len = getAllTransforms().length; - for (int i = 0; i < len; i++) { - sb.append(getAllTransforms()[i].toString()); - if (i < len - 1) sb.append("|"); - } - - return sb.toString(); - } - - /** - * Check if the provided algorithm is a supported encryption algorithm. - * - * @param algorithm IKE standard encryption algorithm id. - * @return true if the provided algorithm is a supported encryption algorithm. - */ - public static boolean isSupportedEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) { - return SUPPORTED_ENCRYPTION_ALGO_TO_STR.get(algorithm) != null; - } - - /** - * Check if the provided algorithm is a supported pseudorandom function. - * - * @param algorithm IKE standard pseudorandom function id. - * @return true if the provided algorithm is a supported pseudorandom function. - */ - public static boolean isSupportedPseudorandomFunction(@PseudorandomFunction int algorithm) { - return SUPPORTED_PRF_TO_STR.get(algorithm) != null; - } - - /** - * Check if the provided algorithm is a supported integrity algorithm. - * - * @param algorithm IKE standard integrity algorithm id. - * @return true if the provided algorithm is a supported integrity algorithm. - */ - public static boolean isSupportedIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) { - return SUPPORTED_INTEGRITY_ALGO_TO_STR.get(algorithm) != null; - } - - /** - * Check if the provided group number is for a supported Diffie-Hellman Group. - * - * @param dhGroup IKE standard DH Group id. - * @return true if the provided number is for a supported Diffie-Hellman Group. - */ - public static boolean isSupportedDhGroup(@DhGroup int dhGroup) { - return SUPPORTED_DH_GROUP_TO_STR.get(dhGroup) != null; - } - - /** Return the encryption algorithm as a String. */ - public static String getEncryptionAlgorithmString(int algorithm) { - if (isSupportedEncryptionAlgorithm(algorithm)) { - return SUPPORTED_ENCRYPTION_ALGO_TO_STR.get(algorithm); - } - return "ENC_Unknown_" + algorithm; - } - - /** Return the pseudorandom function as a String. */ - public static String getPseudorandomFunctionString(int algorithm) { - if (isSupportedPseudorandomFunction(algorithm)) { - return SUPPORTED_PRF_TO_STR.get(algorithm); - } - return "PRF_Unknown_" + algorithm; - } - - /** Return the integrity algorithm as a String. */ - public static String getIntegrityAlgorithmString(int algorithm) { - if (isSupportedIntegrityAlgorithm(algorithm)) { - return SUPPORTED_INTEGRITY_ALGO_TO_STR.get(algorithm); - } - return "AUTH_Unknown_" + algorithm; - } - - /** Return Diffie-Hellman Group as a String. */ - public static String getDhGroupString(int dhGroup) { - if (isSupportedDhGroup(dhGroup)) { - return SUPPORTED_DH_GROUP_TO_STR.get(dhGroup); - } - return "DH_Unknown_" + dhGroup; - } -} diff --git a/src/java/android/net/ipsec/ike/TransportModeChildSessionOptions.java b/src/java/android/net/ipsec/ike/TransportModeChildSessionOptions.java deleted file mode 100644 index 12e0601a..00000000 --- a/src/java/android/net/ipsec/ike/TransportModeChildSessionOptions.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import android.annotation.NonNull; - -/** - * This class contains all user provided configuration options for negotiating a transport mode - * Child Session. - */ -public final class TransportModeChildSessionOptions extends ChildSessionOptions { - private TransportModeChildSessionOptions( - IkeTrafficSelector[] localTs, - IkeTrafficSelector[] remoteTs, - ChildSaProposal[] proposals) { - super(localTs, remoteTs, proposals, true /*isTransport*/); - } - - /** This class can be used to incrementally construct a TransportModeChildSessionOptions. */ - public static final class Builder extends ChildSessionOptions.Builder { - /** Create a Builder for negotiating a transport mode Child Session. */ - public Builder() { - super(); - } - - /** - * Adds an Child SA proposal to TransportModeChildSessionOptions being built. - * - * @param proposal Child SA proposal. - * @return Builder this, to facilitate chaining. - * @throws IllegalArgumentException if input proposal is not a Child SA proposal. - */ - public Builder addSaProposal(@NonNull ChildSaProposal proposal) { - validateAndAddSaProposal(proposal); - return this; - } - - /** - * Validates, builds and returns the TransportModeChildSessionOptions. - * - * @return the validated TransportModeChildSessionOptions. - * @throws IllegalArgumentException if no Child SA proposal is provided. - */ - public TransportModeChildSessionOptions build() { - validateOrThrow(); - - return new TransportModeChildSessionOptions( - mLocalTsList.toArray(new IkeTrafficSelector[mLocalTsList.size()]), - mRemoteTsList.toArray(new IkeTrafficSelector[mRemoteTsList.size()]), - mSaProposalList.toArray(new ChildSaProposal[mSaProposalList.size()])); - } - } -} diff --git a/src/java/android/net/ipsec/ike/TunnelModeChildSessionOptions.java b/src/java/android/net/ipsec/ike/TunnelModeChildSessionOptions.java deleted file mode 100644 index cb8268cf..00000000 --- a/src/java/android/net/ipsec/ike/TunnelModeChildSessionOptions.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_INET6; - -import android.annotation.NonNull; -import android.net.LinkAddress; - -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Dhcp; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Dns; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Subnet; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Dns; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Subnet; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.LinkedList; -import java.util.List; - -/** - * This class contains all user provided configuration options for negotiating a tunnel mode Child - * Session. - */ -public final class TunnelModeChildSessionOptions extends ChildSessionOptions { - private final ConfigAttribute[] mConfigRequests; - - private TunnelModeChildSessionOptions( - IkeTrafficSelector[] localTs, - IkeTrafficSelector[] remoteTs, - ChildSaProposal[] proposals, - ConfigAttribute[] configRequests) { - super(localTs, remoteTs, proposals, false /*isTransport*/); - mConfigRequests = configRequests; - } - - public ConfigAttribute[] getConfigurationRequests() { - return mConfigRequests; - } - - /** This class can be used to incrementally construct a TunnelModeChildSessionOptions. */ - public static final class Builder extends ChildSessionOptions.Builder { - private static final int IPv4_DEFAULT_PREFIX_LEN = 32; - - private boolean mHasIp4AddressRequest; - private List<ConfigAttribute> mConfigRequestList; - - /** Create a Builder for negotiating a transport mode Child Session. */ - public Builder() { - super(); - mHasIp4AddressRequest = false; - mConfigRequestList = new LinkedList<>(); - } - - /** - * Adds an Child SA proposal to TunnelModeChildSessionOptions being built. - * - * @param proposal Child SA proposal. - * @return Builder this, to facilitate chaining. - * @throws IllegalArgumentException if input proposal is not a Child SA proposal. - */ - public Builder addSaProposal(@NonNull ChildSaProposal proposal) { - validateAndAddSaProposal(proposal); - return this; - } - - /** - * Adds internal IP address requests to TunnelModeChildSessionOptions being built. - * - * @param addressFamily the address family. Only {@link OsConstants.AF_INET} and {@link - * OsConstants.AF_INET6} are allowed. - * @param numOfRequest the number of requests for this type of address. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalAddressRequest(int addressFamily, int numOfRequest) { - if (addressFamily == AF_INET) { - mHasIp4AddressRequest = true; - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv4Address()); - } - return this; - } else if (addressFamily == AF_INET6) { - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv6Address()); - } - return this; - } else { - throw new IllegalArgumentException("Invalid address family: " + addressFamily); - } - } - - /** - * Adds specific internal IP address request to TunnelModeChildSessionOptions being built. - * - * @param address the requested address. - * @param prefixLen length of the InetAddress prefix. When requesting an IPv4 address, - * prefixLen MUST be 32. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalAddressRequest(@NonNull InetAddress address, int prefixLen) { - if (address instanceof Inet4Address) { - if (prefixLen != IPv4_DEFAULT_PREFIX_LEN) { - throw new IllegalArgumentException("Invalid IPv4 prefix length: " + prefixLen); - } - mHasIp4AddressRequest = true; - mConfigRequestList.add(new ConfigAttributeIpv4Address((Inet4Address) address)); - return this; - } else if (address instanceof Inet6Address) { - mConfigRequestList.add( - new ConfigAttributeIpv6Address(new LinkAddress(address, prefixLen))); - return this; - } else { - throw new IllegalArgumentException("Invalid address " + address); - } - } - - /** - * Adds internal DNS server requests to TunnelModeChildSessionOptions being built. - * - * @param addressFamily the address family. Only {@link OsConstants.AF_INET} and {@link - * OsConstants.AF_INET6} are allowed. - * @param numOfRequest the number of requests for this type of address. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalDnsServerRequest(int addressFamily, int numOfRequest) { - if (addressFamily == AF_INET) { - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv4Dns()); - } - return this; - } else if (addressFamily == AF_INET6) { - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv6Dns()); - } - return this; - } else { - throw new IllegalArgumentException("Invalid address family: " + addressFamily); - } - } - - /** - * Adds internal DNS server requests to TunnelModeChildSessionOptions being built. - * - * @param address the requested DNS server address. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalDnsServerRequest(@NonNull InetAddress address) { - if (address instanceof Inet4Address) { - mConfigRequestList.add(new ConfigAttributeIpv4Dns((Inet4Address) address)); - return this; - } else if (address instanceof Inet6Address) { - mConfigRequestList.add(new ConfigAttributeIpv6Dns((Inet6Address) address)); - return this; - } else { - throw new IllegalArgumentException("Invalid address " + address); - } - } - - /** - * Adds internal subnet requests to TunnelModeChildSessionOptions being built. - * - * @param addressFamily the address family. Only {@link OsConstants.AF_INET} and {@link - * OsConstants.AF_INET6} are allowed. - * @param numOfRequest the number of requests for this type of address. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalSubnetRequest(int addressFamily, int numOfRequest) { - if (addressFamily == AF_INET) { - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv4Subnet()); - } - return this; - } else if (addressFamily == AF_INET6) { - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv6Subnet()); - } - return this; - } else { - throw new IllegalArgumentException("Invalid address family: " + addressFamily); - } - } - - /** - * Adds internal DHCP server requests to TunnelModeChildSessionOptions being built. - * - * <p>Only DHCP4 server requests are supported. - * - * @param addressFamily the address family. Only {@link OsConstants.AF_INET} is allowed. - * @param numOfRequest the number of requests for this type of address. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalDhcpServerRequest(int addressFamily, int numOfRequest) { - if (addressFamily == AF_INET) { - for (int i = 0; i < numOfRequest; i++) { - mConfigRequestList.add(new ConfigAttributeIpv4Dhcp()); - } - return this; - } else { - throw new IllegalArgumentException("Invalid address family: " + addressFamily); - } - } - - /** - * Adds internal DHCP server requests to TunnelModeChildSessionOptions being built. - * - * <p>Only DHCP4 server requests are supported. - * - * @param address the requested DHCP server address. - * @return Builder this, to facilitate chaining. - */ - public Builder addInternalDhcpServerRequest(@NonNull InetAddress address) { - if (address instanceof Inet4Address) { - mConfigRequestList.add(new ConfigAttributeIpv4Dhcp((Inet4Address) address)); - return this; - } else { - throw new IllegalArgumentException("Invalid address " + address); - } - } - - /** - * Validates, builds and returns the TunnelModeChildSessionOptions. - * - * @return the validated TunnelModeChildSessionOptions. - * @throws IllegalArgumentException if no Child SA proposal is provided. - */ - public TunnelModeChildSessionOptions build() { - validateOrThrow(); - - if (mHasIp4AddressRequest) { - mConfigRequestList.add(new ConfigAttributeIpv4Netmask()); - } - - return new TunnelModeChildSessionOptions( - mLocalTsList.toArray(new IkeTrafficSelector[mLocalTsList.size()]), - mRemoteTsList.toArray(new IkeTrafficSelector[mRemoteTsList.size()]), - mSaProposalList.toArray(new ChildSaProposal[mSaProposalList.size()]), - mConfigRequestList.toArray(new ConfigAttribute[mConfigRequestList.size()])); - } - } -} diff --git a/src/java/android/net/ipsec/ike/exceptions/IkeException.java b/src/java/android/net/ipsec/ike/exceptions/IkeException.java deleted file mode 100644 index 867dcf5e..00000000 --- a/src/java/android/net/ipsec/ike/exceptions/IkeException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike.exceptions; - -/** - * IkeException is a generic IKE library exception class that provides type safety for all the - * IKE-library-related exception classes that extend from it. - */ -public abstract class IkeException extends Exception { - protected IkeException() { - super(); - } - - protected IkeException(String message) { - super(message); - } - - protected IkeException(Throwable cause) { - super(cause); - } - - protected IkeException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Returns if this exception is caused by an IKE protocol error. - * - * @return true if this exception is caused by an IKE protocol error, false otherwise. - */ - public boolean isProtocolException() { - return this instanceof IkeProtocolException; - } -} diff --git a/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java b/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java deleted file mode 100644 index 29a5af94..00000000 --- a/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike.exceptions; - -/** - * IkeInternalException represents all IKE-library-related exceptions that are not IKE protocol - * error. - */ -public final class IkeInternalException extends IkeException { - /** - * Constructs a new exception with the specified cause. - * - * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} - * method). - */ - public IkeInternalException(Throwable cause) { - super(cause); - } - - /** - * Constructs a new exception with the specified cause. - * - * @param message the descriptive message. - * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} - * method). - */ - public IkeInternalException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/android/net/ipsec/ike/exceptions/IkeProtocolException.java b/src/java/android/net/ipsec/ike/exceptions/IkeProtocolException.java deleted file mode 100644 index f589a267..00000000 --- a/src/java/android/net/ipsec/ike/exceptions/IkeProtocolException.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike.exceptions; - -import android.annotation.IntDef; - -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; - -/** - * IkeProtocolException is an abstract class that represents the common information for all IKE - * protocol errors. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.10.1">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public abstract class IkeProtocolException extends IkeException { - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, - ERROR_TYPE_INVALID_IKE_SPI, - ERROR_TYPE_INVALID_MAJOR_VERSION, - ERROR_TYPE_INVALID_SYNTAX, - ERROR_TYPE_INVALID_MESSAGE_ID, - ERROR_TYPE_NO_PROPOSAL_CHOSEN, - ERROR_TYPE_INVALID_KE_PAYLOAD, - ERROR_TYPE_AUTHENTICATION_FAILED, - ERROR_TYPE_SINGLE_PAIR_REQUIRED, - ERROR_TYPE_NO_ADDITIONAL_SAS, - ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, - ERROR_TYPE_FAILED_CP_REQUIRED, - ERROR_TYPE_TS_UNACCEPTABLE, - ERROR_TYPE_INVALID_SELECTORS, - ERROR_TYPE_TEMPORARY_FAILURE, - ERROR_TYPE_CHILD_SA_NOT_FOUND, - }) - public @interface ErrorType {} - - public static final int ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = 1; - public static final int ERROR_TYPE_INVALID_IKE_SPI = 4; - public static final int ERROR_TYPE_INVALID_MAJOR_VERSION = 5; - public static final int ERROR_TYPE_INVALID_SYNTAX = 7; - public static final int ERROR_TYPE_INVALID_MESSAGE_ID = 9; - public static final int ERROR_TYPE_NO_PROPOSAL_CHOSEN = 14; - public static final int ERROR_TYPE_INVALID_KE_PAYLOAD = 17; - public static final int ERROR_TYPE_AUTHENTICATION_FAILED = 24; - public static final int ERROR_TYPE_SINGLE_PAIR_REQUIRED = 34; - public static final int ERROR_TYPE_NO_ADDITIONAL_SAS = 35; - public static final int ERROR_TYPE_INTERNAL_ADDRESS_FAILURE = 36; - public static final int ERROR_TYPE_FAILED_CP_REQUIRED = 37; - public static final int ERROR_TYPE_TS_UNACCEPTABLE = 38; - public static final int ERROR_TYPE_INVALID_SELECTORS = 39; - public static final int ERROR_TYPE_TEMPORARY_FAILURE = 43; - public static final int ERROR_TYPE_CHILD_SA_NOT_FOUND = 44; - - public static final byte[] ERROR_DATA_NOT_INCLUDED = new byte[0]; - - private static final int INTEGER_BYTE_SIZE = 4; - - @ErrorType private final int mErrorType; - private final byte[] mErrorData; - - protected IkeProtocolException(@ErrorType int code) { - super(); - mErrorType = code; - mErrorData = ERROR_DATA_NOT_INCLUDED; - } - - protected IkeProtocolException(@ErrorType int code, String message) { - super(message); - mErrorType = code; - mErrorData = ERROR_DATA_NOT_INCLUDED; - } - - protected IkeProtocolException(@ErrorType int code, Throwable cause) { - super(cause); - mErrorType = code; - mErrorData = ERROR_DATA_NOT_INCLUDED; - } - - protected IkeProtocolException(@ErrorType int code, String message, Throwable cause) { - super(message, cause); - mErrorType = code; - mErrorData = ERROR_DATA_NOT_INCLUDED; - } - - // Construct an instance from a notify Payload. - protected IkeProtocolException(@ErrorType int code, byte[] notifyData) { - super(); - - if (!isValidDataLength(notifyData.length)) { - throw new IllegalArgumentException( - "Invalid error data for error type: " - + code - + " Received error data size: " - + notifyData.length); - } - - mErrorType = code; - mErrorData = notifyData; - } - - protected abstract boolean isValidDataLength(int dataLen); - - protected static byte[] integerToByteArray(int integer, int arraySize) { - if (arraySize > INTEGER_BYTE_SIZE) { - throw new IllegalArgumentException( - "Cannot convert integer to a byte array of length: " + arraySize); - } - - ByteBuffer dataBuffer = ByteBuffer.allocate(INTEGER_BYTE_SIZE); - dataBuffer.putInt(integer); - dataBuffer.rewind(); - - byte[] zeroPad = new byte[INTEGER_BYTE_SIZE - arraySize]; - byte[] byteData = new byte[arraySize]; - dataBuffer.get(zeroPad).get(byteData); - - return byteData; - } - - protected static int byteArrayToInteger(byte[] byteArray) { - if (byteArray == null || byteArray.length > INTEGER_BYTE_SIZE) { - throw new IllegalArgumentException("Cannot convert the byte array to integer"); - } - - ByteBuffer dataBuffer = ByteBuffer.allocate(INTEGER_BYTE_SIZE); - byte[] zeroPad = new byte[INTEGER_BYTE_SIZE - byteArray.length]; - dataBuffer.put(zeroPad).put(byteArray); - dataBuffer.rewind(); - - return dataBuffer.getInt(); - } - - /** - * Returns the IKE standard protocol error type of this {@link IkeProtocolException} instance. - * - * @return the IKE standard protocol error type. - */ - @ErrorType - public int getErrorType() { - return mErrorType; - } - - /** - * Returns the included error data of this {@link IkeProtocolException} instance. - * - * <p>Note that only few error types will go with an error data. This data has different meaning - * with different error types. Users should first check if an error data is included before they - * call this method. - * - * @return the included error data in byte array. - */ - public byte[] getErrorData() { - return mErrorData; - } - - /** - * Build an IKE Notification Payload for this {@link IkeProtocolException} instance. - * - * @return the notification payload. - */ - public IkeNotifyPayload buildNotifyPayload() { - return new IkeNotifyPayload(mErrorType, mErrorData); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2AwaitingEapFailureStateTest.java b/src/java/com/android/ike/ikev2/ChildSessionOptions.java index 1dd60c0c..b311c666 100644 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2AwaitingEapFailureStateTest.java +++ b/src/java/com/android/ike/ikev2/ChildSessionOptions.java @@ -14,16 +14,12 @@ * limitations under the License. */ -package com.android.internal.net.eap.statemachine; +package com.android.ike.ikev2; -import org.junit.Before; - -public class EapMsChapV2AwaitingEapFailureStateTest extends EapMsChapV2StateTest { - @Before - @Override - public void setUp() { - super.setUp(); - - mStateMachine.transitionTo(mStateMachine.new AwaitingEapFailureState()); - } +/** + * ChildSessionOptions contains user-provided Child SA proposals and negotiated Child SA + * information. + */ +public final class ChildSessionOptions { + // TODO: Implement it. } diff --git a/src/java/com/android/ike/ikev2/ChildSessionStateMachine.java b/src/java/com/android/ike/ikev2/ChildSessionStateMachine.java new file mode 100644 index 00000000..81822d5c --- /dev/null +++ b/src/java/com/android/ike/ikev2/ChildSessionStateMachine.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import android.os.Looper; +import android.os.Message; + +import com.android.ike.ikev2.IkeSessionStateMachine.IChildSessionCallback; +import com.android.ike.ikev2.SaRecord.ChildSaRecord; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.message.IkePayload; +import com.android.ike.ikev2.message.IkeSaPayload; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import java.util.List; + +/** + * ChildSessionStateMachine tracks states and manages exchanges of this Child Session. + * + * <p>ChildSessionStateMachine has two types of states. One type are states where there is no + * ongoing procedure affecting Child Session (non-procedure state), including Initial, Closed, Idle + * and Receiving. All other states are "procedure" states which are named as follows: + * + * <pre> + * State Name = [Procedure Type] + [Exchange Initiator] + [Exchange Type]. + * - An IKE procedure consists of one or two IKE exchanges: + * Procedure Type = {CreateChild | DeleteChild | Info | RekeyChild | SimulRekeyChild}. + * - Exchange Initiator indicates whether local or remote peer is the exchange initiator: + * Exchange Initiator = {Local | Remote} + * - Exchange type defines the function of this exchange. + * Exchange Type = {Create | Delete} + * </pre> + */ +public class ChildSessionStateMachine extends StateMachine { + private static final String TAG = "ChildSessionStateMachine"; + + /** Receive request for negotiating first Child SA. */ + private static final int CMD_HANDLE_FIRST_CHILD_EXCHANGE = 1; + + private final ChildSessionOptions mChildSessionOptions; + + /** Package private */ + @VisibleForTesting ChildSaRecord mCurrentChildSaRecord; + + private final State mInitial = new Initial(); + private final State mClosed = new Closed(); + private final State mIdle = new Idle(); + + /** Package private */ + ChildSessionStateMachine(String name, Looper looper, ChildSessionOptions sessionOptions) { + super(name, looper); + + mChildSessionOptions = sessionOptions; + + addState(mInitial); + addState(mClosed); + addState(mIdle); + + setInitialState(mInitial); + } + + private void validateCreateChildResp( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads) throws IkeException { + // TODO: Validate SA reponse against request and set negotiated SA in mChildSessionOptions. + return; + } + + /** + * Receive requesting and responding payloads for negotiating first Child SA. + * + * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call + * as an asynchronous job to the ChildStateMachine handler. + * + * @param reqPayloads SA negotiation related payloads in IKE_AUTH request. + * @param respPayloads SA negotiation related payloads in IKE_AUTH response. + * @param callback callback for notifying IkeSessionStateMachine the negotiation result. + */ + public void handleFirstChildExchange( + List<IkePayload> reqPayloads, + List<IkePayload> respPayloads, + IChildSessionCallback callback) { + registerProvisionalChildSession(respPayloads, callback); + + sendMessage( + CMD_HANDLE_FIRST_CHILD_EXCHANGE, + new FirstChildNegotiationData(reqPayloads, respPayloads, callback)); + } + + /** + * Register provisioning ChildSessionStateMachine in IChildSessionCallback + * + * <p>This method is for avoiding CHILD_SA_NOT_FOUND error in IkeSessionStateMachine when remote + * peer sends request for delete/rekey this Child SA before ChildSessionStateMachine sends + * FirstChildNegotiationData to itself. + */ + private void registerProvisionalChildSession( + List<IkePayload> respPayloads, IChildSessionCallback callback) { + // When decoding responding IkeSaPayload in IkeSessionStateMachine, it is validated that + // IkeSaPayload has exactly one IkeSaPayload.Proposal. + IkeSaPayload saPayload = null; + for (IkePayload payload : respPayloads) { + if (payload.payloadType == IkePayload.PAYLOAD_TYPE_SA) { + saPayload = (IkeSaPayload) payload; + break; + } + } + if (saPayload == null) { + throw new IllegalArgumentException( + "Receive no SA payload for first Child SA negotiation."); + } + // IkeSaPayload.Proposal stores SPI in long type so as to be applied to both 8-byte IKE SPI + // and 4-byte Child SPI. Here we cast the stored SPI to int to represent a Child SPI. + int remoteGenSpi = (int) (saPayload.proposalList.get(0).spi); + callback.onCreateChildSa(remoteGenSpi, this); + } + + /** + * FirstChildNegotiationData contains payloads for negotiating first Child SA in IKE_AUTH + * request and IKE_AUTH response and callback to notify IkeSessionStateMachine the SA + * negotiation result. + */ + private static class FirstChildNegotiationData { + public final List<IkePayload> requestPayloads; + public final List<IkePayload> responsePayloads; + public final IChildSessionCallback childCallback; + + FirstChildNegotiationData( + List<IkePayload> reqPayloads, + List<IkePayload> respPayloads, + IChildSessionCallback callback) { + requestPayloads = reqPayloads; + responsePayloads = respPayloads; + childCallback = callback; + } + } + + /** Initial state of ChildSessionStateMachine. */ + class Initial extends State { + @Override + public boolean processMessage(Message message) { + switch (message.what) { + // TODO: Handle local request for creating Child SA. + case CMD_HANDLE_FIRST_CHILD_EXCHANGE: + FirstChildNegotiationData childNegotiationData = + (FirstChildNegotiationData) message.obj; + try { + List<IkePayload> reqPayloads = childNegotiationData.requestPayloads; + List<IkePayload> respPayloads = childNegotiationData.responsePayloads; + validateCreateChildResp(reqPayloads, respPayloads); + + mCurrentChildSaRecord = + ChildSaRecord.makeChildSaRecord(reqPayloads, respPayloads); + // TODO: Add mCurrentChildSaRecord in mSpiToSaRecordMap. + transitionTo(mIdle); + } catch (IkeException e) { + // TODO: Unregister remotely generated SPI and handle Child SA negotiation + // failure. + } + return HANDLED; + default: + return NOT_HANDLED; + } + } + } + + /** + * Closed represents the state when this ChildSessionStateMachine is closed, and no further + * actions can be performed on it. + */ + class Closed extends State { + // TODO: Implement it. + } + + /** + * Idle represents a state when there is no ongoing IKE exchange affecting established Child SA. + */ + class Idle extends State { + // TODO: Implement it. + } + + // TODO: Add states to support creating additional Child SA, deleting Child SA and rekeying + // Child SA. +} diff --git a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineFactory.java b/src/java/com/android/ike/ikev2/ChildSessionStateMachineFactory.java index 5282d022..f182111f 100644 --- a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineFactory.java +++ b/src/java/com/android/ike/ikev2/ChildSessionStateMachineFactory.java @@ -14,35 +14,22 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike; +package com.android.ike.ikev2; -import android.content.Context; -import android.net.IpSecManager; -import android.net.ipsec.ike.ChildSessionCallback; -import android.net.ipsec.ike.ChildSessionOptions; import android.os.Looper; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachine.IChildSessionSmCallback; - -import java.util.concurrent.Executor; /** Package private factory for making ChildSessionStateMachine. */ -// TODO: Make it a inner Creator class of ChildSessionStateMachine +//TODO: Make it a inner Creator class of ChildSessionStateMachine final class ChildSessionStateMachineFactory { private static IChildSessionFactoryHelper sChildSessionHelper = new ChildSessionFactoryHelper(); /** Package private. */ static ChildSessionStateMachine makeChildSessionStateMachine( - Looper looper, - Context context, - ChildSessionOptions sessionOptions, - Executor userCbExecutor, - ChildSessionCallback userCallbacks, - IChildSessionSmCallback childSmCallback) { - return sChildSessionHelper.makeChildSessionStateMachine( - looper, context, sessionOptions, userCbExecutor, userCallbacks, childSmCallback); + String name, Looper looper, ChildSessionOptions sessionOptions) { + return sChildSessionHelper.makeChildSessionStateMachine(name, looper, sessionOptions); } @VisibleForTesting @@ -58,12 +45,7 @@ final class ChildSessionStateMachineFactory { */ interface IChildSessionFactoryHelper { ChildSessionStateMachine makeChildSessionStateMachine( - Looper looper, - Context context, - ChildSessionOptions sessionOptions, - Executor userCbExecutor, - ChildSessionCallback userCallbacks, - IChildSessionSmCallback childSmCallback); + String name, Looper looper, ChildSessionOptions sessionOptions); } /** @@ -73,21 +55,9 @@ final class ChildSessionStateMachineFactory { */ static class ChildSessionFactoryHelper implements IChildSessionFactoryHelper { public ChildSessionStateMachine makeChildSessionStateMachine( - Looper looper, - Context context, - ChildSessionOptions sessionOptions, - Executor userCbExecutor, - ChildSessionCallback userCallbacks, - IChildSessionSmCallback childSmCallback) { + String name, Looper looper, ChildSessionOptions sessionOptions) { ChildSessionStateMachine childSession = - new ChildSessionStateMachine( - looper, - context, - (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE), - sessionOptions, - userCbExecutor, - userCallbacks, - childSmCallback); + new ChildSessionStateMachine(name, looper, sessionOptions); childSession.start(); return childSession; } diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeDhParams.java b/src/java/com/android/ike/ikev2/IkeDhParams.java index 44373fc9..604353a9 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeDhParams.java +++ b/src/java/com/android/ike/ikev2/IkeDhParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike; +package com.android.ike.ikev2; /** IkeDhParams contains Diffie-Hellman constants for IKEv2 supported DH Groups */ public class IkeDhParams { diff --git a/src/java/com/android/ike/ikev2/IkeIdentification.java b/src/java/com/android/ike/ikev2/IkeIdentification.java new file mode 100644 index 00000000..994a024f --- /dev/null +++ b/src/java/com/android/ike/ikev2/IkeIdentification.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2; + +import android.annotation.IntDef; +import android.util.ArraySet; + +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.UnknownHostException; +import java.util.Objects; +import java.util.Set; + +/** + * IkeIdentification is abstract base class that represents the common information for all types of + * IKE entity identification. + * + * <p>IkeIdentification can be user configured or be constructed from an inbound packet. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public abstract class IkeIdentification { + // Set of supported ID types. + private static final Set<Integer> SUPPORTED_ID_TYPES; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ID_TYPE_IPV4_ADDR, + ID_TYPE_FQDN, + ID_TYPE_RFC822_ADDR, + ID_TYPE_IPV6_ADDR, + ID_TYPE_DER_ASN1_DN, + ID_TYPE_DER_ASN1_GN, + ID_TYPE_KEY_ID + }) + public @interface IdType {} + + public static final int ID_TYPE_IPV4_ADDR = 1; + public static final int ID_TYPE_FQDN = 2; + public static final int ID_TYPE_RFC822_ADDR = 3; + public static final int ID_TYPE_IPV6_ADDR = 5; + public static final int ID_TYPE_DER_ASN1_DN = 9; + public static final int ID_TYPE_DER_ASN1_GN = 10; + public static final int ID_TYPE_KEY_ID = 11; + + static { + SUPPORTED_ID_TYPES = new ArraySet(); + SUPPORTED_ID_TYPES.add(ID_TYPE_IPV4_ADDR); + SUPPORTED_ID_TYPES.add(ID_TYPE_FQDN); + SUPPORTED_ID_TYPES.add(ID_TYPE_RFC822_ADDR); + SUPPORTED_ID_TYPES.add(ID_TYPE_IPV6_ADDR); + SUPPORTED_ID_TYPES.add(ID_TYPE_DER_ASN1_DN); + SUPPORTED_ID_TYPES.add(ID_TYPE_DER_ASN1_GN); + SUPPORTED_ID_TYPES.add(ID_TYPE_KEY_ID); + } + + public final int idType; + + protected IkeIdentification(@IdType int type) { + idType = type; + } + + /** + * Return the encoded identification data in a byte array. + * + * @return the encoded identification data. + */ + public abstract byte[] getEncodedIdData(); + + /** IkeIpv4AddrIdentification represents ID information in IPv4 address ID type. */ + public static class IkeIpv4AddrIdentification extends IkeIdentification { + public final Inet4Address ipv4Address; + + /** + * Construct an instance of IkeIpv4AddrIdentification from decoding an inbound packet. + * + * @param ipv4AddrBytes IPv4 address in byte array. + * @throws AuthenticationFailedException for decoding bytes error. + */ + public IkeIpv4AddrIdentification(byte[] ipv4AddrBytes) + throws AuthenticationFailedException { + super(ID_TYPE_IPV4_ADDR); + try { + ipv4Address = (Inet4Address) (Inet4Address.getByAddress(ipv4AddrBytes)); + } catch (ClassCastException | UnknownHostException e) { + throw new AuthenticationFailedException(e); + } + } + + /** + * Construct an instance of IkeIpv4AddrIdentification with user provided IPv4 address for + * building outbound packet. + * + * @param address user provided IPv4 address + */ + public IkeIpv4AddrIdentification(Inet4Address address) { + super(ID_TYPE_IPV4_ADDR); + ipv4Address = address; + } + + @Override + public int hashCode() { + return Objects.hash(idType, ipv4Address); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IkeIpv4AddrIdentification)) return false; + + return ipv4Address.equals(((IkeIpv4AddrIdentification) o).ipv4Address); + } + + /** + * Retrieve the byte-representation of the IPv4 address. + * + * @return the byte-representation of the IPv4 address. + */ + @Override + public byte[] getEncodedIdData() { + return ipv4Address.getAddress(); + } + } + + /** IkeIpv6AddrIdentification represents ID information in IPv6 address ID type. */ + public static class IkeIpv6AddrIdentification extends IkeIdentification { + public final Inet6Address ipv6Address; + + /** + * Construct an instance of IkeIpv6AddrIdentification from decoding an inbound packet. + * + * @param ipv6AddrBytes IPv6 address in byte array. + * @throws AuthenticationFailedException for decoding bytes error. + */ + public IkeIpv6AddrIdentification(byte[] ipv6AddrBytes) + throws AuthenticationFailedException { + super(ID_TYPE_IPV6_ADDR); + try { + ipv6Address = (Inet6Address) (Inet6Address.getByAddress(ipv6AddrBytes)); + } catch (ClassCastException | UnknownHostException e) { + throw new AuthenticationFailedException(e); + } + } + + /** + * Construct an instance of IkeIpv6AddrIdentification with user provided IPv6 address for + * building outbound packet. + * + * @param address user provided IPv6 address + */ + public IkeIpv6AddrIdentification(Inet6Address address) { + super(ID_TYPE_IPV6_ADDR); + ipv6Address = address; + } + + @Override + public int hashCode() { + return Objects.hash(idType, ipv6Address); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IkeIpv6AddrIdentification)) return false; + + return ipv6Address.equals(((IkeIpv6AddrIdentification) o).ipv6Address); + } + + /** + * Retrieve the byte-representation of the IPv6 address. + * + * @return the byte-representation of the IPv6 address. + */ + @Override + public byte[] getEncodedIdData() { + return ipv6Address.getAddress(); + } + } +} diff --git a/src/java/com/android/ike/ikev2/IkeSessionOptions.java b/src/java/com/android/ike/ikev2/IkeSessionOptions.java new file mode 100644 index 00000000..9f2094f2 --- /dev/null +++ b/src/java/com/android/ike/ikev2/IkeSessionOptions.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import android.net.IpSecManager.UdpEncapsulationSocket; + +import com.android.ike.ikev2.message.IkePayload; + +import java.net.InetAddress; +import java.util.LinkedList; +import java.util.List; + +/** + * IkeSessionOptions contains all user provided configurations for negotiating an IKE SA. + * + * <p>TODO: Make this doc more user-friendly. + */ +public final class IkeSessionOptions { + private final InetAddress mServerAddress; + private final UdpEncapsulationSocket mUdpEncapSocket; + private final SaProposal[] mSaProposals; + private final boolean mIsIkeFragmentationSupported; + + private IkeSessionOptions( + InetAddress serverAddress, + UdpEncapsulationSocket udpEncapsulationSocket, + SaProposal[] proposals, + boolean isIkeFragmentationSupported) { + mServerAddress = serverAddress; + mUdpEncapSocket = udpEncapsulationSocket; + mSaProposals = proposals; + mIsIkeFragmentationSupported = isIkeFragmentationSupported; + } + + /** Package private */ + InetAddress getServerAddress() { + return mServerAddress; + } + /** Package private */ + UdpEncapsulationSocket getUdpEncapsulationSocket() { + return mUdpEncapSocket; + } + /** Package private */ + SaProposal[] getSaProposals() { + return mSaProposals; + } + /** Package private */ + boolean isIkeFragmentationSupported() { + return mIsIkeFragmentationSupported; + } + + /** This class can be used to incrementally construct a IkeSessionOptions. */ + public static final class Builder { + private final InetAddress mServerAddress; + private final UdpEncapsulationSocket mUdpEncapSocket; + private final List<SaProposal> mSaProposalList = new LinkedList<>(); + + private boolean mIsIkeFragmentationSupported = false; + + /** + * Returns a new Builder for an IkeSessionOptions. + * + * @param serverAddress IP address of remote IKE server. + * @param udpEncapsulationSocket {@link IpSecManager.UdpEncapsulationSocket} for sending and + * receiving IKE message. + * @return Builder for an IkeSessionOptions. + */ + public Builder(InetAddress serverAddress, UdpEncapsulationSocket udpEncapsulationSocket) { + mServerAddress = serverAddress; + mUdpEncapSocket = udpEncapsulationSocket; + } + + /** + * Adds an IKE SA proposal to IkeSessionOptions being built. + * + * @param proposal IKE SA proposal. + * @return Builder for an IkeSessionOptions. + * @throws IllegalArgumentException if input proposal is not IKE SA proposal. + */ + public Builder addSaProposal(SaProposal proposal) { + if (proposal.getProtocolId() != IkePayload.PROTOCOL_ID_IKE) { + throw new IllegalArgumentException( + "Expected IKE SA Proposal but received Child SA proposal"); + } + mSaProposalList.add(proposal); + return this; + } + + /** + * Validates, builds and returns the IkeSessionOptions + * + * @return IkeSessionOptions the validated IkeSessionOptions + * @throws IllegalStateException if no IKE SA proposal is provided + */ + public IkeSessionOptions build() { + if (mSaProposalList.isEmpty()) { + throw new IllegalArgumentException("IKE SA proposal not found"); + } + return new IkeSessionOptions( + mServerAddress, + mUdpEncapSocket, + mSaProposalList.toArray(new SaProposal[mSaProposalList.size()]), + mIsIkeFragmentationSupported); + } + + // TODO: add methods for supporting IKE fragmentation. + } +} diff --git a/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java b/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java new file mode 100644 index 00000000..b4a7b9c6 --- /dev/null +++ b/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java @@ -0,0 +1,952 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import android.os.Looper; +import android.os.Message; +import android.system.ErrnoException; +import android.util.LongSparseArray; +import android.util.SparseArray; + +import com.android.ike.ikev2.SaRecord.IkeSaRecord; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.message.IkeHeader; +import com.android.ike.ikev2.message.IkeKePayload; +import com.android.ike.ikev2.message.IkeMessage; +import com.android.ike.ikev2.message.IkeNoncePayload; +import com.android.ike.ikev2.message.IkeNotifyPayload; +import com.android.ike.ikev2.message.IkePayload; +import com.android.ike.ikev2.message.IkeSaPayload; +import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * IkeSessionStateMachine tracks states and manages exchanges of this IKE session. + * + * <p>IkeSessionStateMachine has two types of states. One type are states where there is no ongoing + * procedure affecting IKE session (non-procedure state), including Initial, Closed, Idle and + * Receiving. All other states are "procedure" states which are named as follows: + * + * <pre> + * State Name = [Procedure Type] + [Exchange Initiator] + [Exchange Type]. + * - An IKE procedure consists of one or two IKE exchanges: + * Procedure Type = {CreateIke | DeleteIke | Info | RekeyIke | SimulRekeyIke}. + * - Exchange Initiator indicates whether local or remote peer is the exchange initiator: + * Exchange Initiator = {Local | Remote} + * - Exchange type defines the function of this exchange. To make it more descriptive, we separate + * Delete Exchange from generic Informational Exchange: + * Exchange Type = {IkeInit | IkeAuth | Create | Delete | Info} + * </pre> + */ +public class IkeSessionStateMachine extends StateMachine { + + private static final String TAG = "IkeSessionStateMachine"; + + /** Package private signals accessible for testing code. */ + private static final int CMD_GENERAL_BASE = 0; + /** Receive encoded IKE packet on IkeSessionStateMachine. */ + static final int CMD_RECEIVE_IKE_PACKET = CMD_GENERAL_BASE + 1; + /** Receive locally built payloads from Child Session for building outbound IKE message. */ + static final int CMD_RECEIVE_OUTBOUND_CHILD_PAYLOADS = CMD_GENERAL_BASE + 2; + /** Receive encoded IKE packet with unrecognized IKE SPI on IkeSessionStateMachine. */ + static final int CMD_RECEIVE_PACKET_INVALID_IKE_SPI = CMD_GENERAL_BASE + 3; + // TODO: Add signal for retransmission. + + private static final int CMD_LOCAL_REQUEST_BASE = CMD_GENERAL_BASE + 100; + static final int CMD_LOCAL_REQUEST_CREATE_IKE = CMD_LOCAL_REQUEST_BASE + 1; + static final int CMD_LOCAL_REQUEST_DELETE_IKE = CMD_LOCAL_REQUEST_BASE + 2; + static final int CMD_LOCAL_REQUEST_REKEY_IKE = CMD_LOCAL_REQUEST_BASE + 3; + static final int CMD_LOCAL_REQUEST_INFO = CMD_LOCAL_REQUEST_BASE + 4; + static final int CMD_LOCAL_REQUEST_CREATE_CHILD = CMD_LOCAL_REQUEST_BASE + 5; + static final int CMD_LOCAL_REQUEST_DELETE_CHILD = CMD_LOCAL_REQUEST_BASE + 6; + static final int CMD_LOCAL_REQUEST_REKEY_CHILD = CMD_LOCAL_REQUEST_BASE + 7; + // TODO: Add signals for other procedure types and notificaitons. + + // Remember locally assigned IKE SPIs to avoid SPI collision. + private static final Set<Long> ASSIGNED_LOCAL_IKE_SPI_SET = new HashSet<>(); + private static final int MAX_ASSIGN_IKE_SPI_ATTEMPTS = 100; + private static final SecureRandom IKE_SPI_RANDOM = new SecureRandom(); + + private final IkeSessionOptions mIkeSessionOptions; + private final ChildSessionOptions mFirstChildSessionOptions; + /** Map that stores all IkeSaRecords, keyed by remotely generated IKE SPI. */ + private final LongSparseArray<IkeSaRecord> mSpiToSaRecordMap; + /** + * Map that stores all ChildSessionStateMachines, keyed by remotely generated Child SPI for + * sending IPsec packet. Different SPIs may point to the same ChildSessionStateMachine if this + * Child Session is doing Rekey. + */ + private final SparseArray<ChildSessionStateMachine> mSpiToChildSessionMap; + + /** + * Package private socket that sends and receives encoded IKE message. Initialized in Initial + * State. + */ + @VisibleForTesting IkeSocket mIkeSocket; + + /** Package */ + @VisibleForTesting IkeSaRecord mCurrentIkeSaRecord; + /** Package */ + @VisibleForTesting IkeSaRecord mLocalInitNewIkeSaRecord; + /** Package */ + @VisibleForTesting IkeSaRecord mRemoteInitNewIkeSaRecord; + + /** Package */ + @VisibleForTesting IkeSaRecord mIkeSaRecordSurviving; + /** Package */ + @VisibleForTesting IkeSaRecord mIkeSaRecordAwaitingLocalDel; + /** Package */ + @VisibleForTesting IkeSaRecord mIkeSaRecordAwaitingRemoteDel; + + // States + private final State mInitial = new Initial(); + private final State mClosed = new Closed(); + private final State mIdle = new Idle(); + private final State mReceiving = new Receiving(); + private final State mCreateIkeLocalIkeInit = new CreateIkeLocalIkeInit(); + private final State mCreateIkeLocalIkeAuth = new CreateIkeLocalIkeAuth(); + private final State mRekeyIkeLocalCreate = new RekeyIkeLocalCreate(); + private final State mSimulRekeyIkeLocalCreate = new SimulRekeyIkeLocalCreate(); + private final State mSimulRekeyIkeLocalDeleteRemoteDelete = + new SimulRekeyIkeLocalDeleteRemoteDelete(); + private final State mSimulRekeyIkeLocalDelete = new SimulRekeyIkeLocalDelete(); + private final State mSimulRekeyIkeRemoteDelete = new SimulRekeyIkeRemoteDelete(); + private final State mRekeyIkeLocalDelete = new RekeyIkeLocalDelete(); + private final State mRekeyIkeRemoteDelete = new RekeyIkeRemoteDelete(); + // TODO: Add InfoLocal and DeleteIkeLocal. + + /** Package private constructor */ + IkeSessionStateMachine( + String name, + Looper looper, + IkeSessionOptions ikeOptions, + ChildSessionOptions firstChildOptions) { + super(name, looper); + mIkeSessionOptions = ikeOptions; + mFirstChildSessionOptions = firstChildOptions; + // There are at most three IkeSaRecords co-existing during simultaneous rekeying. + mSpiToSaRecordMap = new LongSparseArray<>(3); + mSpiToChildSessionMap = new SparseArray<>(); + + addState(mInitial); + addState(mClosed); + addState(mCreateIkeLocalIkeInit); + addState(mCreateIkeLocalIkeAuth); + addState(mIdle); + addState(mReceiving); + addState(mRekeyIkeLocalCreate); + addState(mSimulRekeyIkeLocalCreate, mRekeyIkeLocalCreate); + addState(mSimulRekeyIkeLocalDeleteRemoteDelete); + addState(mSimulRekeyIkeLocalDelete, mSimulRekeyIkeLocalDeleteRemoteDelete); + addState(mSimulRekeyIkeRemoteDelete, mSimulRekeyIkeLocalDeleteRemoteDelete); + addState(mRekeyIkeLocalDelete); + addState(mRekeyIkeRemoteDelete); + + setInitialState(mInitial); + } + + // Generate IKE SPI. Throw an exception if it failed and handle this exception in current State. + private static Long getIkeSpiOrThrow() { + for (int i = 0; i < MAX_ASSIGN_IKE_SPI_ATTEMPTS; i++) { + long spi = IKE_SPI_RANDOM.nextLong(); + if (ASSIGNED_LOCAL_IKE_SPI_SET.add(spi)) return spi; + } + throw new IllegalStateException("Failed to generate IKE SPI."); + } + + private IkeMessage buildIkeInitReq() { + // TODO: Handle IKE SPI assigning error in CreateIkeLocalIkeInit State. + + List<IkePayload> payloadList = new LinkedList<>(); + + // Generate IKE SPI + long initSpi = getIkeSpiOrThrow(); + long respSpi = 0; + + // It is validated in IkeSessionOptions.Builder to ensure IkeSessionOptions has at least one + // SaProposal and all SaProposals are valid for IKE SA negotiation. + SaProposal[] saProposals = mIkeSessionOptions.getSaProposals(); + + // Build SA Payload + IkeSaPayload saPayload = new IkeSaPayload(saProposals); + payloadList.add(saPayload); + + // Build KE Payload using the first DH group number in the first SaProposal. + DhGroupTransform dhGroupTransform = saProposals[0].getDhGroupTransforms()[0]; + IkeKePayload kePayload = new IkeKePayload(dhGroupTransform.id); + payloadList.add(kePayload); + + // Build Nonce Payload + IkeNoncePayload noncePayload = new IkeNoncePayload(); + payloadList.add(noncePayload); + + // TODO: Add Notification Payloads according to user configurations. + + // Build IKE header + IkeHeader ikeHeader = + new IkeHeader( + initSpi, + respSpi, + IkePayload.PAYLOAD_TYPE_SA, + IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, + false /*isResponseMsg*/, + true /*fromIkeInitiator*/, + 0 /*messageId*/); + + return new IkeMessage(ikeHeader, payloadList); + } + + private IkeMessage buildIkeAuthReq() { + // TODO: Build IKE_AUTH request according to mIkeSessionOptions and + // firstChildSessionOptions. + return null; + } + + private IkeMessage buildIkeDeleteReq(IkeSaRecord ikeSaRecord) { + // TODO: Implement it. + return null; + } + + private IkeMessage buildIkeDeleteResp(IkeSaRecord ikeSaRecord) { + // TODO: Implement it. + return null; + } + + private IkeMessage buildIkeRekeyReq() { + // TODO: Implement it. + return null; + } + + private IkeMessage buildIkeRekeyResp(IkeMessage reqMsg) { + // TODO: Implement it. + return null; + } + + private void validateIkeInitResp(IkeMessage reqMsg, IkeMessage respMsg) throws IkeException { + // TODO: Validate ikeMessage against IKE_INIT request and set confiugration negotiation + // results + // in mIkeSessionOptions(e.g.NAT detecting result). + } + + private void validateIkeAuthResp(IkeMessage reqMsg, IkeMessage respMsg) throws IkeException { + // TODO: Validate ikeMessage against IKE_AUTH request and mIkeSessionOptions. + } + + private void validateIkeDeleteReq(IkeMessage ikeMessage) throws IkeException { + // TODO: Validate ikeMessage. + } + + private void validateIkeDeleteResp(IkeMessage ikeMessage) throws IkeException { + // TODO: Validate ikeMessage. + } + + private void validateIkeRekeyReq(IkeMessage ikeMessage) throws IkeException { + // TODO: Validate it againsr mIkeSessionOptions. + } + + private void validateIkeRekeyResp(IkeMessage reqMsg, IkeMessage respMsg) throws IkeException { + // TODO: Validate ikeMessage against Rekey request. + } + + // TODO: Add methods for building and validating general Informational packet. + + private void addIkeSaRecord(IkeSaRecord record) { + mSpiToSaRecordMap.put(record.getRemoteSpi(), record); + } + + private void removeIkeSaRecord(IkeSaRecord record) { + mSpiToSaRecordMap.remove(record.getRemoteSpi()); + } + + /** + * Receive IKE packet from remote server. + * + * <p>This method is called synchronously from IkeSocket. It proxies the synchronous call as an + * asynchronous job to the IkeSessionStateMachine handler. + * + * @param ikeHeader the decoded IKE header. + * @param ikePacketBytes the byte array of the entire received IKE packet. + */ + public void receiveIkePacket(IkeHeader ikeHeader, byte[] ikePacketBytes) { + sendMessage(CMD_RECEIVE_IKE_PACKET, new ReceivedIkePacket(ikeHeader, ikePacketBytes)); + } + + /** + * ReceivedIkePacket is a package private data container consists of decoded IkeHeader and + * encoded IKE packet in a byte array. + */ + static class ReceivedIkePacket { + /** Decoded IKE header */ + public final IkeHeader ikeHeader; + /** Entire encoded IKE message including IKE header */ + public final byte[] ikePacketBytes; + + ReceivedIkePacket(IkeHeader ikeHeader, byte[] ikePacketBytes) { + this.ikeHeader = ikeHeader; + this.ikePacketBytes = ikePacketBytes; + } + } + + /** + * Interface for ChildSessionStateMachine to notify IkeSessionStateMachine. + * + * <p>Package private so as to be injectable for testing. + */ + interface IChildSessionCallback { + /** Notify that new Child SA is created. */ + void onCreateChildSa(int remoteSpi, ChildSessionStateMachine childSession); + /** Notify that the Child SA is deleted. */ + void onDeleteChildSa(int remoteSpi); + // TODO: Add methods for handling errors and sending out locally built payloads. + } + + /** + * Callback for ChildSessionStateMachine to notify IkeSessionStateMachine. + * + * <p>Package private for being passed to only ChildSessionStateMachine. + */ + class ChildSessionCallback implements IChildSessionCallback { + public void onCreateChildSa(int remoteSpi, ChildSessionStateMachine childSession) { + mSpiToChildSessionMap.put(remoteSpi, childSession); + } + + public void onDeleteChildSa(int remoteSpi) { + mSpiToChildSessionMap.remove(remoteSpi); + } + } + + /** Initial state of IkeSessionStateMachine. */ + class Initial extends State { + @Override + public void enter() { + try { + mIkeSocket = IkeSocket.getIkeSocket(mIkeSessionOptions.getUdpEncapsulationSocket()); + } catch (ErrnoException e) { + // TODO: handle exception and close IkeSession. + } + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_LOCAL_REQUEST_CREATE_IKE: + transitionTo(mCreateIkeLocalIkeInit); + return HANDLED; + default: + return NOT_HANDLED; + } + } + } + + /** + * Closed represents the state when this IkeSessionStateMachine is closed, and no further + * actions can be performed on it. + */ + class Closed extends State { + // TODO:Implement it. + } + + /** + * Idle represents a state when there is no ongoing IKE exchange affecting established IKE SA. + */ + class Idle extends State { + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_RECEIVE_IKE_PACKET: + deferMessage(message); + transitionTo(mReceiving); + return HANDLED; + case CMD_LOCAL_REQUEST_REKEY_IKE: + transitionTo(mRekeyIkeLocalCreate); + return HANDLED; + default: + return NOT_HANDLED; + // TODO: Add more cases for supporting local request. + } + } + } + + /** Base state defines common behaviours when receiving an IKE packet. */ + private abstract class BaseState extends State { + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_RECEIVE_IKE_PACKET: + handleReceivedIkePacket(message); + return HANDLED; + default: + return NOT_HANDLED; + } + } + + protected void handleReceivedIkePacket(Message message) { + ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; + IkeHeader ikeHeader = receivedIkePacket.ikeHeader; + byte[] ikePacketBytes = receivedIkePacket.ikePacketBytes; + IkeSaRecord ikeSaRecord = getIkeSaRecordForPacket(ikeHeader); + try { + IkeMessage ikeMessage = + IkeMessage.decode( + mIkeSessionOptions, ikeSaRecord, ikeHeader, ikePacketBytes); + int messageType = ikeMessage.getMessageType(); + // TODO: Handle fatal error notifications. + handleIkeMessage(ikeMessage, messageType, message); + } catch (IkeException e) { + + } catch (GeneralSecurityException e) { + // IKE library failed on intergity checksum validation or on message decryption. + // TODO: Handle decrypting exception + } + } + + // Default handler for decode errors in encrypted request. + protected void handleDecodingErrorInEncryptedRequest( + IkeException exception, IkeSaRecord ikeSaRecord) { + switch (exception.errorCode) { + case IkeNotifyPayload.NOTIFY_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD: + // TODO: Send encrypted error notification. + return; + case IkeNotifyPayload.NOTIFY_TYPE_INVALID_MAJOR_VERSION: + // TODO: Send unencrypted error notification. + return; + case IkeNotifyPayload.NOTIFY_TYPE_INVALID_SYNTAX: + // TODO: Send encrypted error notification and close IKE session if Message ID + // and cryptogtaphic checksum were invalid. + return; + default: + // Won't hit this case. + throw new UnsupportedOperationException("Unknown error decoding IKE Message."); + } + } + + // Default handler for decode errors in encrypted responses. + // NOTE: The DeleteIkeLocal state MUST override this state to avoid the possibility of an + // infinite loop. + protected void handleDecodingErrorInEncryptedResponse( + IkeException exception, IkeSaRecord ikeSaRecord) { + // All errors in parsing or processing reponse packets should cause the IKE library to + // initiate a Delete IKE Exchange. + + // TODO: Initiate Delete IKE Exchange + } + + protected IkeSaRecord getIkeSaRecordForPacket(IkeHeader ikeHeader) { + if (ikeHeader.fromIkeInitiator) { + return mSpiToSaRecordMap.get(ikeHeader.ikeInitiatorSpi); + } else { + return mSpiToSaRecordMap.get(ikeHeader.ikeResponderSpi); + } + } + + protected abstract void handleIkeMessage( + IkeMessage ikeMessage, int messageType, Message message); + } + + /** + * Receiving represents a state when idle IkeSessionStateMachine receives an incoming packet. + */ + class Receiving extends BaseState { + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_REKEY_IKE_REQ: + try { + validateIkeRekeyReq(ikeMessage); + // Reply + IkeMessage responseIkeMessage = buildIkeRekeyResp(ikeMessage); + // TODO: Encode and send out responseIkeMessage + + mRemoteInitNewIkeSaRecord = + IkeSaRecord.makeNewIkeSaRecord( + mCurrentIkeSaRecord, ikeMessage, responseIkeMessage); + addIkeSaRecord(mRemoteInitNewIkeSaRecord); + transitionTo(mRekeyIkeRemoteDelete); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + // TODO: Add more cases for supporting local request. + default: + } + } + } + + /** + * LocalNewExchangeBase represents the common behaviours when IKE library initiates a new + * exchange. + */ + private abstract class LocalNewExchangeBase extends BaseState { + protected IkeMessage mRequestMsg; + protected byte[] mRequestPacket; + + @Override + public void enter() { + mRequestMsg = buildRequest(); + mRequestPacket = encodeRequest(); + mIkeSocket.sendIkePacket(mRequestPacket, mIkeSessionOptions.getServerAddress()); + // TODO: Send out packet and start retransmission timer. + } + + @Override + public void exit() { + // TODO: Stop retransmission + mRequestMsg = null; + mRequestPacket = null; + } + + protected abstract IkeMessage buildRequest(); + + // CreateIkeLocalInit should override encodeRequest() to encode unencrypted packet + protected byte[] encodeRequest() { + // TODO: encrypt and encode mRequestMsg + return new byte[0]; + } + } + + /** CreateIkeLocalIkeInit represents state when IKE library initiates IKE_INIT exchange. */ + class CreateIkeLocalIkeInit extends LocalNewExchangeBase { + + @Override + public void enter() { + super.enter(); + mIkeSocket.registerIke( + mRequestMsg.ikeHeader.ikeInitiatorSpi, IkeSessionStateMachine.this); + } + + @Override + protected IkeMessage buildRequest() { + return buildIkeInitReq(); + } + + @Override + protected byte[] encodeRequest() { + return mRequestMsg.encode(); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_RECEIVE_IKE_PACKET: + handleReceivedIkePacket(message); + return HANDLED; + default: + return NOT_HANDLED; + } + } + + protected void handleReceivedIkePacket(Message message) { + ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; + IkeHeader ikeHeader = receivedIkePacket.ikeHeader; + byte[] ikePacketBytes = receivedIkePacket.ikePacketBytes; + try { + IkeMessage ikeMessage = IkeMessage.decode(ikeHeader, ikePacketBytes); + int messageType = ikeMessage.getMessageType(); + // TODO: Handle fatal error notifications. + handleIkeMessage(ikeMessage, messageType, message); + } catch (IkeException e) { + // TODO:Since IKE_INIT is not protected, log and ignore this message. + } + } + + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_IKE_INIT_RESP: + try { + validateIkeInitResp(mRequestMsg, ikeMessage); + mCurrentIkeSaRecord = + IkeSaRecord.makeFirstIkeSaRecord(mRequestMsg, ikeMessage); + addIkeSaRecord(mCurrentIkeSaRecord); + transitionTo(mCreateIkeLocalIkeAuth); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + default: + // TODO: Handle unexpected message type. + } + } + + @Override + public void exit() { + super.exit(); + // TODO: Store IKE_INIT request and response in mIkeSessionOptions for IKE_AUTH + } + } + + /** CreateIkeLocalIkeAuth represents state when IKE library initiates IKE_AUTH exchange. */ + class CreateIkeLocalIkeAuth extends LocalNewExchangeBase { + @Override + protected IkeMessage buildRequest() { + return buildIkeAuthReq(); + } + + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + // TODO: Handle EAP Authentication. + case IkeMessage.MESSAGE_TYPE_IKE_AUTH_RESP: + try { + validateIkeAuthResp(mRequestMsg, ikeMessage); + + ChildSessionStateMachine firstChild = + ChildSessionStateMachineFactory.makeChildSessionStateMachine( + "ChildSessionStateMachine", + getHandler().getLooper(), + mFirstChildSessionOptions); + // TODO: Replace null input params to payload lists in IKE_AUTH request and + // IKE_AUTH response for negotiating Child SA. + firstChild.handleFirstChildExchange(null, null, new ChildSessionCallback()); + + transitionTo(mIdle); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + default: + // TODO: Add more cases for other packet types (e.g. for receiving and sending + // EAP). + } + } + } + + /** RekeyIkeLocalCreate represents state when IKE library initiates Rekey IKE exchange. */ + class RekeyIkeLocalCreate extends LocalNewExchangeBase { + @Override + public IkeMessage buildRequest() { + return buildIkeRekeyReq(); + } + + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_REKEY_IKE_RESP: + try { + handleRekeyResp(ikeMessage); + transitionTo(mRekeyIkeLocalDelete); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + case IkeMessage.MESSAGE_TYPE_REKEY_IKE_REQ: + try { + validateIkeRekeyReq(ikeMessage); + // Reply + IkeMessage responseIkeMessage = buildIkeRekeyResp(ikeMessage); + mRemoteInitNewIkeSaRecord = + IkeSaRecord.makeNewIkeSaRecord( + mCurrentIkeSaRecord, ikeMessage, responseIkeMessage); + addIkeSaRecord(mRemoteInitNewIkeSaRecord); + // TODO: Encode and send responseIkeMessage. + + transitionTo(mSimulRekeyIkeLocalCreate); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + default: + // TODO: Add more cases for other packet types. + } + } + + // Is also called by SimulRekeyIkeLocalCreate to handle incoming rekey response. + protected void handleRekeyResp(IkeMessage ikeMessage) throws IkeException { + validateIkeRekeyResp(mRequestMsg, ikeMessage); + mLocalInitNewIkeSaRecord = + IkeSaRecord.makeNewIkeSaRecord(mCurrentIkeSaRecord, mRequestMsg, ikeMessage); + addIkeSaRecord(mLocalInitNewIkeSaRecord); + // TODO: Stop retransmission + } + } + + /** + * SimulRekeyIkeLocalCreate represents the state where IKE library has replied to rekey request + * sent from the remote and is waiting for a rekey response for a locally initiated rekey + * request. + * + * <p>SimulRekeyIkeLocalCreate extends RekeyIkeLocalCreate so that it can call super class to + * validate incoming rekey response against locally initiated rekey request. + */ + class SimulRekeyIkeLocalCreate extends RekeyIkeLocalCreate { + @Override + public void enter() { + // Do not send request. + } + + @Override + public IkeMessage buildRequest() { + throw new UnsupportedOperationException( + "Do not support sending request in " + getCurrentState().getName()); + } + + @Override + public void exit() { + // Do nothing. + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_RECEIVE_IKE_PACKET: + ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; + IkeHeader ikeHeader = receivedIkePacket.ikeHeader; + + if (mRemoteInitNewIkeSaRecord == getIkeSaRecordForPacket(ikeHeader)) { + deferMessage(message); + } else { + handleReceivedIkePacket(message); + } + return HANDLED; + default: + return NOT_HANDLED; + } + } + + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_DELETE_IKE_REQ: + deferMessage(message); + return; + case IkeMessage.MESSAGE_TYPE_REKEY_IKE_RESP: + try { + super.handleRekeyResp(ikeMessage); + transitionTo(mSimulRekeyIkeLocalDeleteRemoteDelete); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + default: + // TODO: Add more cases for other packet types. + } + } + } + + /** RekeyIkeDeleteBase represents common behaviours of deleting stage during rekeying IKE SA. */ + private abstract class RekeyIkeDeleteBase extends BaseState { + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_RECEIVE_IKE_PACKET: + ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; + IkeHeader ikeHeader = receivedIkePacket.ikeHeader; + + // Request received on the new/surviving SA; treat it as acknowledgement that + // remote has successfully rekeyed. + if (mIkeSaRecordSurviving == getIkeSaRecordForPacket(ikeHeader)) { + deferMessage(message); + // TODO: Locally close old (and losing) IKE SAs. + finishRekey(); + } else { + handleReceivedIkePacket(message); + } + return HANDLED; + default: + return NOT_HANDLED; + // TODO: Add more cases for other packet types. + } + } + + protected void finishRekey() { + mCurrentIkeSaRecord = mIkeSaRecordSurviving; + mLocalInitNewIkeSaRecord = null; + mRemoteInitNewIkeSaRecord = null; + + mIkeSaRecordSurviving = null; + mIkeSaRecordAwaitingLocalDel = null; + mIkeSaRecordAwaitingRemoteDel = null; + } + } + + /** + * SimulRekeyIkeLocalDeleteRemoteDelete represents the deleting stage during simultaneous + * rekeying when IKE library is waiting for both a Delete request and a Delete response. + */ + class SimulRekeyIkeLocalDeleteRemoteDelete extends RekeyIkeDeleteBase { + @Override + public void enter() { + // Detemine surviving IKE SA. According to RFC 7296: "The new IKE SA containing the + // lowest nonce SHOULD be deleted by the node that created it, and the other surviving + // new IKE SA MUST inherit all the Child SAs." + if (mLocalInitNewIkeSaRecord.compareTo(mRemoteInitNewIkeSaRecord) > 0) { + mIkeSaRecordSurviving = mLocalInitNewIkeSaRecord; + mIkeSaRecordAwaitingLocalDel = mCurrentIkeSaRecord; + mIkeSaRecordAwaitingRemoteDel = mRemoteInitNewIkeSaRecord; + } else { + mIkeSaRecordSurviving = mRemoteInitNewIkeSaRecord; + mIkeSaRecordAwaitingLocalDel = mLocalInitNewIkeSaRecord; + mIkeSaRecordAwaitingRemoteDel = mCurrentIkeSaRecord; + } + IkeMessage ikeMessage = buildIkeDeleteReq(mIkeSaRecordAwaitingLocalDel); + // TODO: Encode and send out delete request and start retransmission timer. + // TODO: Set timer awaiting for delete request. + } + + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + IkeSaRecord ikeSaRecordForPacket = getIkeSaRecordForPacket(ikeMessage.ikeHeader); + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_DELETE_IKE_REQ: + if (ikeSaRecordForPacket == mIkeSaRecordAwaitingRemoteDel) { + try { + validateIkeDeleteReq(ikeMessage); + IkeMessage respMsg = buildIkeDeleteResp(mIkeSaRecordAwaitingRemoteDel); + removeIkeSaRecord(mIkeSaRecordAwaitingRemoteDel); + // TODO: Encode and send response and close + // mIkeSaRecordAwaitingRemoteDel. + // TODO: Stop timer awating delete request. + transitionTo(mSimulRekeyIkeLocalDelete); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + } else { + // TODO: The other side deletes wrong IKE SA and we should close whole IKE + // session. + } + return; + case IkeMessage.MESSAGE_TYPE_DELETE_IKE_RESP: + if (ikeSaRecordForPacket == mIkeSaRecordAwaitingLocalDel) { + try { + validateIkeDeleteResp(ikeMessage); + transitionTo(mSimulRekeyIkeRemoteDelete); + removeIkeSaRecord(mIkeSaRecordAwaitingLocalDel); + // TODO: Close mIkeSaRecordAwaitingLocalDel + // TODO: Stop retransmission timer + } catch (IkeException e) { + // TODO: Handle processing errors. + } + } else { + // TODO: Close whole IKE session + } + return; + default: + // TODO: Add more cases for other packet types. + } + } + + @Override + public void exit() { + finishRekey(); + // TODO: Stop retransmission timer and awaiting delete request timer. + } + } + + /** + * SimulRekeyIkeLocalDelete represents the state when IKE library is waiting for a Delete + * response during simultaneous rekeying. + */ + class SimulRekeyIkeLocalDelete extends RekeyIkeDeleteBase { + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_DELETE_IKE_RESP: + try { + validateIkeDeleteResp(ikeMessage); + removeIkeSaRecord(mIkeSaRecordAwaitingLocalDel); + // TODO: Close mIkeSaRecordAwaitingLocalDel. + transitionTo(mIdle); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + default: + // TODO: Add more cases for other packet types. + } + } + } + + /** + * SimulRekeyIkeRemoteDelete represents the state that waiting for a Delete request during + * simultaneous rekeying. + */ + class SimulRekeyIkeRemoteDelete extends RekeyIkeDeleteBase { + // TODO: Implement methods for processing Delete response + @Override + protected void handleIkeMessage(IkeMessage ikeMessage, int messageType, Message message) { + switch (messageType) { + case IkeMessage.MESSAGE_TYPE_DELETE_IKE_REQ: + try { + validateIkeDeleteReq(ikeMessage); + IkeMessage respMsg = buildIkeDeleteResp(mIkeSaRecordAwaitingRemoteDel); + // TODO: Encode and send response and close mIkeSaRecordAwaitingRemoteDel + removeIkeSaRecord(mIkeSaRecordAwaitingRemoteDel); + transitionTo(mIdle); + } catch (IkeException e) { + // TODO: Handle processing errors. + } + return; + default: + // TODO: Add more cases for other packet types. + } + } + } + + /** + * RekeyIkeLocalDelete represents the deleting stage when IKE library is initiating a Rekey + * procedure. + * + * <p>RekeyIkeLocalDelete and SimulRekeyIkeLocalDelete have same behaviours in processMessage(). + * While RekeyIkeLocalDelete overrides enter() and exit() methods for initiating and finishing + * the deleting stage for IKE rekeying. + */ + class RekeyIkeLocalDelete extends SimulRekeyIkeLocalDelete { + @Override + public void enter() { + mIkeSaRecordSurviving = mLocalInitNewIkeSaRecord; + mIkeSaRecordAwaitingLocalDel = mCurrentIkeSaRecord; + IkeMessage ikeMessage = buildIkeDeleteReq(mIkeSaRecordAwaitingLocalDel); + // TODO: Encode ikeMessage, send out packet and start retransmission timer. + } + + @Override + public void exit() { + finishRekey(); + // TODO: Stop retransmission. + } + } + + /** + * RekeyIkeRemoteDelete represents the deleting stage when responding to a Rekey procedure. + * + * <p>RekeyIkeRemoteDelete and SimulRekeyIkeRemoteDelete have same behaviours in + * processMessage(). While RekeyIkeLocalDelete overrides enter() and exit() methods for waiting + * incoming delete request and for finishing the deleting stage for IKE rekeying. + */ + class RekeyIkeRemoteDelete extends SimulRekeyIkeRemoteDelete { + @Override + public void enter() { + mIkeSaRecordSurviving = mRemoteInitNewIkeSaRecord; + mIkeSaRecordAwaitingRemoteDel = mCurrentIkeSaRecord; + // TODO: Set timer awaiting delete request. + } + + @Override + public void exit() { + finishRekey(); + // TODO: Stop timer awaiting delete request. + } + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java b/src/java/com/android/ike/ikev2/IkeSocket.java index 2f853c7f..e235ab88 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java +++ b/src/java/com/android/ike/ikev2/IkeSocket.java @@ -14,23 +14,23 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike; +package com.android.ike.ikev2; -import static android.net.ipsec.ike.IkeManager.getIkeLog; import static android.system.OsConstants.F_SETFL; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.net.util.PacketReader; import android.os.Handler; import android.system.ErrnoException; import android.system.Os; +import android.util.Log; import android.util.LongSparseArray; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.message.IkeHeader; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.message.IkeHeader; -import com.android.internal.net.ipsec.ike.utils.PacketReader; import java.io.FileDescriptor; import java.io.IOException; @@ -38,9 +38,7 @@ import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; /** * IkeSocket sends and receives IKE packets via the user provided {@link UdpEncapsulationSocket}. @@ -82,16 +80,18 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { // Package private map from locally generated IKE SPI to IkeSessionStateMachine instances. @VisibleForTesting - final LongSparseArray<IkeSessionStateMachine> mSpiToIkeSession = new LongSparseArray<>(); - - // Package private set to store all running IKE Sessions that are using this IkeSocket instance. - @VisibleForTesting final Set<IkeSessionStateMachine> mAliveIkeSessions = new HashSet<>(); - + final LongSparseArray<IkeSessionStateMachine> mSpiToIkeSession = + new LongSparseArray<>(); // UdpEncapsulationSocket for sending and receving IKE packet. private final UdpEncapsulationSocket mUdpEncapSocket; + /** Package private */ + @VisibleForTesting + int mRefCount; + private IkeSocket(UdpEncapsulationSocket udpEncapSocket, Handler handler) { super(handler); + mRefCount = 1; mUdpEncapSocket = udpEncapSocket; } @@ -102,31 +102,27 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { * udpEncapSocket. Otherwise, create and return a new IkeSocket instance. * * @param udpEncapSocket user provided UdpEncapsulationSocket - * @param ikeSession the IkeSessionStateMachine that is requesting an IkeSocket. - * @return an IkeSocket instance + * @return an IkSocket instance */ - public static IkeSocket getIkeSocket( - UdpEncapsulationSocket udpEncapSocket, IkeSessionStateMachine ikeSession) + public static IkeSocket getIkeSocket(UdpEncapsulationSocket udpEncapSocket) throws ErrnoException { FileDescriptor fd = udpEncapSocket.getFileDescriptor(); // All created IkeSocket has modified its FileDescriptor to non-blocking type for handling // read events in a non-blocking way. Os.fcntlInt(fd, F_SETFL, SOCK_DGRAM | SOCK_NONBLOCK); - IkeSocket ikeSocket = null; if (sFdToIkeSocketMap.containsKey(udpEncapSocket)) { - ikeSocket = sFdToIkeSocketMap.get(udpEncapSocket); - + IkeSocket ikeSocket = sFdToIkeSocketMap.get(udpEncapSocket); + ikeSocket.mRefCount++; + return ikeSocket; } else { - ikeSocket = new IkeSocket(udpEncapSocket, new Handler()); + IkeSocket ikeSocket = new IkeSocket(udpEncapSocket, new Handler()); // Create and register FileDescriptor for receiving IKE packet on current thread. ikeSocket.start(); sFdToIkeSocketMap.put(udpEncapSocket, ikeSocket); + return ikeSocket; } - - ikeSocket.mAliveIkeSessions.add(ikeSession); - return ikeSocket; } /** @@ -155,6 +151,10 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { static final class PacketReceiver implements IPacketReceiver { public void handlePacket( byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession) { + // TODO: b/129708574 Consider only logging the error some % of the time it happens, or + // only logging the error the first time it happens and then keep a count to prevent + // logspam. + ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf); // Check the existence of the Non-ESP Marker. A received packet can be either an IKE @@ -164,7 +164,7 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { byteBuffer.get(espMarker); if (!Arrays.equals(NON_ESP_MARKER, espMarker)) { // Drop the received ESP packet. - getIkeLog().e(TAG, "Receive an ESP packet."); + Log.e(TAG, "Receive an ESP packet."); return; } @@ -173,11 +173,6 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { // IKE SPI. byte[] ikePacketBytes = new byte[byteBuffer.remaining()]; byteBuffer.get(ikePacketBytes); - - // TODO: Retrieve and log the source address - getIkeLog().d(TAG, "Receive packet of " + ikePacketBytes.length + " bytes)"); - getIkeLog().d(TAG, getIkeLog().pii(ikePacketBytes)); - IkeHeader ikeHeader = new IkeHeader(ikePacketBytes); long localGeneratedSpi = @@ -187,14 +182,14 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { IkeSessionStateMachine ikeStateMachine = spiToIkeSession.get(localGeneratedSpi); if (ikeStateMachine == null) { - getIkeLog().w(TAG, "Unrecognized IKE SPI."); + Log.e(TAG, "Unrecognized IKE SPI."); // TODO: Handle invalid IKE SPI error } else { ikeStateMachine.receiveIkePacket(ikeHeader, ikePacketBytes); } - } catch (IkeProtocolException e) { + } catch (IkeException e) { // Handle invalid IKE header - getIkeLog().i(TAG, "Can't parse malformed IKE packet header."); + Log.e(TAG, "Can't parse malformed IKE packet header."); } } } @@ -221,14 +216,6 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { * @param serverAddress IP address of remote server */ public void sendIkePacket(byte[] ikePacket, InetAddress serverAddress) { - getIkeLog() - .d( - TAG, - "Send packet to " - + serverAddress.getHostAddress() - + "( " - + ikePacket.length - + " bytes)"); try { ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length); @@ -265,9 +252,9 @@ public final class IkeSocket extends PacketReader implements AutoCloseable { } /** Release reference of current IkeSocket when the IKE session is closed. */ - public void releaseReference(IkeSessionStateMachine ikeSession) { - mAliveIkeSessions.remove(ikeSession); - if (mAliveIkeSessions.isEmpty()) close(); + public void releaseReference() { + mRefCount--; + if (mRefCount == 0) close(); } /** Implement {@link AutoCloseable#close()} */ diff --git a/src/java/android/net/ipsec/ike/IkeTrafficSelector.java b/src/java/com/android/ike/ikev2/IkeTrafficSelector.java index 65154b9d..baebe647 100644 --- a/src/java/android/net/ipsec/ike/IkeTrafficSelector.java +++ b/src/java/com/android/ike/ikev2/IkeTrafficSelector.java @@ -14,23 +14,21 @@ * limitations under the License. */ -package android.net.ipsec.ike; +package com.android.ike.ikev2; import android.annotation.IntDef; import android.util.ArraySet; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; -import java.util.Objects; /** * IkeTrafficSelector represents a Traffic Selector of a Child SA. @@ -45,14 +43,14 @@ public final class IkeTrafficSelector { // IpProtocolId consists of standard IP Protocol IDs. @Retention(RetentionPolicy.SOURCE) - @IntDef({IP_PROTOCOL_ID_UNSPEC, IP_PROTOCOL_ID_ICMP, IP_PROTOCOL_ID_TCP, IP_PROTOCOL_ID_UDP}) + @IntDef({IP_PROTOCOL_ID_UNSPEC, IP_PROTOCOL_ID_ICMP, IP_PROTOCOL_ID_TCP, IP_PROTOCOL_ID_UCP}) public @interface IpProtocolId {} // Zero value is re-defined by IKE to indicate that all IP protocols are acceptable. @VisibleForTesting static final int IP_PROTOCOL_ID_UNSPEC = 0; @VisibleForTesting static final int IP_PROTOCOL_ID_ICMP = 1; @VisibleForTesting static final int IP_PROTOCOL_ID_TCP = 6; - @VisibleForTesting static final int IP_PROTOCOL_ID_UDP = 17; + @VisibleForTesting static final int IP_PROTOCOL_ID_UCP = 17; private static final ArraySet<Integer> IP_PROTOCOL_ID_SET = new ArraySet<>(); @@ -60,7 +58,7 @@ public final class IkeTrafficSelector { IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_UNSPEC); IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_ICMP); IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_TCP); - IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_UDP); + IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_UCP); } /** @@ -77,9 +75,6 @@ public final class IkeTrafficSelector { public static final int TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE = 7; public static final int TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE = 8; - public static final int PORT_NUMBER_MIN = 0; - public static final int PORT_NUMBER_MAX = 65535; - // TODO: Consider defining these constants in a central place in Connectivity. private static final int IPV4_ADDR_LEN = 4; private static final int IPV6_ADDR_LEN = 16; @@ -112,57 +107,7 @@ public final class IkeTrafficSelector { this.endingAddress = endingAddress; } - /** - * Construct an instance of IkeTrafficSelector for building an outbound IKE message. - * - * @param tsType the Traffic Selector type. - * @param startPort the smallest port number allowed by this Traffic Selector. - * @param endPort the largest port number allowed by this Traffic Selector. - * @param startingAddress the smallest address included in this Traffic Selector. - * @param endingAddress the largest address included in this Traffic Selector. - */ - public IkeTrafficSelector( - @TrafficSelectorType int tsType, - int startPort, - int endPort, - InetAddress startingAddress, - InetAddress endingAddress) { - - this.tsType = tsType; - this.ipProtocolId = IP_PROTOCOL_ID_UNSPEC; - - switch (tsType) { - case TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: - this.selectorLength = TRAFFIC_SELECTOR_IPV4_LEN; - - if (!(startingAddress instanceof Inet4Address) - || !(endingAddress instanceof Inet4Address)) { - throw new IllegalArgumentException( - "Invalid address range: TS_IPV4_ADDR_RANGE requires IPv4 addresses."); - } - - break; - case TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: - throw new UnsupportedOperationException("Do not support IPv6 Traffic Selector."); - // TODO: Support IPv6 Traffic Selector. - default: - throw new IllegalArgumentException("Unrecognized Traffic Selector type."); - } - - if (compareInetAddressTo(startingAddress, endingAddress) > 0) { - throw new IllegalArgumentException("Received invalid address range."); - } - - if (!isPortRangeValid(startPort, endPort)) { - throw new IllegalArgumentException( - "Invalid port range. startPort: " + startPort + " endPort: " + endPort); - } - - this.startPort = startPort; - this.endPort = endPort; - this.startingAddress = startingAddress; - this.endingAddress = endingAddress; - } + // TODO: Add a constructor for users to construct IkeTrafficSelector. /** * Decode IkeTrafficSelectors from inbound Traffic Selector Payload. @@ -226,12 +171,8 @@ public final class IkeTrafficSelector { // Decode and validate ports int startPort = Short.toUnsignedInt(inputBuffer.getShort()); int endPort = Short.toUnsignedInt(inputBuffer.getShort()); - if (!isPortRangeValid(startPort, endPort)) { - throw new InvalidSyntaxException( - "Received invalid port range. startPort: " - + startPort - + " endPort: " - + endPort); + if (startPort > endPort) { + throw new InvalidSyntaxException("Received invalid port range."); } // Decode and validate IPv4 addresses @@ -245,7 +186,7 @@ public final class IkeTrafficSelector { Inet4Address endAddress = (Inet4Address) (Inet4Address.getByAddress(endAddressBytes)); // Validate address range. - if (compareInetAddressTo(startAddress, endAddress) > 0) { + if (!isInetAddressRangeValid(startAddress, endAddress)) { throw new InvalidSyntaxException("Received invalid IPv4 address range."); } @@ -264,110 +205,27 @@ public final class IkeTrafficSelector { // TODO: Add a method for decoding IPv6 traffic selector. - // Validate port range. - private static boolean isPortRangeValid(int startPort, int endPort) { - return (startPort >= PORT_NUMBER_MIN - && startPort <= PORT_NUMBER_MAX - && endPort >= PORT_NUMBER_MIN - && endPort <= PORT_NUMBER_MAX - && startPort <= endPort); - } - - // Compare two InetAddresses. Return -1 if the first input is smaller; 1 if the second input is - // smaller; 0 if two addresses are equal. - // TODO: Consider moving it to the platform code in the future./ - private static int compareInetAddressTo(InetAddress leftAddress, InetAddress rightAddress) { - byte[] leftAddrBytes = leftAddress.getAddress(); - byte[] rightAddrBytes = rightAddress.getAddress(); + // Validate address range. Caller must ensure two address are same types. + // TODO: Consider moving it to the platform code in the future. + private static boolean isInetAddressRangeValid( + InetAddress startAddress, InetAddress endAddress) { + byte[] startAddrBytes = startAddress.getAddress(); + byte[] endAddrBytes = endAddress.getAddress(); - if (leftAddrBytes.length != rightAddrBytes.length) { + if (startAddrBytes.length != endAddrBytes.length) { throw new IllegalArgumentException("Two addresses are different types."); } - for (int i = 0; i < leftAddrBytes.length; i++) { - int unsignedByteLeft = Byte.toUnsignedInt(leftAddrBytes[i]); - int unsignedByteRight = Byte.toUnsignedInt(rightAddrBytes[i]); + for (int i = 0; i < startAddrBytes.length; i++) { + int unsignedByteStart = Byte.toUnsignedInt(startAddrBytes[i]); + int unsignedByteEnd = Byte.toUnsignedInt(endAddrBytes[i]); - int result = Integer.compare(unsignedByteLeft, unsignedByteRight); - if (result != 0) return result; - } - return 0; - } - - /** - * Check if the input IkeTrafficSelector is a subset of this instance. - * - * @param ts the provided IkeTrafficSelector to check. - * @return true if the input IkeTrafficSelector is a subset of this instance, otherwise false. - */ - public boolean contains(IkeTrafficSelector ts) { - if (tsType == ts.tsType - && ipProtocolId == ts.ipProtocolId - && startPort <= ts.startPort - && endPort >= ts.endPort - && compareInetAddressTo(startingAddress, ts.startingAddress) <= 0 - && compareInetAddressTo(endingAddress, ts.endingAddress) >= 0) { - return true; - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash( - tsType, - ipProtocolId, - selectorLength, - startPort, - endPort, - startingAddress, - endingAddress); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IkeTrafficSelector)) return false; - - IkeTrafficSelector other = (IkeTrafficSelector) o; - - if (tsType != other.tsType - || ipProtocolId != other.ipProtocolId - || startPort != other.startPort - || endPort != other.endPort) { - return false; - } - - switch (tsType) { - case TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: - return (((Inet4Address) startingAddress) - .equals((Inet4Address) other.startingAddress) - && ((Inet4Address) endingAddress) - .equals((Inet4Address) other.endingAddress)); - case TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: - return (((Inet6Address) startingAddress) - .equals((Inet6Address) other.startingAddress) - && ((Inet6Address) endingAddress) - .equals((Inet6Address) other.endingAddress)); - default: - throw new UnsupportedOperationException("Unrecognized TS type"); + if (unsignedByteStart < unsignedByteEnd) { + return true; + } else if (unsignedByteStart > unsignedByteEnd) { + return false; + } } - } - - /** - * Encode traffic selector to ByteBuffer. - * - * <p>This method will be only called by IkeTsPayload for building an outbound IKE message. - * - * @param byteBuffer destination ByteBuffer that stores encoded traffic selector. - */ - public void encodeToByteBuffer(ByteBuffer byteBuffer) { - byteBuffer - .put((byte) tsType) - .put((byte) ipProtocolId) - .putShort((short) selectorLength) - .putShort((short) startPort) - .putShort((short) endPort) - .put(startingAddress.getAddress()) - .put(endingAddress.getAddress()); + return true; } } diff --git a/src/java/com/android/ike/ikev2/SaProposal.java b/src/java/com/android/ike/ikev2/SaProposal.java new file mode 100644 index 00000000..53206cc9 --- /dev/null +++ b/src/java/com/android/ike/ikev2/SaProposal.java @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2; + +import android.annotation.IntDef; +import android.util.ArraySet; + +import com.android.ike.ikev2.message.IkePayload; +import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform; +import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform; +import com.android.ike.ikev2.message.IkeSaPayload.EsnTransform; +import com.android.ike.ikev2.message.IkeSaPayload.IntegrityTransform; +import com.android.ike.ikev2.message.IkeSaPayload.PrfTransform; +import com.android.ike.ikev2.message.IkeSaPayload.Transform; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** + * SaProposal represents a user configured set contains cryptograhic algorithms and key generating + * materials for negotiating an IKE or Child SA. + * + * <p>User must provide at least a valid SaProposal when they are creating a new IKE SA or Child SA. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public final class SaProposal { + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ENCRYPTION_ALGORITHM_3DES, + ENCRYPTION_ALGORITHM_AES_CBC, + ENCRYPTION_ALGORITHM_AES_GCM_8, + ENCRYPTION_ALGORITHM_AES_GCM_12, + ENCRYPTION_ALGORITHM_AES_GCM_16 + }) + public @interface EncryptionAlgorithm {} + + public static final int ENCRYPTION_ALGORITHM_3DES = 3; + public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; + public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; + public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; + public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; + + private static final Set<Integer> SUPPORTED_ENCRYPTION_ALGORITHM; + + static { + SUPPORTED_ENCRYPTION_ALGORITHM = new ArraySet<>(); + SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_3DES); + SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_CBC); + SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_GCM_8); + SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_GCM_12); + SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_GCM_16); + } + + public static final int KEY_LEN_AES_128 = 128; + public static final int KEY_LEN_AES_192 = 192; + public static final int KEY_LEN_AES_256 = 256; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({PSEUDORANDOM_FUNCTION_HMAC_SHA1, PSEUDORANDOM_FUNCTION_AES128_XCBC}) + public @interface PseudorandomFunction {} + + public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; + public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; + + private static final Set<Integer> SUPPORTED_PSEUDORANDOM_FUNCTION; + + static { + SUPPORTED_PSEUDORANDOM_FUNCTION = new ArraySet<>(); + SUPPORTED_PSEUDORANDOM_FUNCTION.add(PSEUDORANDOM_FUNCTION_HMAC_SHA1); + SUPPORTED_PSEUDORANDOM_FUNCTION.add(PSEUDORANDOM_FUNCTION_AES128_XCBC); + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + INTEGRITY_ALGORITHM_NONE, + INTEGRITY_ALGORITHM_HMAC_SHA1_96, + INTEGRITY_ALGORITHM_AES_XCBC_96, + INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, + INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 + }) + public @interface IntegrityAlgorithm {} + + public static final int INTEGRITY_ALGORITHM_NONE = 0; + public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; + public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; + public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; + public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; + public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; + + private static final Set<Integer> SUPPORTED_INTEGRITY_ALGORITHM; + + static { + SUPPORTED_INTEGRITY_ALGORITHM = new ArraySet<>(); + SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_NONE); + SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA1_96); + SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_AES_XCBC_96); + SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); + SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); + SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({DH_GROUP_NONE, DH_GROUP_1024_BIT_MODP, DH_GROUP_2048_BIT_MODP}) + public @interface DhGroup {} + + public static final int DH_GROUP_NONE = 0; + public static final int DH_GROUP_1024_BIT_MODP = 2; + public static final int DH_GROUP_2048_BIT_MODP = 14; + + private static final Set<Integer> SUPPORTED_DH_GROUP; + + static { + SUPPORTED_DH_GROUP = new ArraySet<>(); + SUPPORTED_DH_GROUP.add(DH_GROUP_NONE); + SUPPORTED_DH_GROUP.add(DH_GROUP_1024_BIT_MODP); + SUPPORTED_DH_GROUP.add(DH_GROUP_2048_BIT_MODP); + } + + /** Package private */ + @IkePayload.ProtocolId private final int mProtocolId; + /** Package private */ + private final EncryptionTransform[] mEncryptionAlgorithms; + /** Package private */ + private final PrfTransform[] mPseudorandomFunctions; + /** Package private */ + private final IntegrityTransform[] mIntegrityAlgorithms; + /** Package private */ + private final DhGroupTransform[] mDhGroups; + /** Package private */ + private final EsnTransform[] mEsns; + + private SaProposal( + @IkePayload.ProtocolId int protocol, + EncryptionTransform[] encryptionAlgos, + PrfTransform[] prfs, + IntegrityTransform[] integrityAlgos, + DhGroupTransform[] dhGroups) { + mProtocolId = protocol; + mEncryptionAlgorithms = encryptionAlgos; + mPseudorandomFunctions = prfs; + mIntegrityAlgorithms = integrityAlgos; + mDhGroups = dhGroups; + + if (protocol == IkePayload.PROTOCOL_ID_IKE) { + // Do not negotiate ESN for IKE SA proposal + mEsns = new EsnTransform[0]; + } else { + // Do not support negotiating Child SAs using extended sequence numbers. + mEsns = new EsnTransform[] {new EsnTransform()}; + } + } + + /** + * Construct SaProposal from a decoded inbound IKE packet, only called by IkeSaPayload. + * + * @param protocol IP protocol ID + * @param encryptionAlgos encryption algorithms decoded from inbound IKE packet. + * @param prfs pseudorandom functions decoded from inbound IKE packet. + * @param integrityAlgos integrity algorithms decoded from inbound IKE packet. + * @param dhGroups Dh groups decoded from inbound IKE packet. + * @param esns ESN policies decoded from IKE packet. + */ + public SaProposal( + @IkePayload.ProtocolId int protocol, + EncryptionTransform[] encryptionAlgos, + PrfTransform[] prfs, + IntegrityTransform[] integrityAlgos, + DhGroupTransform[] dhGroups, + EsnTransform[] esns) { + mProtocolId = protocol; + mEncryptionAlgorithms = encryptionAlgos; + mPseudorandomFunctions = prfs; + mIntegrityAlgorithms = integrityAlgos; + mDhGroups = dhGroups; + mEsns = esns; + } + + /** + * Check if the current SaProposal from the SA responder is consistent with the selected + * reqProposal from the SA initiator. + * + * @param reqProposal selected SaProposal from SA initiator + * @return if current SaProposal from SA responder is consistent with the selected reqProposal + * from SA initiator. + */ + public boolean isNegotiatedFrom(SaProposal reqProposal) { + return isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms) + && isTransformSelectedFrom( + mPseudorandomFunctions, reqProposal.mPseudorandomFunctions) + && isTransformSelectedFrom(mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms) + && isTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups) + && isTransformSelectedFrom(mEsns, reqProposal.mEsns); + } + + /** Package private */ + static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) { + // If the selected proposal has multiple transforms with the same type, the responder MUST + // choose a single one. + if ((selected.length > 1) || (selected.length == 0) != (selectFrom.length == 0)) { + return false; + } + + if (selected.length == 0) return true; + + return Arrays.asList(selectFrom).contains(selected[0]); + } + + /*Package private*/ + @IkePayload.ProtocolId + int getProtocolId() { + return mProtocolId; + } + + /*Package private*/ + EncryptionTransform[] getEncryptionTransforms() { + return mEncryptionAlgorithms; + } + + /*Package private*/ + PrfTransform[] getPrfTransforms() { + return mPseudorandomFunctions; + } + + /*Package private*/ + IntegrityTransform[] getIntegrityTransforms() { + return mIntegrityAlgorithms; + } + + /*Package private*/ + DhGroupTransform[] getDhGroupTransforms() { + return mDhGroups; + } + + /*Package private*/ + EsnTransform[] getEsnTransforms() { + return mEsns; + } + + /** + * Return all SA Transforms in this SaProposal to be encoded for building an outbound IKE + * message. + * + * <p>This method can be called by only IKE library. + * + * @return Array of Transforms to be encoded. + */ + public Transform[] getAllTransforms() { + int encodedNumTransforms = + mEncryptionAlgorithms.length + + mPseudorandomFunctions.length + + mIntegrityAlgorithms.length + + mDhGroups.length + + mEsns.length; + + List<Transform> transformList = new ArrayList<Transform>(encodedNumTransforms); + transformList.addAll(Arrays.asList(mEncryptionAlgorithms)); + transformList.addAll(Arrays.asList(mPseudorandomFunctions)); + transformList.addAll(Arrays.asList(mIntegrityAlgorithms)); + transformList.addAll(Arrays.asList(mDhGroups)); + transformList.addAll(Arrays.asList(mEsns)); + + return transformList.toArray(new Transform[encodedNumTransforms]); + } + + /** + * This class can be used to incrementally construct a SaProposal. SaProposal instances are + * immutable once built. + * + * <p>TODO: Support users to add algorithms from most preferred to least preferred. + */ + public static final class Builder { + private static final String ERROR_TAG = "Invalid SA Proposal: "; + + /** Indicate if Builder is for building IKE SA proposal or Child SA proposal. */ + private final boolean mIsIkeProposal; + /** + * Indicate if Builder is for building first Child SA proposal or addtional Child SA + * proposal. Only valid if mIsIkeProposal is false. + */ + private final boolean mIsFirstChild; + + // Use set to avoid adding repeated algorithms. + private final Set<EncryptionTransform> mProposedEncryptAlgos = new ArraySet<>(); + private final Set<PrfTransform> mProposedPrfs = new ArraySet<>(); + private final Set<IntegrityTransform> mProposedIntegrityAlgos = new ArraySet<>(); + private final Set<DhGroupTransform> mProposedDhGroups = new ArraySet<>(); + + private boolean mHasAead = false; + + private Builder(boolean isIke, boolean isFirstChild) { + mIsIkeProposal = isIke; + mIsFirstChild = isFirstChild; + } + + private static boolean isAead(@EncryptionAlgorithm int algorithm) { + switch (algorithm) { + case ENCRYPTION_ALGORITHM_3DES: + // Fall through + case ENCRYPTION_ALGORITHM_AES_CBC: + return false; + case ENCRYPTION_ALGORITHM_AES_GCM_8: + // Fall through + case ENCRYPTION_ALGORITHM_AES_GCM_12: + // Fall through + case ENCRYPTION_ALGORITHM_AES_GCM_16: + return true; + default: + // Won't hit here. + throw new IllegalArgumentException("Unsupported Encryption Algorithm."); + } + } + + private EncryptionTransform[] buildEncryptAlgosOrThrow() { + if (mProposedEncryptAlgos.isEmpty()) { + throw new IllegalArgumentException( + ERROR_TAG + "Encryption algorithm must be proposed."); + } + + return mProposedEncryptAlgos.toArray( + new EncryptionTransform[mProposedEncryptAlgos.size()]); + } + + private PrfTransform[] buildPrfsOrThrow() { + if (mIsIkeProposal == mProposedPrfs.isEmpty()) { + throw new IllegalArgumentException( + ERROR_TAG + "Invalid PRF configuration for this SA Proposal."); + } + + return mProposedPrfs.toArray(new PrfTransform[mProposedPrfs.size()]); + } + + private IntegrityTransform[] buildIntegAlgosForIkeOrThrow() { + // When building IKE SA Proposal with normal-mode ciphers, mProposedIntegrityAlgos must + // not be empty and must not have INTEGRITY_ALGORITHM_NONE. When building IKE SA + // Proposal with combined-mode ciphers, mProposedIntegrityAlgos must be either empty or + // only have INTEGRITY_ALGORITHM_NONE. + if (mProposedIntegrityAlgos.isEmpty() && !mHasAead) { + throw new IllegalArgumentException( + ERROR_TAG + + "Integrity algorithm " + + "must be proposed with normal ciphers in IKE proposal."); + } + + for (IntegrityTransform transform : mProposedIntegrityAlgos) { + if ((transform.id == INTEGRITY_ALGORITHM_NONE) != mHasAead) { + throw new IllegalArgumentException( + ERROR_TAG + + "Invalid integrity algorithm configuration" + + " for this SA Proposal"); + } + } + + return mProposedIntegrityAlgos.toArray( + new IntegrityTransform[mProposedIntegrityAlgos.size()]); + } + + private IntegrityTransform[] buildIntegAlgosForChildOrThrow() { + // When building Child SA Proposal with normal-mode ciphers, there is no contraint on + // integrity algorithm. When building Child SA Proposal with combined-mode ciphers, + // mProposedIntegrityAlgos must be either empty or only have INTEGRITY_ALGORITHM_NONE. + for (IntegrityTransform transform : mProposedIntegrityAlgos) { + if (transform.id != INTEGRITY_ALGORITHM_NONE && mHasAead) { + throw new IllegalArgumentException( + ERROR_TAG + + "Only INTEGRITY_ALGORITHM_NONE can be" + + " proposed with combined-mode ciphers in any proposal."); + } + } + + return mProposedIntegrityAlgos.toArray( + new IntegrityTransform[mProposedIntegrityAlgos.size()]); + } + + private DhGroupTransform[] buildDhGroupsForIkeOrThrow() { + if (mProposedDhGroups.isEmpty()) { + throw new IllegalArgumentException( + ERROR_TAG + "DH group must be proposed in IKE SA proposal."); + } + + for (DhGroupTransform transform : mProposedDhGroups) { + if (transform.id == DH_GROUP_NONE) { + throw new IllegalArgumentException( + ERROR_TAG + + "None-value DH group must not" + + " be proposed in IKE SA proposal"); + } + } + + return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]); + } + + private DhGroupTransform[] buildDhGroupsForChildOrThrow() { + for (DhGroupTransform transform : mProposedDhGroups) { + if (transform.id != DH_GROUP_NONE && mIsFirstChild) { + throw new IllegalArgumentException( + ERROR_TAG + + "Only DH_GROUP_NONE can be" + + " proposed in first Child SA proposal."); + } + } + return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]); + } + + /** Returns a new Builder for a IKE SA Proposal. */ + public static Builder newIkeSaProposalBuilder() { + return new Builder(true, false); + } + + /** + * Returns a new Builder for a Child SA Proposal. + * + * @param isFirstChildSaProposal indicates if this SA proposal for first Child SA. + * @return Builder for a Child SA Proposal. + */ + public static Builder newChildSaProposalBuilder(boolean isFirstChildSaProposal) { + return new Builder(false, isFirstChildSaProposal); + } + + /** + * Adds an encryption algorithm to SA proposal being built. + * + * @param algorithm encryption algorithm to add to SaProposal. + * @return Builder of SaProposal. + */ + public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) { + // Construct EncryptionTransform and validate proposed algorithm during + // construction. + EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm); + + validateOnlyOneModeEncryptAlgoProposedOrThrow(algorithm); + + mProposedEncryptAlgos.add(encryptionTransform); + return this; + } + + /** + * Adds an encryption algorithm with specific key length to SA proposal being built. + * + * @param algorithm encryption algorithm to add to SaProposal. + * @param keyLength key length of algorithm. + * @return Builder of SaProposal. + * @throws IllegalArgumentException if AEAD and non-combined mode algorithms are mixed. + */ + public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) { + // Construct EncryptionTransform and validate proposed algorithm during + // construction. + EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength); + + validateOnlyOneModeEncryptAlgoProposedOrThrow(algorithm); + + mProposedEncryptAlgos.add(encryptionTransform); + return this; + } + + private void validateOnlyOneModeEncryptAlgoProposedOrThrow( + @EncryptionAlgorithm int algorithm) { + boolean isCurrentAead = isAead(algorithm); + + if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) { + throw new IllegalArgumentException( + ERROR_TAG + + "Proposal cannot has both normal ciphers " + + "and combined-mode ciphers."); + } + + if (isCurrentAead) mHasAead = true; + } + + /** + * Adds a pseudorandom function to SA proposal being built. + * + * @param algorithm pseudorandom function to add to SaProposal. + * @return Builder of SaProposal. + */ + public Builder addPseudorandomFunction(@PseudorandomFunction int algorithm) { + // Construct PrfTransform and validate proposed algorithm during + // construction. + mProposedPrfs.add(new PrfTransform(algorithm)); + return this; + } + + /** + * Adds an integrity algorithm to SA proposal being built. + * + * @param algorithm integrity algorithm to add to SaProposal. + * @return Builder of SaProposal. + */ + public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) { + // Construct IntegrityTransform and validate proposed algorithm during + // construction. + mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm)); + return this; + } + + /** + * Adds a Diffie-Hellman Group to SA proposal being built. + * + * @param dhGroup to add to SaProposal. + * @return Builder of SaProposal. + */ + public Builder addDhGroup(@DhGroup int dhGroup) { + // Construct DhGroupTransform and validate proposed dhGroup during + // construction. + mProposedDhGroups.add(new DhGroupTransform(dhGroup)); + return this; + } + + /** + * Validates, builds and returns the SaProposal + * + * @return SaProposal the validated SaProposal. + * @throws IllegalArgumentException if SaProposal is invalid. + */ + public SaProposal build() { + EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow(); + PrfTransform[] prfTransforms = buildPrfsOrThrow(); + IntegrityTransform[] integrityTransforms = + mIsIkeProposal + ? buildIntegAlgosForIkeOrThrow() + : buildIntegAlgosForChildOrThrow(); + + DhGroupTransform[] dhGroupTransforms = + mIsIkeProposal ? buildDhGroupsForIkeOrThrow() : buildDhGroupsForChildOrThrow(); + // IKE library only supports negotiating ESP Child SA. + int protocol = mIsIkeProposal ? IkePayload.PROTOCOL_ID_IKE : IkePayload.PROTOCOL_ID_ESP; + + return new SaProposal( + protocol, + encryptionTransforms, + prfTransforms, + integrityTransforms, + dhGroupTransforms); + } + } + + /** + * Check if the provided algorithm is a supported encryption algorithm. + * + * @param algorithm IKE standard encryption algorithm id. + * @return true if the provided algorithm is a supported encryption algorithm. + */ + public static boolean isSupportedEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) { + return SUPPORTED_ENCRYPTION_ALGORITHM.contains(algorithm); + } + + /** + * Check if the provided algorithm is a supported pseudorandom function. + * + * @param algorithm IKE standard pseudorandom function id. + * @return true if the provided algorithm is a supported pseudorandom function. + */ + public static boolean isSupportedPseudorandomFunction(@PseudorandomFunction int algorithm) { + return SUPPORTED_PSEUDORANDOM_FUNCTION.contains(algorithm); + } + + /** + * Check if the provided algorithm is a supported integrity algorithm. + * + * @param algorithm IKE standard integrity algorithm id. + * @return true if the provided algorithm is a supported integrity algorithm. + */ + public static boolean isSupportedIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) { + return SUPPORTED_INTEGRITY_ALGORITHM.contains(algorithm); + } + + /** + * Check if the provided group number is for a supported Diffie-Hellman Group. + * + * @param dhGroup IKE standard DH Group id. + * @return true if the provided number is for a supported Diffie-Hellman Group. + */ + public static boolean isSupportedDhGroup(@DhGroup int dhGroup) { + return SUPPORTED_DH_GROUP.contains(dhGroup); + } +} diff --git a/src/java/com/android/ike/ikev2/SaRecord.java b/src/java/com/android/ike/ikev2/SaRecord.java new file mode 100644 index 00000000..4dcddec4 --- /dev/null +++ b/src/java/com/android/ike/ikev2/SaRecord.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import com.android.ike.ikev2.message.IkeMessage; +import com.android.ike.ikev2.message.IkePayload; +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/** + * SaRecord represents common information of an IKE SA and a Child SA. + * + * <p>When doing rekey, there can be multiple SAs in the same IkeSessionStateMachine or + * ChildSessionStateMachine, where they use same cryptographic algorithms but with different keys. + * We store cryptographic algorithms and unchanged SA configurations in IkeSessionOptions or + * ChildSessionOptions and store changed information including keys, SPIs, and nonces in SaRecord. + */ +public abstract class SaRecord { + + private static ISaRecordHelper sSaRecordHelper = new SaRecordHelper(); + + public final byte[] nonceInitiator; + public final byte[] nonceResponder; + + /** Package private */ + SaRecord(byte[] nonceInit, byte[] nonceResp) { + nonceInitiator = nonceInit; + nonceResponder = nonceResp; + } + + /** + * SaRecordHelper implements methods for constructing SaRecord. + * + * <p>Package private + */ + static class SaRecordHelper implements ISaRecordHelper { + @Override + public IkeSaRecord makeFirstIkeSaRecord(IkeMessage initRequest, IkeMessage initResponse) { + // TODO: Generate keying materials + return null; + } + + @Override + public IkeSaRecord makeNewIkeSaRecord( + IkeSaRecord oldSaRecord, IkeMessage rekeyRequest, IkeMessage rekeyResponse) { + // TODO: Generate keying materials based on old SK_d + return null; + } + + @Override + public ChildSaRecord makeChildSaRecord( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads) { + // TODO: Calculate keys and build IpSecTransform. + return null; + } + } + + /** Package private */ + static void setSaRecordHelper(ISaRecordHelper helper) { + sSaRecordHelper = helper; + } + + /** IkeSaRecord represents an IKE SA. */ + public static class IkeSaRecord extends SaRecord implements Comparable<IkeSaRecord> { + + /** SPI of IKE SA initiator */ + public final long initiatorSpi; + /** SPI of IKE SA responder */ + public final long responderSpi; + /** Flag indicates if this IKE SA is locally initiated */ + public final boolean isLocalInit; + + /** Package private */ + IkeSaRecord( + long initSpi, long respSpi, boolean localInit, byte[] nonceInit, byte[] nonceResp) { + super(nonceInit, nonceResp); + initiatorSpi = initSpi; + responderSpi = respSpi; + isLocalInit = localInit; + // TODO: Impement constructor. There will be more input parameters. + } + + /** Package private */ + static IkeSaRecord makeFirstIkeSaRecord(IkeMessage initRequest, IkeMessage initResponse) { + return sSaRecordHelper.makeFirstIkeSaRecord(initRequest, initResponse); + } + + /** Package private */ + static IkeSaRecord makeNewIkeSaRecord( + IkeSaRecord oldSaRecord, IkeMessage rekeyRequest, IkeMessage rekeyResponse) { + return sSaRecordHelper.makeNewIkeSaRecord(oldSaRecord, rekeyRequest, rekeyResponse); + } + + /** Package private */ + long getRemoteSpi() { + if (isLocalInit) { + return responderSpi; + } else { + return initiatorSpi; + } + } + + /** + * Compare with a specific IkeSaRecord + * + * @param record IkeSaRecord to be compared. + * @return a negative integer if input IkeSaRecord contains lowest nonce; a positive integer + * if this IkeSaRecord has lowest nonce; return zero if lowest nonces of two + * IkeSaRecords match. + */ + public int compareTo(IkeSaRecord record) { + // TODO: Implement it b/122924815. + return 1; + } + } + + /** ChildSaRecord represents an Child SA. */ + public static class ChildSaRecord extends SaRecord implements Comparable<ChildSaRecord> { + + /** Locally generated SPI for receiving IPsec Packet. */ + public final int inboundSpi; + /** Remotely generated SPI for sending IPsec Packet. */ + public final int outboundSpi; + + /** Package private */ + ChildSaRecord(int inSpi, int outSpi, byte[] nonceInit, byte[] nonceResp) { + super(nonceInit, nonceResp); + inboundSpi = inSpi; + outboundSpi = outSpi; + // TODO: Impement constructor. Will be more input parameters. + } + + /** Package private */ + static ChildSaRecord makeChildSaRecord( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads) { + return sSaRecordHelper.makeChildSaRecord(reqPayloads, respPayloads); + } + + /** + * Compare with a specific ChildSaRecord + * + * @param record ChildSaRecord to be compared. + * @return a negative integer if input ChildSaRecord contains lowest nonce; a positive + * integer if this ChildSaRecord has lowest nonce; return zero if lowest nonces of two + * ChildSaRecord match. + */ + public int compareTo(ChildSaRecord record) { + // TODO: Implement it b/122924815 + return 1; + } + } + + /** + * ISaRecordHelper provides a package private interface for constructing SaRecord. + * + * <p>ISaRecordHelper exists so that the interface is injectable for testing. + */ + interface ISaRecordHelper { + /** + * Construct IkeSaRecord as results of IKE initial exchange. + * + * @param initRequest IKE_INIT request. + * @param initResponse IKE_INIT request. + * @return ikeSaRecord for initial IKE SA. + */ + IkeSaRecord makeFirstIkeSaRecord(IkeMessage initRequest, IkeMessage initResponse); + + /** + * Construct new IkeSaRecord when doing rekey. + * + * @param oldSaRecord old IKE SA + * @param rekeyRequest Rekey IKE request. + * @param rekeyResponse Rekey IKE response. + * @return ikeSaRecord for new IKE SA. + */ + IkeSaRecord makeNewIkeSaRecord( + IkeSaRecord oldSaRecord, IkeMessage rekeyRequest, IkeMessage rekeyResponse); + + /** + * Construct ChildSaRecord and generate IpSecTransform pairs. + * + * @param reqPayloads payload list in request. + * @param respPayloads payload list in response. + * @return new Child SA. + */ + ChildSaRecord makeChildSaRecord( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads); + } + + /** Generate SKEYSEED using negotiated PRF. */ + @VisibleForTesting + static byte[] generateSKeySeed( + String prfAlgorithm, byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey) { + try { + ByteBuffer keyBuffer = ByteBuffer.allocate(nonceInit.length + nonceResp.length); + keyBuffer.put(nonceInit).put(nonceResp); + SecretKeySpec prfKeySpec = new SecretKeySpec(keyBuffer.array(), prfAlgorithm); + + Mac prfMac = Mac.getInstance(prfAlgorithm, IkeMessage.getSecurityProvider()); + prfMac.init(prfKeySpec); + + ByteBuffer sharedKeyBuffer = ByteBuffer.wrap(sharedDhKey); + prfMac.update(sharedKeyBuffer); + + return prfMac.doFinal(); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Failed to generate SKEYSEED", e); + } + } + + /** + * Derives key materials using negotiated PRF. + * + * <p>prf+(K, S) outputs a pseudorandom stream by using negotiated PRF iteratively. In this way + * it can generate long enough keying material containing all the keys for this IKE/Child SA. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296 nternet Key Exchange + * Protocol Version 2 (IKEv2) 2.13. Generating Keying Material </a> + */ + @VisibleForTesting + static byte[] generateKeyMat( + String prfAlgorithm, byte[] prfKey, byte[] dataToSign, int keyMaterialLen) + throws InvalidKeyException { + try { + SecretKeySpec prfKeySpec = new SecretKeySpec(prfKey, prfAlgorithm); + Mac prfMac = Mac.getInstance(prfAlgorithm, IkeMessage.getSecurityProvider()); + + ByteBuffer keyMatBuffer = ByteBuffer.allocate(keyMaterialLen); + + byte[] previousMac = new byte[0]; + final int padLen = 1; + byte padValue = 1; + + while (keyMatBuffer.remaining() > 0) { + prfMac.init(prfKeySpec); + + ByteBuffer dataToSignBuffer = + ByteBuffer.allocate(previousMac.length + dataToSign.length + padLen); + dataToSignBuffer.put(previousMac).put(dataToSign).put(padValue); + dataToSignBuffer.rewind(); + + prfMac.update(dataToSignBuffer); + + previousMac = prfMac.doFinal(); + keyMatBuffer.put( + previousMac, 0, Math.min(previousMac.length, keyMatBuffer.remaining())); + + padValue++; + } + + return keyMatBuffer.array(); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Failed to generate keying material", e); + } + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/AuthenticationFailedException.java b/src/java/com/android/ike/ikev2/exceptions/AuthenticationFailedException.java index e5873648..8a5433b9 100644 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/AuthenticationFailedException.java +++ b/src/java/com/android/ike/ikev2/exceptions/AuthenticationFailedException.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.net.ipsec.ike.exceptions; +package com.android.ike.ikev2.exceptions; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; +import com.android.ike.ikev2.message.IkeNotifyPayload; /** * This exception is thrown when IKE authentication fails. @@ -27,16 +25,14 @@ import android.net.ipsec.ike.exceptions.IkeProtocolException; * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.21.2">RFC 7296, Internet Key Exchange * Protocol Version 2 (IKEv2)</a> */ -public final class AuthenticationFailedException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 0; - +public final class AuthenticationFailedException extends IkeException { /** * Construct a instance of AuthenticationFailedException. * * @param message the detail message. */ public AuthenticationFailedException(String message) { - super(ERROR_TYPE_AUTHENTICATION_FAILED, message); + super(IkeNotifyPayload.NOTIFY_TYPE_AUTHENTICATION_FAILED, message); } /** @@ -45,20 +41,6 @@ public final class AuthenticationFailedException extends IkeProtocolException { * @param cause the cause. */ public AuthenticationFailedException(Throwable cause) { - super(ERROR_TYPE_AUTHENTICATION_FAILED, cause); - } - - /** - * Construct a instance of AuthenticationFailedExcepion from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public AuthenticationFailedException(byte[] notifyData) { - super(ERROR_TYPE_AUTHENTICATION_FAILED, notifyData); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; + super(IkeNotifyPayload.NOTIFY_TYPE_AUTHENTICATION_FAILED, cause); } } diff --git a/src/java/com/android/ike/ikev2/exceptions/IkeException.java b/src/java/com/android/ike/ikev2/exceptions/IkeException.java new file mode 100644 index 00000000..532b6343 --- /dev/null +++ b/src/java/com/android/ike/ikev2/exceptions/IkeException.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.exceptions; + +import com.android.ike.ikev2.message.IkeNotifyPayload; + +/** + * IkeException is an abstract class that represents the common information for all IKE protocol + * errors. + * + * <p>Each types of IKE error should implement its own subclass + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.10.1">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public abstract class IkeException extends Exception { + @IkeNotifyPayload.NotifyType public final int errorCode; + + /** + * Construct an instance of IkeException. + * + * @param code the protocol error code. + */ + public IkeException(@IkeNotifyPayload.NotifyType int code) { + super(); + errorCode = code; + } + + /** + * Construct an instance of IkeException with specified detail message. + * + * @param code the protocol error code. + * @param message the detail message. + */ + public IkeException(@IkeNotifyPayload.NotifyType int code, String message) { + super(message); + errorCode = code; + } + + /** + * Construct an instance of IkeException with specified cause. + * + * @param code the protocol error code. + * @param cause the cause. + */ + public IkeException(@IkeNotifyPayload.NotifyType int code, Throwable cause) { + super(cause); + errorCode = code; + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidMajorVersionException.java b/src/java/com/android/ike/ikev2/exceptions/InvalidMajorVersionException.java index dc0357ee..d23d0e3c 100644 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidMajorVersionException.java +++ b/src/java/com/android/ike/ikev2/exceptions/InvalidMajorVersionException.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.net.ipsec.ike.exceptions; +package com.android.ike.ikev2.exceptions; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MAJOR_VERSION; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; +import com.android.ike.ikev2.message.IkeNotifyPayload; /** * This exception is thrown when major version is higher than 2. @@ -28,8 +26,8 @@ import android.net.ipsec.ike.exceptions.IkeProtocolException; * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.5">RFC 7296, Internet Key Exchange * Protocol Version 2 (IKEv2)</a> */ -public final class InvalidMajorVersionException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 1; +public final class InvalidMajorVersionException extends IkeException { + public final byte receivedMajorVersion; /** * Construct a instance of InvalidMajorVersionException @@ -37,29 +35,7 @@ public final class InvalidMajorVersionException extends IkeProtocolException { * @param version the major version in received packet */ public InvalidMajorVersionException(byte version) { - super(ERROR_TYPE_INVALID_MAJOR_VERSION, new byte[] {version}); - } - - /** - * Construct a instance of InvalidMajorVersionException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public InvalidMajorVersionException(byte[] notifyData) { - super(ERROR_TYPE_INVALID_MAJOR_VERSION, notifyData); - } - - /** - * Return the major verion included in this exception. - * - * @return the major verion - */ - public int getMajorVerion() { - return byteArrayToInteger(getErrorData()); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; + super(IkeNotifyPayload.NOTIFY_TYPE_INVALID_MAJOR_VERSION); + receivedMajorVersion = version; } } diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidSyntaxException.java b/src/java/com/android/ike/ikev2/exceptions/InvalidSyntaxException.java index fd73f2f1..489eeff8 100644 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidSyntaxException.java +++ b/src/java/com/android/ike/ikev2/exceptions/InvalidSyntaxException.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.net.ipsec.ike.exceptions; +package com.android.ike.ikev2.exceptions; -import android.net.ipsec.ike.exceptions.IkeProtocolException; +import com.android.ike.ikev2.message.IkeNotifyPayload; /** * This exception is thrown if any IKE message field is invalid. @@ -26,16 +26,14 @@ import android.net.ipsec.ike.exceptions.IkeProtocolException; * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.10.1">RFC 7296, Internet Key Exchange * Protocol Version 2 (IKEv2)</a> */ -public final class InvalidSyntaxException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 0; - +public final class InvalidSyntaxException extends IkeException { /** * Construct an instance of InvalidSyntaxException. * * @param message the descriptive message. */ public InvalidSyntaxException(String message) { - super(ERROR_TYPE_INVALID_SYNTAX, message); + super(IkeNotifyPayload.NOTIFY_TYPE_INVALID_SYNTAX); } /** @@ -44,30 +42,6 @@ public final class InvalidSyntaxException extends IkeProtocolException { * @param cause the reason of exception. */ public InvalidSyntaxException(Throwable cause) { - super(ERROR_TYPE_INVALID_SYNTAX, cause); - } - - /** - * Construct a instance of InvalidSyntaxException. - * - * @param message the descriptive message. - * @param cause the reason of exception. - */ - public InvalidSyntaxException(String message, Throwable cause) { - super(ERROR_TYPE_INVALID_SYNTAX, message, cause); - } - - /** - * Construct a instance of InvalidSyntaxException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public InvalidSyntaxException(byte[] notifyData) { - super(ERROR_TYPE_INVALID_SYNTAX, notifyData); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; + super(IkeNotifyPayload.NOTIFY_TYPE_INVALID_SYNTAX, cause); } } diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/NoValidProposalChosenException.java b/src/java/com/android/ike/ikev2/exceptions/NoValidProposalChosenException.java index 4514c65a..8aac8724 100644 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/NoValidProposalChosenException.java +++ b/src/java/com/android/ike/ikev2/exceptions/NoValidProposalChosenException.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.net.ipsec.ike.exceptions; +package com.android.ike.ikev2.exceptions; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; +import com.android.ike.ikev2.message.IkeNotifyPayload; /** * This exception is thrown if either none of SA proposals from SA initiator is acceptable or the @@ -29,39 +27,13 @@ import android.net.ipsec.ike.exceptions.IkeProtocolException; * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.7">RFC 7296, Internet Key Exchange * Protocol Version 2 (IKEv2)</a> */ -public final class NoValidProposalChosenException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 0; - +public final class NoValidProposalChosenException extends IkeException { /** * Construct an instance of NoValidProposalChosenException. * * @param message the descriptive message. */ public NoValidProposalChosenException(String message) { - super(ERROR_TYPE_NO_PROPOSAL_CHOSEN, message); - } - - /** - * Construct an instance of NoValidProposalChosenException. - * - * @param message the descriptive message. - * @param cause the reason of exception. - */ - public NoValidProposalChosenException(String message, Throwable cause) { - super(ERROR_TYPE_NO_PROPOSAL_CHOSEN, cause); - } - - /** - * Construct a instance of NoValidProposalChosenException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public NoValidProposalChosenException(byte[] notifyData) { - super(ERROR_TYPE_NO_PROPOSAL_CHOSEN, notifyData); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; + super(IkeNotifyPayload.NOTIFY_TYPE_NO_PROPOSAL_CHOSEN, message); } } diff --git a/src/java/com/android/ike/ikev2/exceptions/UnsupportedCriticalPayloadException.java b/src/java/com/android/ike/ikev2/exceptions/UnsupportedCriticalPayloadException.java new file mode 100644 index 00000000..1bfde971 --- /dev/null +++ b/src/java/com/android/ike/ikev2/exceptions/UnsupportedCriticalPayloadException.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.exceptions; + +import com.android.ike.ikev2.message.IkeNotifyPayload; + +import java.util.List; + +/** + * This exception is thrown when payload type is not supported and critical bit is set + * + * <p>Include UNSUPPORTED_CRITICAL_PAYLOAD Notify payload in a response message containing the + * payload type for each payload. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.5">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public final class UnsupportedCriticalPayloadException extends IkeException { + + public final List<Integer> payloadTypeList; + + /** + * Construct an instance of UnsupportedCriticalPayloadException + * + * @param payloadList the list of all unsupported critical payload types + */ + public UnsupportedCriticalPayloadException(List<Integer> payloadList) { + super(IkeNotifyPayload.NOTIFY_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD); + payloadTypeList = payloadList; + } +} diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java new file mode 100644 index 00000000..9d7e0449 --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import android.annotation.StringDef; + +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.message.IkeAuthPayload.AuthMethod; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * IkeAuthDigitalSignPayload represents Authentication Payload using a specific or generic digital + * signature authentication method. + * + * <p>If AUTH_METHOD_RSA_DIGITAL_SIGN is used, then the hash algorithm is SHA1. If + * AUTH_METHOD_GENERIC_DIGITAL_SIGN is used, the signature algorihtm and hash algorithm are + * extracted from authentication data. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + * @see <a href="https://tools.ietf.org/html/rfc7427">RFC 7427, Signature Authentication in the + * Internet Key Exchange Version 2 (IKEv2)</a> + */ +public class IkeAuthDigitalSignPayload extends IkeAuthPayload { + + // Byte arrays of DER encoded identifier ASN.1 objects that indicates the algorithm used to + // generate the signature, extracted from + // <a href="https://tools.ietf.org/html/rfc7427#appendix-A"> RFC 7427. There is no need to + // understand the encoding process. They are just constants to indicate the algorithm type. + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA1 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x05, (byte) 0x05, (byte) 0x00 + }; + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x0b, (byte) 0x05, (byte) 0x00 + }; + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x0c, (byte) 0x05, (byte) 0x00 + }; + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x0d, (byte) 0x05, (byte) 0x00 + }; + + // Length of ASN.1 object length field. + private static final int SIGNATURE_ALGO_ASN1_LEN_LEN = 1; + + // Currently we only support RSA for signature algorithm. + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + SIGNATURE_ALGO_RSA_SHA1, + SIGNATURE_ALGO_RSA_SHA2_256, + SIGNATURE_ALGO_RSA_SHA2_384, + SIGNATURE_ALGO_RSA_SHA2_512 + }) + public @interface SignatureAlgo {} + + public static final String SIGNATURE_ALGO_RSA_SHA1 = "SHA1withRSA"; + public static final String SIGNATURE_ALGO_RSA_SHA2_256 = "SHA256withRSA"; + public static final String SIGNATURE_ALGO_RSA_SHA2_384 = "SHA384withRSA"; + public static final String SIGNATURE_ALGO_RSA_SHA2_512 = "SHA512withRSA"; + // TODO: Allow users to configure authentication method using @SignatureAlgo + + public final String signatureAlgoAndHash; + public final byte[] signature; + + protected IkeAuthDigitalSignPayload( + boolean critical, @AuthMethod int authMethod, byte[] authData) throws IkeException { + super(critical, authMethod); + switch (authMethod) { + case AUTH_METHOD_RSA_DIGITAL_SIGN: + signatureAlgoAndHash = SIGNATURE_ALGO_RSA_SHA1; + signature = authData; + break; + case AUTH_METHOD_GENERIC_DIGITAL_SIGN: + ByteBuffer inputBuffer = ByteBuffer.wrap(authData); + + // Get signature algorithm. + int signAlgoLen = Byte.toUnsignedInt(inputBuffer.get()); + byte[] signAlgoBytes = new byte[signAlgoLen]; + inputBuffer.get(signAlgoBytes); + signatureAlgoAndHash = bytesToSignAlgoName(signAlgoBytes); + + // Get signature. + signature = new byte[authData.length - SIGNATURE_ALGO_ASN1_LEN_LEN - signAlgoLen]; + inputBuffer.get(signature); + break; + default: + // Won't hit here. + throw new IllegalArgumentException("Unrecognized authentication method."); + } + } + + private String bytesToSignAlgoName(byte[] signAlgoBytes) throws AuthenticationFailedException { + if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA1, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA1; + } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA2_256; + } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA2_384; + } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA2_512; + } else { + throw new AuthenticationFailedException( + "Unrecognized ASN.1 objects for Signature algorithm and Hash"); + } + } + + // TODO: Add methods for generating and validating signature. + + @Override + protected void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer) { + // TODO: Implement it. + throw new UnsupportedOperationException( + "It is not supported to encode a " + getTypeString()); + } + + @Override + protected int getAuthDataLength() { + // TODO: Implement it. + throw new UnsupportedOperationException( + "It is not supported to get payload length of " + getTypeString()); + } + + @Override + public String getTypeString() { + return "Authentication-Digital-Signature Payload"; + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java index 53a3f65f..d6e2db06 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java @@ -14,17 +14,19 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import android.annotation.IntDef; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.IkeException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; +import java.security.InvalidKeyException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; /** * IkeAuthPayload is an abstract class that represents the common information for all Authentication @@ -65,7 +67,7 @@ public abstract class IkeAuthPayload extends IkePayload { } protected static IkeAuthPayload getIkeAuthPayload(boolean critical, byte[] payloadBody) - throws IkeProtocolException { + throws IkeException { ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); int authMethod = Byte.toUnsignedInt(inputBuffer.get()); @@ -76,6 +78,7 @@ public abstract class IkeAuthPayload extends IkePayload { byte[] authData = new byte[payloadBody.length - AUTH_HEADER_LEN]; inputBuffer.get(authData); switch (authMethod) { + // TODO: Handle RSA and generic signature-based authentication. case AUTH_METHOD_PRE_SHARED_KEY: return new IkeAuthPskPayload(critical, authData); case AUTH_METHOD_RSA_DIGITAL_SIGN: @@ -85,10 +88,26 @@ public abstract class IkeAuthPayload extends IkePayload { return new IkeAuthDigitalSignPayload( critical, AUTH_METHOD_GENERIC_DIGITAL_SIGN, authData); default: - throw new AuthenticationFailedException("Unsupported authentication method"); + // TODO: Throw AuthenticationFailedException + throw new UnsupportedOperationException("Unsupported authentication method"); } } + // Sign data with PRF when building outbound packet or verifying inbound packet. It is called + // for calculating signature over ID payload for all types of authentication and also for + // calculating signature over PSK for PSK authentication. + protected static byte[] signWithPrf(Mac prfMac, byte[] prfKeyBytes, byte[] dataToSign) + throws InvalidKeyException { + SecretKeySpec prfKey = new SecretKeySpec(prfKeyBytes, prfMac.getAlgorithm()); + prfMac.init(prfKey); + + ByteBuffer dataBuffer = ByteBuffer.wrap(dataToSign); + + // Calculate MAC. + prfMac.update(dataBuffer); + return prfMac.doFinal(); + } + // When not using EAP, the peers are authenticated by having each sign a block of data named as // SignedOctets. IKE initiator's SignedOctets are the concatenation of the IKE_INIT request // message, the Nonce of IKE responder and the signed ID-Initiator payload body. Similarly, IKE @@ -98,9 +117,10 @@ public abstract class IkeAuthPayload extends IkePayload { byte[] ikeInitBytes, byte[] nonce, byte[] idPayloadBodyBytes, - IkeMacPrf ikePrf, - byte[] prfKeyBytes) { - byte[] signedidPayloadBodyBytes = ikePrf.signBytes(prfKeyBytes, idPayloadBodyBytes); + Mac prfMac, + byte[] prfKeyBytes) + throws InvalidKeyException { + byte[] signedidPayloadBodyBytes = signWithPrf(prfMac, prfKeyBytes, idPayloadBodyBytes); ByteBuffer buffer = ByteBuffer.allocate( @@ -122,6 +142,11 @@ public abstract class IkeAuthPayload extends IkePayload { return GENERIC_HEADER_LENGTH + AUTH_HEADER_LEN + getAuthDataLength(); } + @Override + public String getTypeString() { + return "Authentication Payload"; + } + protected abstract void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer); protected abstract int getAuthDataLength(); diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPskPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java index 93bef170..182df8a8 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPskPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; import java.nio.ByteBuffer; +import java.security.InvalidKeyException; import java.util.Arrays; +import javax.crypto.Mac; + /** * IkeAuthPskPayload represents an Authentication Payload using Pre-Shared Key to do authentication. * @@ -61,20 +63,20 @@ public final class IkeAuthPskPayload extends IkeAuthPayload { * @param nonce nonce of IKE responder for calculating IKE initiator's SignedOctets. * @param idPayloadBodyBytes ID-Initiator payload body for calculating IKE initiator's * SignedOctets. - * @param ikePrf the negotiated PRF. - * @param prfKeyBytes the negotiated PRF key. + * @param prfMac locally stored PRF + * @param prfKeyBytes locally stored PRF keys */ public IkeAuthPskPayload( byte[] psk, byte[] ikeInitBytes, byte[] nonce, byte[] idPayloadBodyBytes, - IkeMacPrf ikePrf, + Mac prfMac, byte[] prfKeyBytes) { super(false, IkeAuthPayload.AUTH_METHOD_PRE_SHARED_KEY); signature = calculatePskSignature( - psk, ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes); + psk, ikeInitBytes, nonce, idPayloadBodyBytes, prfMac, prfKeyBytes); } private static byte[] calculatePskSignature( @@ -82,13 +84,16 @@ public final class IkeAuthPskPayload extends IkeAuthPayload { byte[] ikeInitBytes, byte[] nonce, byte[] idPayloadBodyBytes, - IkeMacPrf ikePrf, + Mac prfMac, byte[] prfKeyBytes) { - byte[] signingKeyBytes = ikePrf.signBytes(psk, IKE_KEY_PAD_STRING_ASCII_HEX_BYTES); - byte[] dataToSignBytes = - getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes); - - return ikePrf.signBytes(signingKeyBytes, dataToSignBytes); + try { + byte[] signingKeyBytes = signWithPrf(prfMac, psk, IKE_KEY_PAD_STRING_ASCII_HEX_BYTES); + byte[] dataToSignBytes = + getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, prfMac, prfKeyBytes); + return signWithPrf(prfMac, signingKeyBytes, dataToSignBytes); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("Locally stored PRF key is invalid: ", e); + } } /** @@ -103,8 +108,8 @@ public final class IkeAuthPskPayload extends IkeAuthPayload { * @param nonce nonce of IKE initiator for calculating IKE responder's SignedOctets. * @param idPayloadBodyBytes ID-Responder payload body for calculating IKE responder's * SignedOctets. - * @param ikePrf the negotiated PRF. - * @param prfKeyBytes the negotiated PRF key. + * @param prfMac locally stored PRF + * @param prfKeyBytes locally stored PRF keys * @throws AuthenticationFailedException if received signature is not equal to calculated * signature. */ @@ -113,12 +118,12 @@ public final class IkeAuthPskPayload extends IkeAuthPayload { byte[] ikeInitBytes, byte[] nonce, byte[] idPayloadBodyBytes, - IkeMacPrf ikePrf, + Mac prfMac, byte[] prfKeyBytes) throws AuthenticationFailedException { byte[] calculatedSignature = calculatePskSignature( - psk, ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes); + psk, ikeInitBytes, nonce, idPayloadBodyBytes, prfMac, prfKeyBytes); if (!Arrays.equals(signature, calculatedSignature)) { throw new AuthenticationFailedException("Signature verification failed."); } @@ -136,6 +141,6 @@ public final class IkeAuthPskPayload extends IkeAuthPayload { @Override public String getTypeString() { - return "Auth(PSK)"; + return "Authentication-PSK Payload"; } } diff --git a/src/java/com/android/ike/ikev2/message/IkeCertPayload.java b/src/java/com/android/ike/ikev2/message/IkeCertPayload.java new file mode 100644 index 00000000..bd54ee23 --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeCertPayload.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import android.annotation.IntDef; + +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.IkeException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.ByteBuffer; + +/** + * IkeCertPayload is an abstract class that represents the common information for all Certificate + * Payload carrying different types of certifciate-related data and static methods related to + * certificate validation. + * + * <p>Certificate Payload is only sent in IKE_AUTH exchange. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.6">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public abstract class IkeCertPayload extends IkePayload { + // Length of certificate encoding type field in octets. + private static final int CERT_ENCODING_LEN = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + CERTIFICATE_ENCODING_X509_CERT_SIGNATURE, + CERTIFICATE_ENCODING_CRL, + CERTIFICATE_ENCODING_X509_CERT_HASH_URL, + }) + public @interface CertificateEncoding {} + + public static final int CERTIFICATE_ENCODING_X509_CERT_SIGNATURE = 4; + public static final int CERTIFICATE_ENCODING_CRL = 7; + public static final int CERTIFICATE_ENCODING_X509_CERT_HASH_URL = 12; + + @CertificateEncoding public final int certEncodingType; + + protected IkeCertPayload(boolean critical, @CertificateEncoding int encodingType) { + super(PAYLOAD_TYPE_CERT, critical); + certEncodingType = encodingType; + } + + protected static IkeCertPayload getIkeCertPayload(boolean critical, byte[] payloadBody) + throws IkeException { + ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); + + int certEncodingType = Byte.toUnsignedInt(inputBuffer.get()); + byte[] certData = new byte[payloadBody.length - CERT_ENCODING_LEN]; + inputBuffer.get(certData); + switch (certEncodingType) { + case CERTIFICATE_ENCODING_X509_CERT_SIGNATURE: + return new IkeCertX509CertPayload(critical, certData); + // TODO: Support decoding CRL and "Hash and URL". + case CERTIFICATE_ENCODING_CRL: + throw new AuthenticationFailedException( + "CERTIFICATE_ENCODING_CRL decoding is unsupported."); + case CERTIFICATE_ENCODING_X509_CERT_HASH_URL: + throw new AuthenticationFailedException( + "CERTIFICATE_ENCODING_X509_CERT_HASH_URL decoding is unsupported"); + default: + throw new AuthenticationFailedException("Unrecognized certificate encoding type."); + } + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java b/src/java/com/android/ike/ikev2/message/IkeCertX509CertPayload.java index 1804b9b9..76fab4f2 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeCertX509CertPayload.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.IkeException; import java.io.ByteArrayInputStream; import java.nio.ByteBuffer; @@ -40,14 +39,7 @@ import java.security.cert.X509Certificate; public final class IkeCertX509CertPayload extends IkeCertPayload { public final X509Certificate certificate; - /** Construct IkeCertX509CertPayload for an outbound packet. */ - public IkeCertX509CertPayload(X509Certificate x509Certificate) { - super(CERTIFICATE_ENCODING_X509_CERT_SIGNATURE); - certificate = x509Certificate; - } - - protected IkeCertX509CertPayload(boolean critical, byte[] certData) - throws IkeProtocolException { + protected IkeCertX509CertPayload(boolean critical, byte[] certData) throws IkeException { super(critical, CERTIFICATE_ENCODING_X509_CERT_SIGNATURE); try { CertificateFactory factory = @@ -100,6 +92,6 @@ public final class IkeCertX509CertPayload extends IkeCertPayload { */ @Override public String getTypeString() { - return "Cert(X509)"; + return "Certificate Payload Carrying X.509 Certificate"; } } diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeDeletePayload.java b/src/java/com/android/ike/ikev2/message/IkeDeletePayload.java index f629f506..fd4f3644 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeDeletePayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeDeletePayload.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import java.nio.ByteBuffer; @@ -30,30 +29,10 @@ import java.nio.ByteBuffer; * packets. Since IKE library only supports negotiating Child SA using ESP, only the protocol ID of * 3 (ESP) is used for deleting Child SA. * - * The possible request/response pairs for deletion are as follows: - * - IKE SA deletion: - * Incoming: INFORMATIONAL(DELETE(PROTO_IKE)) - * Outgoing: INFORMATIONAL() - * - * - ESP SA deletion: - * Incoming: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_OUT)) - * Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_IN)) - * - * - ESP SA simultaneous deletion: - * Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_IN)) - * Incoming: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_OUT)) - * Outgoing: INFORMATIONAL() // Notice DELETE payload omitted - * - * - ESP SA simultaneous multi-deletion: - * Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_IN)) - * Incoming: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_OUT, SPI_B_OUT)) - * Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_B_IN)) // Notice SPI_A_OUT omitted - * * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.11">RFC 7296, Internet Key Exchange * Protocol Version 2 (IKEv2)</a> */ -public final class IkeDeletePayload extends IkeInformationalPayload { - private static final int DELETE_HEADER_LEN = 4; +public final class IkeDeletePayload extends IkePayload { @ProtocolId public final int protocolId; public final byte spiSize; @@ -69,9 +48,9 @@ public final class IkeDeletePayload extends IkeInformationalPayload { * @param critical indicates if this payload is critical. Ignored in supported payload as * instructed by the RFC 7296. * @param payloadBody payload body in byte array - * @throws IkeProtocolException if there is any error + * @throws IkeException if there is any error */ - IkeDeletePayload(boolean critical, byte[] payloadBody) throws IkeProtocolException { + IkeDeletePayload(boolean critical, byte[] payloadBody) throws IkeException { super(PAYLOAD_TYPE_DELETE, critical); ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); @@ -107,37 +86,7 @@ public final class IkeDeletePayload extends IkeInformationalPayload { } } - /** - * Constructor for an outbound IKE SA deletion payload. - * - * <p>This constructor takes no SPI, as IKE SAs are deleted by sending a delete payload within - * the negotiated session. As such, the SPIs are shared state that does not need to be sent. - */ - public IkeDeletePayload() { - super(PAYLOAD_TYPE_DELETE, false); - protocolId = PROTOCOL_ID_IKE; - spiSize = SPI_LEN_NOT_INCLUDED; - numSpi = 0; - spisToDelete = new int[0]; - } - - /** - * Constructor for an outbound Child SA deletion payload. - * - * @param spis array of SPIs of Child SAs to delete. Must contain at least one SPI. - */ - public IkeDeletePayload(int[] spis) { - super(PAYLOAD_TYPE_DELETE, false); - - if (spis == null || spis.length < 1) { - throw new IllegalArgumentException("No SPIs provided"); - } - - protocolId = PROTOCOL_ID_ESP; - spiSize = SPI_LEN_IPSEC; - numSpi = spis.length; - spisToDelete = spis; - } + // TODO: Add a constructor for building outbound IKE message. /** * Encode Delete Payload to ByteBuffer. @@ -147,12 +96,8 @@ public final class IkeDeletePayload extends IkeInformationalPayload { */ @Override protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) numSpi); - - for (int toDelete : spisToDelete) { - byteBuffer.putInt(toDelete); - } + throw new UnsupportedOperationException("Operation not supported."); + // TODO: Implement it. } /** @@ -162,7 +107,8 @@ public final class IkeDeletePayload extends IkeInformationalPayload { */ @Override protected int getPayloadLength() { - return GENERIC_HEADER_LENGTH + DELETE_HEADER_LEN + spisToDelete.length * spiSize; + throw new UnsupportedOperationException("Operation not supported."); + // TODO: Implement it. } /** @@ -172,6 +118,6 @@ public final class IkeDeletePayload extends IkeInformationalPayload { */ @Override public String getTypeString() { - return "Del"; + return "Delete Payload"; } } diff --git a/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java b/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java new file mode 100644 index 00000000..a41d8555 --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** + * IkeEncryptedPayloadBody is a package private class that represents an IKE payload substructure + * that contains initialization vector, encrypted content, padding, pad length and integrity + * checksum. + * + * <p>Both an Encrypted Payload (IkeSkPayload) and an EncryptedFragmentPayload (IkeSkfPayload) + * consists of an IkeEncryptedPayloadBody instance. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#page-105">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + * @see <a href="https://tools.ietf.org/html/rfc7383#page-6">RFC 7383, Internet Key Exchange + * Protocol Version 2 (IKEv2) Message Fragmentation</a> + */ +final class IkeEncryptedPayloadBody { + // Length of pad length field. + private static final int PAD_LEN_LEN = 1; + + private final byte[] mUnencryptedData; + private final byte[] mEncryptedAndPaddedData; + private final byte[] mIv; + private final byte[] mIntegrityChecksum; + + /** + * Package private constructor for constructing an instance of IkeEncryptedPayloadBody from + * decrypting an incoming packet. + */ + IkeEncryptedPayloadBody( + byte[] message, Mac integrityMac, int checksumLen, Cipher decryptCipher, SecretKey dKey) + throws IkeException, GeneralSecurityException { + ByteBuffer inputBuffer = ByteBuffer.wrap(message); + + // Skip IKE header and SK payload header + byte[] tempArray = new byte[IkeHeader.IKE_HEADER_LENGTH + IkePayload.GENERIC_HEADER_LENGTH]; + inputBuffer.get(tempArray); + + // Extract bytes for authentication and decryption. + int expectedIvLen = decryptCipher.getBlockSize(); + mIv = new byte[expectedIvLen]; + + int encryptedDataLen = + message.length + - (IkeHeader.IKE_HEADER_LENGTH + + IkePayload.GENERIC_HEADER_LENGTH + + expectedIvLen + + checksumLen); + // IkeMessage will catch exception if encryptedDataLen is negative. + mEncryptedAndPaddedData = new byte[encryptedDataLen]; + + mIntegrityChecksum = new byte[checksumLen]; + inputBuffer.get(mIv).get(mEncryptedAndPaddedData).get(mIntegrityChecksum); + + // Authenticate and decrypt. + byte[] dataToAuthenticate = Arrays.copyOfRange(message, 0, message.length - checksumLen); + validateChecksumOrThrow(dataToAuthenticate, integrityMac, mIntegrityChecksum); + mUnencryptedData = decrypt(mEncryptedAndPaddedData, decryptCipher, dKey, mIv); + } + + /** + * Package private constructor for constructing an instance of IkeEncryptedPayloadBody for + * building an outbound packet. + */ + IkeEncryptedPayloadBody( + IkeHeader ikeHeader, + @IkePayload.PayloadType int firstPayloadType, + byte[] unencryptedPayloads, + Mac integrityMac, + int checksumLen, + Cipher encryptCipher, + SecretKey eKey) { + this( + ikeHeader, + firstPayloadType, + unencryptedPayloads, + integrityMac, + checksumLen, + encryptCipher, + eKey, + encryptCipher.getIV(), + calculatePadding(unencryptedPayloads.length, encryptCipher.getBlockSize())); + } + + /** Package private constructor only for testing. */ + @VisibleForTesting + IkeEncryptedPayloadBody( + IkeHeader ikeHeader, + @IkePayload.PayloadType int firstPayloadType, + byte[] unencryptedPayloads, + Mac integrityMac, + int checksumLen, + Cipher encryptCipher, + SecretKey eKey, + byte[] iv, + byte[] padding) { + mUnencryptedData = unencryptedPayloads; + + // Encrypt data + mIv = iv; + mEncryptedAndPaddedData = encrypt(unencryptedPayloads, encryptCipher, eKey, iv, padding); + + // Build authenticated section using ByteBuffer. Authenticated section includes bytes from + // beginning of IKE header to the pad length, which are concatenation of IKE header, current + // payload header, iv and encrypted and padded data. + int dataToAuthenticateLength = + IkeHeader.IKE_HEADER_LENGTH + + IkePayload.GENERIC_HEADER_LENGTH + + iv.length + + mEncryptedAndPaddedData.length; + ByteBuffer authenticatedSectionBuffer = ByteBuffer.allocate(dataToAuthenticateLength); + + // Encode IKE header + int encryptedPayloadLength = + IkePayload.GENERIC_HEADER_LENGTH + + iv.length + + mEncryptedAndPaddedData.length + + checksumLen; + ikeHeader.encodeToByteBuffer(authenticatedSectionBuffer, encryptedPayloadLength); + + // Encode payload header. The next payload type field indicates the first payload nested in + // this SkPayload/SkfPayload. + int payloadLength = + IkePayload.GENERIC_HEADER_LENGTH + + iv.length + + mEncryptedAndPaddedData.length + + checksumLen; + IkePayload.encodePayloadHeaderToByteBuffer( + firstPayloadType, payloadLength, authenticatedSectionBuffer); + + // Encode iv and padded encrypted data. + authenticatedSectionBuffer.put(iv).put(mEncryptedAndPaddedData); + + // Calculate checksum + mIntegrityChecksum = + calculateChecksum(authenticatedSectionBuffer.array(), integrityMac, checksumLen); + } + + // TODO: Add another constructor for AEAD protected payload. + + // TODO: Add constructors that initiate IkeEncryptedPayloadBody for an outbound packet + + /** Package private for testing */ + @VisibleForTesting + static byte[] calculateChecksum(byte[] dataToAuthenticate, Mac integrityMac, int checksumLen) { + ByteBuffer inputBuffer = ByteBuffer.wrap(dataToAuthenticate); + integrityMac.update(inputBuffer); + byte[] calculatedChecksum = Arrays.copyOfRange(integrityMac.doFinal(), 0, checksumLen); + return calculatedChecksum; + } + + /** Package private for testing */ + @VisibleForTesting + static void validateChecksumOrThrow( + byte[] dataToAuthenticate, Mac integrityMac, byte[] integrityChecksum) + throws GeneralSecurityException { + // TODO: Make it package private and add test. + int checkSumLen = integrityChecksum.length; + byte[] calculatedChecksum = + calculateChecksum(dataToAuthenticate, integrityMac, checkSumLen); + + if (!Arrays.equals(integrityChecksum, calculatedChecksum)) { + throw new GeneralSecurityException("Message authentication failed."); + } + } + + /** Package private for testing */ + @VisibleForTesting + static byte[] encrypt( + byte[] dataToEncrypt, Cipher encryptCipher, SecretKey eKey, byte[] iv, byte[] padding) { + int padLength = padding.length; + int paddedDataLength = dataToEncrypt.length + padLength + PAD_LEN_LEN; + ByteBuffer inputBuffer = ByteBuffer.allocate(paddedDataLength); + inputBuffer.put(dataToEncrypt).put(padding).put((byte) padLength); + inputBuffer.rewind(); + + try { + // Encrypt data. + ByteBuffer outputBuffer = ByteBuffer.allocate(paddedDataLength); + encryptCipher.init(Cipher.ENCRYPT_MODE, eKey, new IvParameterSpec(iv)); + encryptCipher.doFinal(inputBuffer, outputBuffer); + return outputBuffer.array(); + } catch (GeneralSecurityException e) { + throw new IllegalArgumentException("Fail to encrypt IKE message. ", e); + } + } + + /** Package private for testing */ + @VisibleForTesting + static byte[] decrypt(byte[] encryptedData, Cipher decryptCipher, SecretKey dKey, byte[] iv) + throws GeneralSecurityException { + // TODO: Make it package private and add test. + decryptCipher.init(Cipher.DECRYPT_MODE, dKey, new IvParameterSpec(iv)); + + ByteBuffer inputBuffer = ByteBuffer.wrap(encryptedData); + ByteBuffer outputBuffer = ByteBuffer.allocate(encryptedData.length); + decryptCipher.doFinal(inputBuffer, outputBuffer); + + // Remove padding + outputBuffer.rewind(); + int padLength = Byte.toUnsignedInt(outputBuffer.get(encryptedData.length - PAD_LEN_LEN)); + byte[] decryptedData = new byte[encryptedData.length - padLength - PAD_LEN_LEN]; + + outputBuffer.get(decryptedData); + return decryptedData; + } + + /** Package private for testing */ + @VisibleForTesting + static byte[] calculatePadding(int dataToEncryptLength, int blockSize) { + // Sum of dataToEncryptLength, PAD_LEN_LEN and padLength should be aligned with block size. + int unpaddedLen = dataToEncryptLength + PAD_LEN_LEN; + int padLength = (unpaddedLen + blockSize - 1) / blockSize * blockSize - unpaddedLen; + byte[] padding = new byte[padLength]; + + // According to RFC 7296, "Padding MAY contain any value". + new SecureRandom().nextBytes(padding); + + return padding; + } + + /** Package private */ + byte[] getUnencryptedData() { + return mUnencryptedData; + } + + /** Package private */ + int getLength() { + return (mIv.length + mEncryptedAndPaddedData.length + mIntegrityChecksum.length); + } + + /** Package private */ + byte[] encode() { + ByteBuffer buffer = ByteBuffer.allocate(getLength()); + buffer.put(mIv).put(mEncryptedAndPaddedData).put(mIntegrityChecksum); + return buffer.array(); + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeHeader.java b/src/java/com/android/ike/ikev2/message/IkeHeader.java index 7aa4fbc8..c4f215ca 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeHeader.java +++ b/src/java/com/android/ike/ikev2/message/IkeHeader.java @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PayloadType; +import static com.android.ike.ikev2.message.IkePayload.PayloadType; import android.annotation.IntDef; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.util.SparseArray; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidMajorVersionException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.exceptions.InvalidMajorVersionException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -46,8 +45,6 @@ public final class IkeHeader { // Indicate whether this message is sent from the original IKE initiator private static final byte IKE_HEADER_FLAG_FROM_IKE_INITIATOR = (byte) 0x08; - private static final SparseArray<String> EXCHANGE_TYPE_TO_STRING; - public static final int IKE_HEADER_LENGTH = 28; @Retention(RetentionPolicy.SOURCE) @@ -64,14 +61,6 @@ public final class IkeHeader { public static final int EXCHANGE_TYPE_CREATE_CHILD_SA = 36; public static final int EXCHANGE_TYPE_INFORMATIONAL = 37; - static { - EXCHANGE_TYPE_TO_STRING = new SparseArray<>(); - EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_IKE_SA_INIT, "IKE INIT"); - EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_IKE_AUTH, "IKE AUTH"); - EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_CREATE_CHILD_SA, "Create Child"); - EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_INFORMATIONAL, "Informational"); - } - public final long ikeInitiatorSpi; public final long ikeResponderSpi; @PayloadType public final int nextPayloadType; @@ -131,7 +120,7 @@ public final class IkeHeader { * * @param packet the raw byte array of the whole IKE message */ - public IkeHeader(byte[] packet) throws IkeProtocolException { + public IkeHeader(byte[] packet) throws IkeException { if (packet.length <= IKE_HEADER_LENGTH) { throw new InvalidSyntaxException("IKE message is too short to contain a header"); } @@ -156,21 +145,6 @@ public final class IkeHeader { mEncodedMessageLength = buffer.getInt(); } - /** Packet private method to build header of an IKE fragemnt from current IKE header. */ - IkeHeader makeSkfHeaderFromSkHeader() { - if (nextPayloadType != IkePayload.PAYLOAD_TYPE_SK) { - throw new IllegalArgumentException("Next payload type is not SK."); - } - return new IkeHeader( - ikeInitiatorSpi, - ikeResponderSpi, - IkePayload.PAYLOAD_TYPE_SKF, - exchangeType, - isResponseMsg, - fromIkeInitiator, - messageId); - } - /*Package private*/ @VisibleForTesting int getInboundMessageLength() { @@ -181,8 +155,8 @@ public final class IkeHeader { return mEncodedMessageLength; } - /** Validate major version of inbound IKE header. */ - public void validateMajorVersion() throws IkeProtocolException { + /** Validate syntax and major version of inbound IKE header. */ + public void checkInboundValidOrThrow(int packetLength) throws IkeException { if (majorVersion > 2) { // Receive higher version of protocol. Stop parsing. throw new InvalidMajorVersionException(majorVersion); @@ -194,15 +168,6 @@ public final class IkeHeader { // error. throw new InvalidSyntaxException("Major version is smaller than 2."); } - } - - /** - * Validate syntax of inbound IKE header. - * - * <p>It MUST only be used for an inbound IKE header because we don't know the outbound message - * length before we encode it. - */ - public void validateInboundHeader(int packetLength) throws IkeProtocolException { if (exchangeType < EXCHANGE_TYPE_IKE_SA_INIT || exchangeType > EXCHANGE_TYPE_INFORMATIONAL) { throw new InvalidSyntaxException("Invalid IKE Exchange Type."); @@ -231,14 +196,4 @@ public final class IkeHeader { byteBuffer.put(flag).putInt(messageId).putInt(IKE_HEADER_LENGTH + encodedMessageBodyLen); } - - /** Returns basic information for logging. */ - public String getBasicInfoString() { - String exchangeStr = EXCHANGE_TYPE_TO_STRING.get(exchangeType); - if (exchangeStr == null) exchangeStr = "Unknown exchange (" + exchangeType + ")"; - - String reqOrResp = isResponseMsg ? "response" : "request"; - - return exchangeStr + " " + reqOrResp + " " + messageId; - } } diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeIdPayload.java b/src/java/com/android/ike/ikev2/message/IkeIdPayload.java index 12cc14ab..49b88603 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeIdPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeIdPayload.java @@ -14,18 +14,15 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -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.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.IkeIdentification; +import com.android.ike.ikev2.IkeIdentification.IkeIpv4AddrIdentification; +import com.android.ike.ikev2.IkeIdentification.IkeIpv6AddrIdentification; +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.message.IkePayload.PayloadType; import java.nio.ByteBuffer; @@ -54,10 +51,9 @@ public final class IkeIdPayload extends IkePayload { * @param payloadBody payload body in byte array. * @param isInitiator indicates whether this payload contains the ID of IKE initiator or IKE * responder. - * @throws IkeProtocolException for decoding error. + * @throws IkeException for decoding error. */ - IkeIdPayload(boolean critical, byte[] payloadBody, boolean isInitiator) - throws IkeProtocolException { + IkeIdPayload(boolean critical, byte[] payloadBody, boolean isInitiator) throws IkeException { super((isInitiator ? PAYLOAD_TYPE_ID_INITIATOR : PAYLOAD_TYPE_ID_RESPONDER), critical); // TODO: b/119791832 Add helper method for checking payload body length in superclass. if (payloadBody.length <= ID_HEADER_LEN) { @@ -78,20 +74,18 @@ public final class IkeIdPayload extends IkePayload { ikeId = new IkeIpv4AddrIdentification(idData); return; case IkeIdentification.ID_TYPE_FQDN: - ikeId = new IkeFqdnIdentification(idData); - return; + // Fall through case IkeIdentification.ID_TYPE_RFC822_ADDR: - ikeId = new IkeRfc822AddrIdentification(idData); - return; + throw new UnsupportedOperationException("ID type is not supported currently."); case IkeIdentification.ID_TYPE_IPV6_ADDR: ikeId = new IkeIpv6AddrIdentification(idData); return; - case IkeIdentification.ID_TYPE_DER_ASN1_DN: // Fall through + case IkeIdentification.ID_TYPE_DER_ASN1_DN: + // Fall through case IkeIdentification.ID_TYPE_DER_ASN1_GN: - throw new UnsupportedOperationException("ID type is not supported currently."); + // Fall through case IkeIdentification.ID_TYPE_KEY_ID: - ikeId = new IkeKeyIdIdentification(idData); - return; + throw new UnsupportedOperationException("ID type is not supported currently."); default: throw new AuthenticationFailedException("Unsupported ID type: " + idType); } @@ -110,21 +104,6 @@ public final class IkeIdPayload extends IkePayload { } /** - * Get encoded ID payload body for building or validating an Auth Payload. - * - * @return the byte array of encoded ID payload body. - */ - public byte[] getEncodedPayloadBody() { - ByteBuffer byteBuffer = ByteBuffer.allocate(getPayloadLength() - GENERIC_HEADER_LENGTH); - - byteBuffer - .put((byte) ikeId.idType) - .put(new byte[ID_HEADER_RESERVED_LEN]) - .put(ikeId.getEncodedIdData()); - return byteBuffer.array(); - } - - /** * Encode Identification Payload to ByteBuffer. * * @param nextPayload type of payload that follows this payload. @@ -133,7 +112,10 @@ public final class IkeIdPayload extends IkePayload { @Override protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer.put(getEncodedPayloadBody()); + byteBuffer + .put((byte) ikeId.idType) + .put(new byte[ID_HEADER_RESERVED_LEN]) + .put(ikeId.getEncodedIdData()); } /** @@ -155,9 +137,9 @@ public final class IkeIdPayload extends IkePayload { public String getTypeString() { switch (payloadType) { case PAYLOAD_TYPE_ID_INITIATOR: - return "IDi"; + return "Identification Initiator Payload"; case PAYLOAD_TYPE_ID_RESPONDER: - return "IDr"; + return "Identification Responder Payload"; default: // Won't reach here. throw new IllegalArgumentException( diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java b/src/java/com/android/ike/ikev2/message/IkeKePayload.java index 7389bf8d..92e22381 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeKePayload.java @@ -14,28 +14,26 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import android.annotation.Nullable; -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import com.android.internal.net.ipsec.ike.IkeDhParams; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.utils.BigIntegerUtils; +import com.android.ike.ikev2.IkeDhParams; +import com.android.ike.ikev2.SaProposal; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.utils.BigIntegerUtils; import java.math.BigInteger; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.ProviderException; import java.security.SecureRandom; -import java.security.spec.InvalidKeySpecException; import javax.crypto.KeyAgreement; import javax.crypto.interfaces.DHPrivateKey; @@ -77,8 +75,8 @@ public final class IkeKePayload extends IkePayload { /** * localPrivateKey caches the locally generated private key when building an outbound KE - * payload. It will not be sent out. It is only used to calculate DH shared key when IKE library - * receives a public key from the remote server. + * payload. It will not be sent out. It is only used to calculate DH shared + * key when IKE library receives a public key from the remote server. * * <p>localPrivateKey of a inbound payload will be set to null. Caller MUST ensure its an * outbound payload before using localPrivateKey. @@ -91,11 +89,11 @@ public final class IkeKePayload extends IkePayload { * @param critical indicates if this payload is critical. Ignored in supported payload as * instructed by the RFC 7296. * @param payloadBody payload body in byte array - * @throws IkeProtocolException if there is any error + * @throws IkeException if there is any error * @see <a href="https://tools.ietf.org/html/rfc7296#page-76">RFC 7296, Internet Key Exchange * Protocol Version 2 (IKEv2), Critical. */ - IkeKePayload(boolean critical, byte[] payloadBody) throws IkeProtocolException { + IkeKePayload(boolean critical, byte[] payloadBody) throws IkeException { super(PAYLOAD_TYPE_KE, critical); isOutbound = false; @@ -225,38 +223,29 @@ public final class IkeKePayload extends IkePayload { * * @param privateKeySpec contains the local private key, DH prime and DH base generator. * @param remotePublicKey the public key from remote server. - * @throws GeneralSecurityException if the remote public key is invalid. + * @throws GeneralSecurityException for security-related exception. */ public static byte[] getSharedKey(DHPrivateKeySpec privateKeySpec, byte[] remotePublicKey) throws GeneralSecurityException { - KeyAgreement dhKeyAgreement; - KeyFactory dhKeyFactory; - try { - // Apply local private key. - dhKeyAgreement = - KeyAgreement.getInstance( - KEY_EXCHANGE_ALGORITHM, IkeMessage.getSecurityProvider()); - dhKeyFactory = - KeyFactory.getInstance( - KEY_EXCHANGE_ALGORITHM, IkeMessage.getSecurityProvider()); - DHPrivateKey privateKey = (DHPrivateKey) dhKeyFactory.generatePrivate(privateKeySpec); - dhKeyAgreement.init(privateKey); - } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException e) { - throw new IllegalArgumentException("Failed to generate DH private key", e); - } - - // Build public key. BigInteger publicKeyValue = BigIntegerUtils.unsignedByteArrayToBigInteger(remotePublicKey); BigInteger primeValue = privateKeySpec.getP(); + // TODO: Add recipient test of remotePublicKey, as instructed by RFC6989 section 2.1 + BigInteger baseGenValue = privateKeySpec.getG(); + DHPublicKeySpec publicKeySpec = new DHPublicKeySpec(publicKeyValue, primeValue, baseGenValue); - - // Validate and apply public key. Validation includes range check as instructed by RFC6989 - // section 2.1 + KeyFactory dhKeyFactory = + KeyFactory.getInstance(KEY_EXCHANGE_ALGORITHM, IkeMessage.getSecurityProvider()); DHPublicKey publicKey = (DHPublicKey) dhKeyFactory.generatePublic(publicKeySpec); + DHPrivateKey privateKey = (DHPrivateKey) dhKeyFactory.generatePrivate(privateKeySpec); + + // Calculate shared secret + KeyAgreement dhKeyAgreement = + KeyAgreement.getInstance(KEY_EXCHANGE_ALGORITHM, IkeMessage.getSecurityProvider()); + dhKeyAgreement.init(privateKey); + dhKeyAgreement.doPhase(publicKey, true/** Last phase */); - dhKeyAgreement.doPhase(publicKey, true /* Last phase */); return dhKeyAgreement.generateSecret(); } @@ -267,6 +256,6 @@ public final class IkeKePayload extends IkePayload { */ @Override public String getTypeString() { - return "KE"; + return "KE Payload"; } } diff --git a/src/java/com/android/ike/ikev2/message/IkeMessage.java b/src/java/com/android/ike/ikev2/message/IkeMessage.java new file mode 100644 index 00000000..5b63ee7f --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeMessage.java @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import static com.android.ike.ikev2.message.IkePayload.PayloadType; + +import android.annotation.IntDef; +import android.util.Pair; + +import com.android.ike.ikev2.IkeSessionOptions; +import com.android.ike.ikev2.SaRecord.IkeSaRecord; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.UnsupportedCriticalPayloadException; +import com.android.internal.annotations.VisibleForTesting; +import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.Provider; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; + +/** + * IkeMessage represents an IKE message. + * + * <p>It contains all attributes and provides methods for encoding, decoding, encrypting and + * decrypting. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public final class IkeMessage { + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + MESSAGE_TYPE_IKE_INIT_RESP, + MESSAGE_TYPE_IKE_AUTH_RESP, + MESSAGE_TYPE_DELETE_IKE_REQ, + MESSAGE_TYPE_DELETE_IKE_RESP, + MESSAGE_TYPE_REKEY_IKE_REQ, + MESSAGE_TYPE_REKEY_IKE_RESP, + MESSAGE_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, + MESSAGE_TYPE_INVALID_MAJOR_VERSION, + MESSAGE_TYPE_INVALID_SYNTAX + }) + public @interface MessageType {} + + // Message type for decoded IkeMessage. + public static final int PROCEDURE_TYPE_BASE = 0; + + public static final int MESSAGE_TYPE_IKE_INIT_RESP = PROCEDURE_TYPE_BASE + 1; + public static final int MESSAGE_TYPE_IKE_AUTH_RESP = PROCEDURE_TYPE_BASE + 2; + public static final int MESSAGE_TYPE_DELETE_IKE_REQ = PROCEDURE_TYPE_BASE + 3; + public static final int MESSAGE_TYPE_DELETE_IKE_RESP = PROCEDURE_TYPE_BASE + 4; + public static final int MESSAGE_TYPE_REKEY_IKE_REQ = PROCEDURE_TYPE_BASE + 5; + public static final int MESSAGE_TYPE_REKEY_IKE_RESP = PROCEDURE_TYPE_BASE + 6; + + public static final int NOTIFICATION_TYPE_BASE = PROCEDURE_TYPE_BASE + 100; + public static final int MESSAGE_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = + NOTIFICATION_TYPE_BASE + IkeNotifyPayload.NOTIFY_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; + public static final int MESSAGE_TYPE_INVALID_MAJOR_VERSION = + NOTIFICATION_TYPE_BASE + IkeNotifyPayload.NOTIFY_TYPE_INVALID_MAJOR_VERSION; + public static final int MESSAGE_TYPE_INVALID_SYNTAX = + NOTIFICATION_TYPE_BASE + IkeNotifyPayload.NOTIFY_TYPE_INVALID_SYNTAX; + + private static IIkeMessageHelper sIkeMessageHelper = new IkeMessageHelper(); + // Currently use Bouncy Castle as crypto security provider + static final Provider SECURITY_PROVIDER = new BouncyCastleProvider(); + + public final IkeHeader ikeHeader; + public final List<IkePayload> ikePayloadList; + /** + * Conctruct an instance of IkeMessage. It is called by decode or for building outbound message. + * + * @param header the header of this IKE message + * @param payloadList the list of decoded IKE payloads in this IKE message + */ + public IkeMessage(IkeHeader header, List<IkePayload> payloadList) { + ikeHeader = header; + ikePayloadList = payloadList; + } + + /** + * Get security provider for IKE library + * + * <p>Use BouncyCastleProvider as the default security provider. + * + * @return the security provider of IKE library. + */ + public static Provider getSecurityProvider() { + // TODO: Move this getter out of IKE message package since not only this package uses it. + return SECURITY_PROVIDER; + } + + /** + * Decode unencrypted IKE message body and create an instance of IkeMessage. + * + * <p>This method catches all RuntimeException during decoding incoming IKE packet. + * + * @param header the IKE header that is decoded but not validated. + * @param inputPacket the byte array contains the whole IKE message. + * @return the IkeMessage instance. + * @throws IkeException if there is any protocol error. + */ + public static IkeMessage decode(IkeHeader header, byte[] inputPacket) throws IkeException { + return sIkeMessageHelper.decode(header, inputPacket); + } + + /** + * Decrypt and decode encrypted IKE message body and create an instance of IkeMessage. + * + * @param ikeSessionOptions IkeSessionOptions that contains cryptographic algorithm set. + * @param ikeSaRecord ikeSaRecord where this packet is sent on. + * @param ikeHeader header of IKE packet. + * @param packet IKE packet as a byte array. + * @return decoded IKE message. + * @throws IkeException for decoding errors. + * @throws GeneralSecurityException if there is any error during integrity check or decryption. + */ + public static IkeMessage decode( + IkeSessionOptions ikeSessionOptions, + IkeSaRecord ikeSaRecord, + IkeHeader ikeHeader, + byte[] packet) + throws IkeException, GeneralSecurityException { + return sIkeMessageHelper.decode(ikeSessionOptions, ikeSaRecord, ikeHeader, packet); + } + + private static List<IkePayload> decodePayloadList( + @PayloadType int firstPayloadType, boolean isResp, byte[] unencryptedPayloads) + throws IkeException { + ByteBuffer inputBuffer = ByteBuffer.wrap(unencryptedPayloads); + int currentPayloadType = firstPayloadType; + // For supported payload + List<IkePayload> supportedPayloadList = new LinkedList<>(); + // For unsupported critical payload + List<Integer> unsupportedCriticalPayloadList = new LinkedList<>(); + + while (currentPayloadType != IkePayload.PAYLOAD_TYPE_NO_NEXT) { + Pair<IkePayload, Integer> pair = + IkePayloadFactory.getIkePayload(currentPayloadType, isResp, inputBuffer); + IkePayload payload = pair.first; + + if (!(payload instanceof IkeUnsupportedPayload)) { + supportedPayloadList.add(payload); + } else if (payload.isCritical) { + unsupportedCriticalPayloadList.add(payload.payloadType); + } + // Simply ignore unsupported uncritical payload. + + currentPayloadType = pair.second; + } + + if (inputBuffer.remaining() > 0) { + throw new InvalidSyntaxException( + "Malformed IKE Payload: Unexpected bytes at the end of packet."); + } + + if (unsupportedCriticalPayloadList.size() > 0) { + throw new UnsupportedCriticalPayloadException(unsupportedCriticalPayloadList); + } + return supportedPayloadList; + } + + /** + * Encode unencrypted IKE message. + * + * @return encoded IKE message in byte array. + */ + public byte[] encode() { + return sIkeMessageHelper.encode(this); + } + + /** + * Encrypt and encode packet. + * + * @param ikeSessionOptions IkeSessionOptions that contains cryptographic algorithm set. + * @param ikeSaRecord ikeSaRecord where this packet is sent on. + * @return encoded IKE message in byte array. + */ + public byte[] encode(IkeSessionOptions ikeSessionOptions, IkeSaRecord ikeSaRecord) { + return sIkeMessageHelper.encode(ikeSessionOptions, ikeSaRecord, this); + } + + /** + * Encode all payloads to a byte array. + * + * @return byte array contains all encoded payloads + */ + private byte[] encodePayloads() { + if (ikePayloadList.isEmpty()) { + return new byte[0]; + } + + int payloadLengthSum = 0; + for (IkePayload payload : ikePayloadList) { + payloadLengthSum += payload.getPayloadLength(); + } + + ByteBuffer byteBuffer = ByteBuffer.allocate(payloadLengthSum); + + for (int i = 0; i < ikePayloadList.size() - 1; i++) { + ikePayloadList + .get(i) + .encodeToByteBuffer(ikePayloadList.get(i + 1).payloadType, byteBuffer); + } + ikePayloadList + .get(ikePayloadList.size() - 1) + .encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, byteBuffer); + + return byteBuffer.array(); + } + + /** Package */ + @VisibleForTesting + byte[] attachEncodedHeader(byte[] encodedIkeBody) { + ByteBuffer outputBuffer = + ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH + encodedIkeBody.length); + ikeHeader.encodeToByteBuffer(outputBuffer, encodedIkeBody.length); + outputBuffer.put(encodedIkeBody); + return outputBuffer.array(); + } + + @MessageType + public int getMessageType() { + return sIkeMessageHelper.getMessageType(this); + } + + /** + * IIkeMessageHelper provides interface for decoding, encoding and processing IKE packet. + * + * <p>IkeMessageHelper exists so that the interface is injectable for testing. + */ + @VisibleForTesting + public interface IIkeMessageHelper { + /** + * Check message type of decoded IKE message. + * + * @param ikeMessage IKE message to be checked. + * @return message type. + */ + @MessageType + int getMessageType(IkeMessage ikeMessage); + + /** + * Encode IKE message. + * + * @param ikeMessage message need to be encoded. + * @return encoded IKE message in byte array. + */ + byte[] encode(IkeMessage ikeMessage); + + /** + * Encrypt and encode IKE message. + * + * @param ikeSessionOptions ikeSessionOptions that contains cryptographic algorithm set. + * @param ikeSaRecord ikeSaRecord where this packet is sent on. + * @param ikeMessage message need to be encoded. + * @return encoded IKE message in byte array. + */ + byte[] encode( + IkeSessionOptions ikeSessionOptions, + IkeSaRecord ikeSaRecord, + IkeMessage ikeMessage); + + /** + * Decode unencrypted packet. + * + * @param ikeHeader header of IKE packet. + * @param packet IKE packet as a byte array. + * @return decoded IKE message. + * @throws IkeException for decoding errors. + */ + IkeMessage decode(IkeHeader ikeHeader, byte[] packet) throws IkeException; + + /** + * Decrypt and decode packet. + * + * @param ikeSessionOptions ikeSessionOptions that contains cryptographic algorithm set. + * @param ikeSaRecord ikeSaRecord where this packet is sent on. + * @param ikeHeader header of IKE packet. + * @param packet IKE packet as a byte array. + * @return decoded IKE message. + * @throws IkeException for decoding errors. + */ + IkeMessage decode( + IkeSessionOptions ikeSessionOptions, + IkeSaRecord ikeSaRecord, + IkeHeader ikeHeader, + byte[] packet) + throws IkeException, GeneralSecurityException; + } + + /** IkeMessageHelper provides methods for decoding, encoding and processing IKE packet. */ + public static final class IkeMessageHelper implements IIkeMessageHelper { + @Override + public byte[] encode(IkeMessage ikeMessage) { + byte[] encodedIkeBody = ikeMessage.encodePayloads(); + return ikeMessage.attachEncodedHeader(encodedIkeBody); + } + + @Override + public byte[] encode( + IkeSessionOptions ikeSessionOptions, + IkeSaRecord ikeSaRecord, + IkeMessage ikeMessage) { + // TODO: Extract crypto attributes and call encrypt() + return null; + } + + //TODO: Create and use a container class for crypto algorithms and keys. + private byte[] encryptAndEncode( + IkeHeader ikeHeader, + @PayloadType int firstPayload, + byte[] unencryptedPayloads, + Mac integrityMac, + int checksumLen, + Cipher encryptCipher, + SecretKey eKey) { + IkeSkPayload skPayload = + new IkeSkPayload( + ikeHeader, + firstPayload, + unencryptedPayloads, + integrityMac, + checksumLen, + encryptCipher, + eKey); + + ByteBuffer outputBuffer = + ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH + skPayload.getPayloadLength()); + ikeHeader.encodeToByteBuffer(outputBuffer, skPayload.getPayloadLength()); + skPayload.encodeToByteBuffer(firstPayload, outputBuffer); + + return outputBuffer.array(); + } + + @Override + public IkeMessage decode(IkeHeader header, byte[] inputPacket) throws IkeException { + header.checkInboundValidOrThrow(inputPacket.length); + + byte[] unencryptedPayloads = + Arrays.copyOfRange( + inputPacket, IkeHeader.IKE_HEADER_LENGTH, inputPacket.length); + + try { + List<IkePayload> supportedPayloadList = + decodePayloadList( + header.nextPayloadType, header.isResponseMsg, unencryptedPayloads); + return new IkeMessage(header, supportedPayloadList); + } catch (NegativeArraySizeException | BufferUnderflowException e) { + // Invalid length error when parsing payload bodies. + throw new InvalidSyntaxException("Malformed IKE Payload"); + } + } + + @Override + public IkeMessage decode( + IkeSessionOptions ikeSessionOptions, + IkeSaRecord ikeSaRecord, + IkeHeader ikeHeader, + byte[] packet) + throws IkeException, GeneralSecurityException { + // TODO: Extract crypto params and call private decode method. + return null; + } + + private IkeMessage decode( + IkeHeader header, + byte[] inputPacket, + Mac integrityMac, + int checksumLen, + Cipher decryptCipher, + SecretKey dKey) + throws IkeException, GeneralSecurityException { + + header.checkInboundValidOrThrow(inputPacket.length); + + if (header.nextPayloadType != IkePayload.PAYLOAD_TYPE_SK) { + // TODO: b/123372339 Handle message containing unprotected payloads. + throw new UnsupportedOperationException("Message contains unprotected payloads"); + } + + try { + Pair<IkeSkPayload, Integer> pair = + IkePayloadFactory.getIkeSkPayload( + inputPacket, integrityMac, checksumLen, decryptCipher, dKey); + IkeSkPayload skPayload = pair.first; + int firstPayloadType = pair.second; + + List<IkePayload> supportedPayloadList = + decodePayloadList( + firstPayloadType, + header.isResponseMsg, + skPayload.getUnencryptedPayloads()); + + return new IkeMessage(header, supportedPayloadList); + } catch (NegativeArraySizeException | BufferUnderflowException e) { + // Invalid length error when parsing payload bodies. + throw new InvalidSyntaxException("Malformed IKE Payload"); + } + } + + @Override + @MessageType + public int getMessageType(IkeMessage ikeMessage) { + // TODO: Implement it. + return 0; + } + } + + /** + * For setting mocked IIkeMessageHelper for testing + * + * @param helper the mocked IIkeMessageHelper + */ + public static void setIkeMessageHelper(IIkeMessageHelper helper) { + sIkeMessageHelper = helper; + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeNoncePayload.java b/src/java/com/android/ike/ikev2/message/IkeNoncePayload.java index 70fc824d..3792e938 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeNoncePayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeNoncePayload.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import java.nio.ByteBuffer; import java.security.SecureRandom; @@ -53,7 +52,7 @@ public final class IkeNoncePayload extends IkePayload { * @param critical indicates if it is a critical payload. * @param payloadBody the nonce data */ - IkeNoncePayload(boolean critical, byte[] payloadBody) throws IkeProtocolException { + IkeNoncePayload(boolean critical, byte[] payloadBody) throws IkeException { super(PAYLOAD_TYPE_NONCE, critical); if (payloadBody.length < MIN_NONCE_LEN || payloadBody.length > MAX_NONCE_LEN) { throw new InvalidSyntaxException( @@ -103,6 +102,6 @@ public final class IkeNoncePayload extends IkePayload { */ @Override public String getTypeString() { - return "Nonce"; + return "Nonce Payload"; } } diff --git a/src/java/com/android/ike/ikev2/message/IkeNotifyPayload.java b/src/java/com/android/ike/ikev2/message/IkeNotifyPayload.java new file mode 100644 index 00000000..ee6fe47a --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeNotifyPayload.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import android.annotation.IntDef; +import android.util.ArraySet; + +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Set; + +/** + * IkeNotifyPayload represents a Notify Payload. + * + * <p>As instructed by RFC 7296, for IKE SA concerned Notify Payload, Protocol ID and SPI Size must + * be zero. Unrecognized notify message type must be ignored but should be logged. + * + * <p>Critical bit for this payload must be ignored in received packet and must not be set in + * outbound packet. + * + * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol + * Version 2 (IKEv2)</a> + */ +public final class IkeNotifyPayload extends IkePayload { + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + NOTIFY_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, + NOTIFY_TYPE_INVALID_MAJOR_VERSION, + NOTIFY_TYPE_INVALID_SYNTAX, + NOTIFY_TYPE_NO_PROPOSAL_CHOSEN, + NOTIFY_TYPE_INVALID_SELECTORS, + NOTIFY_TYPE_CHILD_SA_NOT_FOUND, + NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, + NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, + NOTIFY_TYPE_REKEY_SA + }) + public @interface NotifyType {} + + public static final int NOTIFY_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = 1; + public static final int NOTIFY_TYPE_INVALID_MAJOR_VERSION = 5; + public static final int NOTIFY_TYPE_INVALID_SYNTAX = 7; + public static final int NOTIFY_TYPE_NO_PROPOSAL_CHOSEN = 14; + public static final int NOTIFY_TYPE_AUTHENTICATION_FAILED = 24; + public static final int NOTIFY_TYPE_INVALID_SELECTORS = 39; + public static final int NOTIFY_TYPE_CHILD_SA_NOT_FOUND = 44; + + public static final int NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP = 16388; + public static final int NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP = 16389; + public static final int NOTIFY_TYPE_REKEY_SA = 16393; + // TODO: List all supported notify types. + + private static final int NOTIFY_HEADER_LEN = 4; + + private static final String NAT_DETECTION_DIGEST_ALGORITHM = "SHA-1"; + + private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_CHILD_SA; + + static { + VALID_NOTIFY_TYPES_FOR_CHILD_SA = new ArraySet<>(); + VALID_NOTIFY_TYPES_FOR_CHILD_SA.add(NOTIFY_TYPE_INVALID_SELECTORS); + VALID_NOTIFY_TYPES_FOR_CHILD_SA.add(NOTIFY_TYPE_CHILD_SA_NOT_FOUND); + VALID_NOTIFY_TYPES_FOR_CHILD_SA.add(NOTIFY_TYPE_REKEY_SA); + } + + public final int protocolId; + public final byte spiSize; + public final int notifyType; + public final int spi; + public final byte[] notifyData; + + /** + * Construct an instance of IkeNotifyPayload in the context of IkePayloadFactory + * + * @param critical indicates if this payload is critical. Ignored in supported payload as + * instructed by the RFC 7296. + * @param payloadBody payload body in byte array + * @throws IkeException if there is any error + */ + IkeNotifyPayload(boolean isCritical, byte[] payloadBody) throws IkeException { + super(PAYLOAD_TYPE_NOTIFY, isCritical); + + ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); + + protocolId = Byte.toUnsignedInt(inputBuffer.get()); + spiSize = inputBuffer.get(); + notifyType = Short.toUnsignedInt(inputBuffer.getShort()); + + // Validate syntax of spiSize, protocolId and notifyType. + // Reference: <https://tools.ietf.org/html/rfc7296#page-100> + if (spiSize == SPI_LEN_IPSEC) { + // For message concerning existing Child SA + validateNotifyPayloadForExistingChildSa(); + spi = inputBuffer.getInt(); + + } else if (spiSize == SPI_LEN_NOT_INCLUDED) { + // For message concerning IKE SA or for new Child SA that to be negotiated. + validateNotifyPayloadForIkeAndNewChild(); + spi = SPI_NOT_INCLUDED; + + } else { + throw new InvalidSyntaxException("Invalid SPI Size: " + spiSize); + } + + notifyData = new byte[payloadBody.length - NOTIFY_HEADER_LEN]; + inputBuffer.get(notifyData); + } + + private void validateNotifyPayloadForExistingChildSa() throws InvalidSyntaxException { + if (protocolId != PROTOCOL_ID_AH && protocolId != PROTOCOL_ID_ESP) { + throw new InvalidSyntaxException( + "Expected Procotol ID AH(2) or ESP(3): Protocol ID is " + protocolId); + } + + if (!VALID_NOTIFY_TYPES_FOR_CHILD_SA.contains(notifyType)) { + throw new InvalidSyntaxException( + "Expected Notify Type for existing Child SA: Notify Type is " + notifyType); + } + } + + private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException { + if (protocolId != PROTOCOL_ID_UNSET) { + throw new InvalidSyntaxException( + "Expected Procotol ID unset: Protocol ID is " + protocolId); + } + + if (notifyType == NOTIFY_TYPE_INVALID_SELECTORS + || notifyType == NOTIFY_TYPE_CHILD_SA_NOT_FOUND) { + throw new InvalidSyntaxException( + "Expected Notify Type concerning IKE SA or new Child SA under negotiation" + + ": Notify Type is " + + notifyType); + } + } + + /** + * Generate NAT DETECTION notification data. + * + * <p>This method calculates NAT DETECTION notification data which is a SHA-1 digest of the IKE + * initiator's SPI, IKE responder's SPI, IP address and port. Source address and port should be + * used for generating NAT_DETECTION_SOURCE_IP data. Destination address and port should be used + * for generating NAT_DETECTION_DESTINATION_IP data. + * + * @param initiatorIkeSpi the SPI of IKE initiator + * @param responderIkeSpi the SPI of IKE responder + * @param ipAddress the IP address + * @param port the port + * @return the generated NAT DETECTION notification data as a byte array. + * @throws NoSuchAlgorithmException when "SHA-1" is not supported by the security provider. + */ + public static byte[] generateNatDetectionData( + long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port) + throws NoSuchAlgorithmException { + byte[] rawIpAddr = ipAddress.getAddress(); + + ByteBuffer byteBuffer = + ByteBuffer.allocate(2 * SPI_LEN_IKE + rawIpAddr.length + IP_PORT_LEN); + byteBuffer + .putLong(initiatorIkeSpi) + .putLong(responderIkeSpi) + .put(rawIpAddr) + .putShort((short) port); + + MessageDigest natDetectionDataDigest = + MessageDigest.getInstance( + NAT_DETECTION_DIGEST_ALGORITHM, IkeMessage.getSecurityProvider()); + return natDetectionDataDigest.digest(byteBuffer.array()); + } + + /** + * Encode Notify payload to ByteBuffer. + * + * @param nextPayload type of payload that follows this payload. + * @param byteBuffer destination ByteBuffer that stores encoded payload. + */ + @Override + protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { + encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); + byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) notifyType); + if (spiSize == SPI_LEN_IPSEC) { + byteBuffer.putInt(spi); + } + byteBuffer.put(notifyData); + } + + /** + * Get entire payload length. + * + * @return entire payload length. + */ + @Override + protected int getPayloadLength() { + return GENERIC_HEADER_LENGTH + NOTIFY_HEADER_LEN + spiSize + notifyData.length; + } + + protected IkeNotifyPayload( + @ProtocolId int protocolId, + byte spiSize, + int spi, + @NotifyType int notifyType, + byte[] notifyData) { + super(PAYLOAD_TYPE_NOTIFY, false); + this.protocolId = protocolId; + this.spiSize = spiSize; + this.spi = spi; + this.notifyType = notifyType; + this.notifyData = notifyData; + } + + /** + * Construct IkeNotifyPayload concerning either an IKE SA or Child SA that is going to be + * negotiated. + * + * @param notifyType the notify type concerning IKE SA + * @param notifytData status or error data transmitted. Values for this field are notify type + * specific. + */ + public IkeNotifyPayload(@NotifyType int notifyType, byte[] notifyData) { + this(PROTOCOL_ID_UNSET, SPI_LEN_NOT_INCLUDED, SPI_NOT_INCLUDED, notifyType, notifyData); + try { + validateNotifyPayloadForIkeAndNewChild(); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Construct IkeNotifyPayload concerning existing Child SA + * + * @param notifyType the notify type concerning Child SA + * @param notifytData status or error data transmitted. Values for this field are notify type + * specific. + */ + public IkeNotifyPayload( + @ProtocolId int protocolId, int spi, @NotifyType int notifyType, byte[] notifyData) { + this(protocolId, SPI_LEN_IPSEC, spi, notifyType, notifyData); + try { + validateNotifyPayloadForExistingChildSa(); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Return the payload type as a String. + * + * @return the payload type as a String. + */ + @Override + public String getTypeString() { + return "Notify Payload"; + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkePayload.java b/src/java/com/android/ike/ikev2/message/IkePayload.java index 9ea54c14..2474bb64 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkePayload.java +++ b/src/java/com/android/ike/ikev2/message/IkePayload.java @@ -14,16 +14,13 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import android.annotation.IntDef; -import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; -import java.util.LinkedList; -import java.util.List; /** * IkePayload is an abstract class that represents the common information for all IKE payload types. @@ -51,7 +48,6 @@ public abstract class IkePayload { PAYLOAD_TYPE_SA, PAYLOAD_TYPE_KE, PAYLOAD_TYPE_CERT, - PAYLOAD_TYPE_CERT_REQUEST, PAYLOAD_TYPE_AUTH, PAYLOAD_TYPE_ID_INITIATOR, PAYLOAD_TYPE_ID_RESPONDER, @@ -61,10 +57,7 @@ public abstract class IkePayload { PAYLOAD_TYPE_VENDOR, PAYLOAD_TYPE_TS_INITIATOR, PAYLOAD_TYPE_TS_RESPONDER, - PAYLOAD_TYPE_SK, - PAYLOAD_TYPE_CP, - PAYLOAD_TYPE_EAP, - PAYLOAD_TYPE_SKF + PAYLOAD_TYPE_SK }) public @interface PayloadType {} @@ -78,10 +71,8 @@ public abstract class IkePayload { public static final int PAYLOAD_TYPE_ID_INITIATOR = 35; /** Identification Payload for IKE SA Responder */ public static final int PAYLOAD_TYPE_ID_RESPONDER = 36; - /** Certificate Payload */ + /** Certification Payload */ public static final int PAYLOAD_TYPE_CERT = 37; - /** Certificate Request Payload */ - public static final int PAYLOAD_TYPE_CERT_REQUEST = 38; /** Authentication Payload */ public static final int PAYLOAD_TYPE_AUTH = 39; /** Nonce Payload */ @@ -98,12 +89,6 @@ public abstract class IkePayload { public static final int PAYLOAD_TYPE_TS_RESPONDER = 45; /** Encrypted and Authenticated Payload */ public static final int PAYLOAD_TYPE_SK = 46; - /** Configuration Payload */ - public static final int PAYLOAD_TYPE_CP = 47; - /** EAP Payload */ - public static final int PAYLOAD_TYPE_EAP = 48; - /** Encrypted and Authenticated Fragment */ - public static final int PAYLOAD_TYPE_SKF = 53; // TODO: List all payload types. @@ -121,16 +106,6 @@ public abstract class IkePayload { public static final int PROTOCOL_ID_AH = 2; public static final int PROTOCOL_ID_ESP = 3; - private static final SparseArray<String> PROTOCOL_TO_STR; - - static { - PROTOCOL_TO_STR = new SparseArray<>(); - PROTOCOL_TO_STR.put(PROTOCOL_ID_UNSET, "Protocol Unset"); - PROTOCOL_TO_STR.put(PROTOCOL_ID_IKE, "IKE"); - PROTOCOL_TO_STR.put(PROTOCOL_ID_AH, "AH"); - PROTOCOL_TO_STR.put(PROTOCOL_ID_ESP, "ESP"); - } - public static final byte SPI_LEN_NOT_INCLUDED = 0; public static final byte SPI_LEN_IPSEC = 4; public static final byte SPI_LEN_IKE = 8; @@ -159,56 +134,6 @@ public abstract class IkePayload { } /** - * A helper method to quickly obtain payloads with the input payload type in the provided - * payload list. - * - * <p>This method will not check if this payload type can be repeatable in an IKE message - * because it does not know the context of the provided payload list. Caller should call this - * method if they are expecting more than one payloads in the list. - * - * @param payloadType the payloadType to look for. - * @param payloadClass the class of the desired payload. - * @param searchList the payload list to do the search. - * @return a list of IkePayloads with the payloadType. - */ - public static <T extends IkePayload> List<T> getPayloadListForTypeInProvidedList( - @IkePayload.PayloadType int payloadType, - Class<T> payloadClass, - List<IkePayload> searchList) { - List<T> payloadList = new LinkedList<>(); - - for (IkePayload payload : searchList) { - if (payloadType == payload.payloadType) { - payloadList.add(payloadClass.cast(payload)); - } - } - - return payloadList; - } - - /** - * A helper method to quickly obtain the payload with the input payload type in the provided - * payload list. - * - * <p>This method will not check if this payload type can be repeatable in an IKE message - * because it does not know the context of the provided payload list. Caller should call this - * method if they are expecting no more than one payloads in the list. - * - * @param payloadType the payloadType to look for. - * @param payloadClass the class of the desired payload. - * @param searchList the payload list to do the search. - * @return the IkePayload with the payloadType. - */ - public static <T extends IkePayload> T getPayloadForTypeInProvidedList( - @IkePayload.PayloadType int payloadType, - Class<T> payloadClass, - List<IkePayload> searchList) { - List<T> payloadList = - getPayloadListForTypeInProvidedList(payloadType, payloadClass, searchList); - return payloadList.isEmpty() ? null : payloadList.get(0); - } - - /** * Encode generic payload header to ByteBuffer. * * @param nextPayload type of payload that follows this payload. @@ -223,11 +148,6 @@ public abstract class IkePayload { .putShort((short) payloadLength); } - /** Retuns protocol type as String. */ - public static String getProtocolTypeString(@ProtocolId int protocol) { - return PROTOCOL_TO_STR.get(protocol); - } - /** * Encode payload to ByteBuffer. * diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java b/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java index 2f191d4e..bea87cad 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java +++ b/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java @@ -14,20 +14,21 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import android.annotation.Nullable; -import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.util.Pair; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; + /** * IkePayloadFactory is used for creating IkePayload according to is type. * @@ -58,7 +59,7 @@ final class IkePayloadFactory { @Override public IkePayload decodeIkePayload( int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody) - throws IkeProtocolException { + throws IkeException { switch (payloadType) { // TODO: Add cases for creating supported payloads. case IkePayload.PAYLOAD_TYPE_SA: @@ -85,43 +86,10 @@ final class IkePayloadFactory { return new IkeTsPayload(isCritical, payloadBody, true); case IkePayload.PAYLOAD_TYPE_TS_RESPONDER: return new IkeTsPayload(isCritical, payloadBody, false); - case IkePayload.PAYLOAD_TYPE_CP: - return new IkeConfigPayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_EAP: - return new IkeEapPayload(isCritical, payloadBody); default: return new IkeUnsupportedPayload(payloadType, isCritical); } } - - @Override - public IkeSkPayload decodeIkeSkPayload( - boolean isSkf, - boolean critical, - byte[] message, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException { - if (isSkf) { - return new IkeSkfPayload( - critical, - message, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - } else { - return new IkeSkPayload( - critical, - message, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - } - } } /** @@ -134,7 +102,7 @@ final class IkePayloadFactory { * @return a Pair including IkePayload and next payload type. */ protected static Pair<IkePayload, Integer> getIkePayload( - int payloadType, boolean isResp, ByteBuffer input) throws IkeProtocolException { + int payloadType, boolean isResp, ByteBuffer input) throws IkeException { int nextPayloadType = (int) input.get(); // read critical bit boolean isCritical = isCriticalPayload(input.get()); @@ -161,24 +129,22 @@ final class IkePayloadFactory { /** * Construct an instance of IkeSkPayload by decrypting the received message. * - * @param isSkf indicates if this is a SKF Payload. * @param message the byte array contains the whole IKE message. - * @param integrityMac the negotiated integrity algorithm. - * @param decryptCipher the negotiated encryption algorithm. - * @param integrityKey the negotiated integrity algorithm key. - * @param decryptionKey the negotiated decryption key. + * @param integrityMac the initialized Mac for integrity check. + * @param checksumLen the checksum length of negotiated integrity algorithm. + * @param decryptCipher the uninitialized Cipher for doing decryption. + * @param dKey the decryption key. * @return a pair including IkePayload and next payload type. - * @throws IkeProtocolException for decoding errors. + * @throws IkeException for decoding errors. * @throws GeneralSecurityException if there is any error during integrity check or decryption. */ protected static Pair<IkeSkPayload, Integer> getIkeSkPayload( - boolean isSkf, byte[] message, - IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException { + Mac integrityMac, + int checksumLen, + Cipher decryptCipher, + SecretKey dKey) + throws IkeException, GeneralSecurityException { ByteBuffer input = ByteBuffer.wrap( message, @@ -207,15 +173,13 @@ final class IkePayloadFactory { } IkeSkPayload payload = - sDecoderInstance.decodeIkeSkPayload( - isSkf, + new IkeSkPayload( isCritical, message, integrityMac, + checksumLen, decryptCipher, - integrityKey, - decryptionKey); - + dKey); return new Pair(payload, nextPayloadType); } @@ -229,16 +193,6 @@ final class IkePayloadFactory { interface IIkePayloadDecoder { IkePayload decodeIkePayload( int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody) - throws IkeProtocolException; - - IkeSkPayload decodeIkeSkPayload( - boolean isSkf, - boolean critical, - byte[] message, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException; + throws IkeException; } } diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java b/src/java/com/android/ike/ikev2/message/IkeSaPayload.java index 3cce6255..c0651ae0 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeSaPayload.java @@ -14,36 +14,25 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; -import static android.net.ipsec.ike.IkeManager.getIkeLog; -import static android.net.ipsec.ike.SaProposal.DhGroup; -import static android.net.ipsec.ike.SaProposal.EncryptionAlgorithm; -import static android.net.ipsec.ike.SaProposal.IntegrityAlgorithm; -import static android.net.ipsec.ike.SaProposal.PseudorandomFunction; +import static com.android.ike.ikev2.SaProposal.DhGroup; +import static com.android.ike.ikev2.SaProposal.EncryptionAlgorithm; +import static com.android.ike.ikev2.SaProposal.IntegrityAlgorithm; +import static com.android.ike.ikev2.SaProposal.PseudorandomFunction; import android.annotation.IntDef; -import android.annotation.NonNull; -import android.net.IpSecManager; -import android.net.IpSecManager.ResourceUnavailableException; -import android.net.IpSecManager.SecurityParameterIndex; -import android.net.IpSecManager.SpiUnavailableException; -import android.net.ipsec.ike.ChildSaProposal; -import android.net.ipsec.ike.IkeSaProposal; -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.util.ArraySet; import android.util.Pair; +import com.android.ike.ikev2.SaProposal; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.NoValidProposalChosenException; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IkeSecurityParameterIndex; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.LinkedList; @@ -58,8 +47,6 @@ import java.util.Set; * Protocol Version 2 (IKEv2)</a> */ public final class IkeSaPayload extends IkePayload { - private static final String TAG = "IkeSaPayload"; - public final boolean isSaResponse; public final List<Proposal> proposalList; /** @@ -70,7 +57,7 @@ public final class IkeSaPayload extends IkePayload { * @param isResp indicates if this payload is in a response message. * @param payloadBody the encoded payload body in byte array. */ - IkeSaPayload(boolean critical, boolean isResp, byte[] payloadBody) throws IkeProtocolException { + IkeSaPayload(boolean critical, boolean isResp, byte[] payloadBody) throws IkeException { super(IkePayload.PAYLOAD_TYPE_SA, critical); ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); @@ -80,10 +67,6 @@ public final class IkeSaPayload extends IkePayload { proposalList.add(proposal); } - if (proposalList.isEmpty()) { - throw new InvalidSyntaxException("Found no SA Proposal in this SA Payload."); - } - // An SA response must have exactly one SA proposal. if (isResp && proposalList.size() != 1) { throw new InvalidSyntaxException( @@ -91,393 +74,113 @@ public final class IkeSaPayload extends IkePayload { + "Multiple negotiated proposals found."); } isSaResponse = isResp; - - boolean firstIsIkeProposal = (proposalList.get(0).protocolId == PROTOCOL_ID_IKE); - for (int i = 1; i < proposalList.size(); i++) { - boolean isIkeProposal = (proposalList.get(i).protocolId == PROTOCOL_ID_IKE); - if (firstIsIkeProposal != isIkeProposal) { - getIkeLog() - .w(TAG, "Found both IKE proposals and Child proposals in this SA Payload."); - break; - } - } - - getIkeLog().d(TAG, "Receive " + toString()); - } - - /** Package private constructor for building a request for IKE SA initial creation or rekey */ - @VisibleForTesting - IkeSaPayload( - boolean isResp, byte spiSize, IkeSaProposal[] saProposals, InetAddress localAddress) - throws IOException { - this(isResp, spiSize, localAddress); - - if (saProposals.length < 1 || isResp && (saProposals.length > 1)) { - throw new IllegalArgumentException("Invalid SA payload."); - } - - for (int i = 0; i < saProposals.length; i++) { - // Proposal number must start from 1. - proposalList.add( - IkeProposal.createIkeProposal( - (byte) (i + 1) /*number*/, spiSize, saProposals[i], localAddress)); - } - - getIkeLog().d(TAG, "Generate " + toString()); - } - - /** Package private constructor for building an response SA Payload for IKE SA rekeys. */ - @VisibleForTesting - IkeSaPayload( - boolean isResp, - byte spiSize, - byte proposalNumber, - IkeSaProposal saProposal, - InetAddress localAddress) - throws IOException { - this(isResp, spiSize, localAddress); - - proposalList.add( - IkeProposal.createIkeProposal( - proposalNumber /*number*/, spiSize, saProposal, localAddress)); - - getIkeLog().d(TAG, "Generate " + toString()); - } - - private IkeSaPayload(boolean isResp, byte spiSize, InetAddress localAddress) - throws IOException { - super(IkePayload.PAYLOAD_TYPE_SA, false); - - // TODO: Check that proposals.length <= 255 in IkeSessionOptions and ChildSessionOptions - isSaResponse = isResp; - - // TODO: Allocate IKE SPI and pass to IkeProposal.createIkeProposal() - - // ProposalList populated in other constructors - proposalList = new ArrayList<Proposal>(); } /** - * Package private constructor for building an outbound request SA Payload for Child SA - * negotiation. + * Construct an instance of IkeSaPayload for building outbound packet. + * + * <p>The length of spis must be the same as saProposals. + * + * @param isResp indicates if this payload is in a response message. + * @param isIkeSa indicates if this payload is for IKE SA or Child SA + * @param spiSize the size of attached SPIs. + * @param spis the array of all attached SPIs. + * @param saProposals the array of all SA Proposals. */ - @VisibleForTesting - IkeSaPayload(ChildSaProposal[] saProposals, IpSecManager ipSecManager, InetAddress localAddress) - throws ResourceUnavailableException { - this(false /*isResp*/, ipSecManager, localAddress); + public IkeSaPayload( + boolean isResp, boolean isIkeSa, byte spiSize, long[] spis, SaProposal[] saProposals) { + super(IkePayload.PAYLOAD_TYPE_SA, false); - if (saProposals.length < 1) { + if (saProposals.length < 1 + || isResp && (saProposals.length > 1) + || saProposals.length != spis.length) { throw new IllegalArgumentException("Invalid SA payload."); } // TODO: Check that saProposals.length <= 255 in IkeSessionOptions and ChildSessionOptions + isSaResponse = isResp; + proposalList = new ArrayList<Proposal>(saProposals.length); + int protocolId = isIkeSa ? PROTOCOL_ID_IKE : PROTOCOL_ID_ESP; for (int i = 0; i < saProposals.length; i++) { // Proposal number must start from 1. - proposalList.add( - ChildProposal.createChildProposal( - (byte) (i + 1) /*number*/, saProposals[i], ipSecManager, localAddress)); + Proposal proposal = + new Proposal( + (byte) (i + 1) /*proposal number*/, + protocolId, + spiSize, + spis[i], + saProposals[i], + false /*does not have unrecognized Transform*/); + proposalList.add(proposal); } - - getIkeLog().d(TAG, "Generate " + toString()); } /** - * Package private constructor for building an outbound response SA Payload for Child SA - * negotiation. - */ - @VisibleForTesting - IkeSaPayload( - byte proposalNumber, - ChildSaProposal saProposal, - IpSecManager ipSecManager, - InetAddress localAddress) - throws ResourceUnavailableException { - this(true /*isResp*/, ipSecManager, localAddress); - - proposalList.add( - ChildProposal.createChildProposal( - proposalNumber /*number*/, saProposal, ipSecManager, localAddress)); - - getIkeLog().d(TAG, "Generate " + toString()); - } - - /** Constructor for building an outbound SA Payload for Child SA negotiation. */ - private IkeSaPayload(boolean isResp, IpSecManager ipSecManager, InetAddress localAddress) { - super(IkePayload.PAYLOAD_TYPE_SA, false); - - isSaResponse = isResp; - - // TODO: Allocate Child SPI and pass to ChildProposal.createChildProposal() - - // ProposalList populated in other constructors - proposalList = new ArrayList<Proposal>(); - } - - /** - * Construct an instance of IkeSaPayload for building an outbound IKE initial setup request. + * Construct an instance of IkeSaPayload for building outbound IKE initial setup request. * * <p>According to RFC 7296, for an initial IKE SA negotiation, no SPI is included in SA * Proposal. IKE library, as a client, only supports requesting this initial negotiation. * * @param saProposals the array of all SA Proposals. */ - public static IkeSaPayload createInitialIkeSaPayload(IkeSaProposal[] saProposals) - throws IOException { - return new IkeSaPayload(false /*isResp*/, SPI_LEN_NOT_INCLUDED, saProposals, null); + public IkeSaPayload(SaProposal[] saProposals) { + this( + false /*is request*/, + true /*is IKE SA*/, + (byte) 0, + new long[saProposals.length], + saProposals); } /** - * Construct an instance of IkeSaPayload for building an outbound request for Rekey IKE. + * Validate and return the negotiated SA proposal from the received SA payload. * - * @param saProposals the array of all IKE SA Proposals. - * @param localAddress the local address assigned on-device. + * @param reqSaPayload SA payload from SA initiator to validate against. + * @return the validated negotiated SA proposal. + * @throws NoValidProposalChosenException if received SA proposal is invalid. */ - public static IkeSaPayload createRekeyIkeSaRequestPayload( - IkeSaProposal[] saProposals, InetAddress localAddress) throws IOException { - return new IkeSaPayload(false /*isResp*/, SPI_LEN_IKE, saProposals, localAddress); - } - - /** - * Construct an instance of IkeSaPayload for building an outbound response for Rekey IKE. - * - * @param respProposalNumber the selected proposal's number. - * @param saProposal the expected selected IKE SA Proposal. - * @param localAddress the local address assigned on-device. - */ - public static IkeSaPayload createRekeyIkeSaResponsePayload( - byte respProposalNumber, IkeSaProposal saProposal, InetAddress localAddress) - throws IOException { - return new IkeSaPayload( - true /*isResp*/, SPI_LEN_IKE, respProposalNumber, saProposal, localAddress); - } - - /** - * Construct an instance of IkeSaPayload for building an outbound request for Child SA - * negotiation. - * - * @param saProposals the array of all Child SA Proposals. - * @param ipSecManager the IpSecManager for generating IPsec SPIs. - * @param localAddress the local address assigned on-device. - * @throws ResourceUnavailableException if too many SPIs are currently allocated for this user. - */ - public static IkeSaPayload createChildSaRequestPayload( - ChildSaProposal[] saProposals, IpSecManager ipSecManager, InetAddress localAddress) - throws ResourceUnavailableException { - - return new IkeSaPayload(saProposals, ipSecManager, localAddress); - } - - /** - * Construct an instance of IkeSaPayload for building an outbound response for Child SA - * negotiation. - * - * @param respProposalNumber the selected proposal's number. - * @param saProposal the expected selected Child SA Proposal. - * @param ipSecManager the IpSecManager for generating IPsec SPIs. - * @param localAddress the local address assigned on-device. - */ - public static IkeSaPayload createChildSaResponsePayload( - byte respProposalNumber, - ChildSaProposal saProposal, - IpSecManager ipSecManager, - InetAddress localAddress) - throws ResourceUnavailableException { - return new IkeSaPayload(respProposalNumber, saProposal, ipSecManager, localAddress); - } - - /** - * Finds the proposal in this (request) payload that matches the response proposal. - * - * @param respProposal the Proposal to match against. - * @return the byte-value proposal number of the selected proposal - * @throws NoValidProposalChosenException if no matching proposal was found. - */ - public byte getNegotiatedProposalNumber(SaProposal respProposal) + public SaProposal getVerifiedNegotiatedProposal(IkeSaPayload reqSaPayload) throws NoValidProposalChosenException { - for (int i = 0; i < proposalList.size(); i++) { - Proposal reqProposal = proposalList.get(i); - if (respProposal.isNegotiatedFrom(reqProposal.getSaProposal()) - && reqProposal.getSaProposal().getProtocolId() - == respProposal.getProtocolId()) { - return reqProposal.number; - } + if (!isSaResponse) { + throw new UnsupportedOperationException( + "Cannot get negotiated SA proposal from a request message."); } - throw new NoValidProposalChosenException("No remotely proposed protocol acceptable"); - } - - /** - * Validate the IKE SA Payload pair (request/response) and return the IKE SA negotiation result. - * - * <p>Caller is able to extract the negotiated IKE SA Proposal from the response Proposal and - * the IKE SPI pair generated by both sides. - * - * <p>In a locally-initiated case all IKE SA proposals (from users in initial creation or from - * previously negotiated proposal in rekey creation) in the locally generated reqSaPayload have - * been validated during building and are unmodified. All Transform combinations in these SA - * proposals are valid for IKE SA negotiation. It means each IKE SA request proposal MUST have - * Encryption algorithms, DH group configurations and PRFs. Integrity algorithms can only be - * omitted when AEAD is used. - * - * <p>In a remotely-initiated case the locally generated respSaPayload has exactly one SA - * proposal. It is validated during building and are unmodified. This proposal has a valid - * Transform combination for an IKE SA and has at most one value for each Transform type. - * - * <p>The response IKE SA proposal is validated against one of the request IKE SA proposals. It - * is guaranteed that for each Transform type that the request proposal has provided options, - * the response proposal has exact one Transform value. - * - * @param reqSaPayload the request payload. - * @param respSaPayload the response payload. - * @param remoteAddress the address of the remote IKE peer. - * @return the Pair of selected IkeProposal in request and the IkeProposal in response. - * @throws NoValidProposalChosenException if the response SA Payload cannot be negotiated from - * the request SA Payload. - */ - public static Pair<IkeProposal, IkeProposal> getVerifiedNegotiatedIkeProposalPair( - IkeSaPayload reqSaPayload, IkeSaPayload respSaPayload, InetAddress remoteAddress) - throws NoValidProposalChosenException, IOException { - Pair<Proposal, Proposal> proposalPair = - getVerifiedNegotiatedProposalPair(reqSaPayload, respSaPayload); - IkeProposal reqProposal = (IkeProposal) proposalPair.first; - IkeProposal respProposal = (IkeProposal) proposalPair.second; - - try { - // Allocate initiator's inbound SPI as needed for remotely initiated IKE SA creation - if (reqProposal.spiSize != SPI_NOT_INCLUDED - && reqProposal.getIkeSpiResource() == null) { - reqProposal.allocateResourceForRemoteIkeSpi(remoteAddress); - } - // Allocate responder's inbound SPI as needed for locally initiated IKE SA creation - if (respProposal.spiSize != SPI_NOT_INCLUDED - && respProposal.getIkeSpiResource() == null) { - respProposal.allocateResourceForRemoteIkeSpi(remoteAddress); - } - return new Pair(reqProposal, respProposal); - } catch (Exception e) { - reqProposal.releaseSpiResourceIfExists(); - respProposal.releaseSpiResourceIfExists(); - throw e; + // If negotiated proposal has an unrecognized Transform, throw an exception. + Proposal respProposal = proposalList.get(0); + if (respProposal.hasUnrecognizedTransform) { + throw new NoValidProposalChosenException( + "Negotiated proposal has unrecognized Transform."); } - } - - /** - * Validate the SA Payload pair (request/response) and return the Child SA negotiation result. - * - * <p>Caller is able to extract the negotiated SA Proposal from the response Proposal and the - * IPsec SPI pair generated by both sides. - * - * <p>In a locally-initiated case all Child SA proposals (from users in initial creation or from - * previously negotiated proposal in rekey creation) in the locally generated reqSaPayload have - * been validated during building and are unmodified. All Transform combinations in these SA - * proposals are valid for Child SA negotiation. It means each request SA proposal MUST have - * Encryption algorithms and ESN configurations. - * - * <p>In a remotely-initiated case the locally generated respSapayload has exactly one SA - * proposal. It is validated during building and are unmodified. This proposal has a valid - * Transform combination for an Child SA and has at most one value for each Transform type. - * - * <p>The response Child SA proposal is validated against one of the request SA proposals. It is - * guaranteed that for each Transform type that the request proposal has provided options, the - * response proposal has exact one Transform value. - * - * @param reqSaPayload the request payload. - * @param respSaPayload the response payload. - * @param ipSecManager the IpSecManager to allocate SPI resource for the Proposal in this - * inbound SA Payload. - * @param remoteAddress the address of the remote IKE peer. - * @return the Pair of selected ChildProposal in the locally generated request and the - * ChildProposal in this response. - * @throws NoValidProposalChosenException if the response SA Payload cannot be negotiated from - * the request SA Payload. - * @throws ResourceUnavailableException if too many SPIs are currently allocated for this user. - * @throws SpiUnavailableException if the remotely generated SPI is in use. - */ - public static Pair<ChildProposal, ChildProposal> getVerifiedNegotiatedChildProposalPair( - IkeSaPayload reqSaPayload, - IkeSaPayload respSaPayload, - IpSecManager ipSecManager, - InetAddress remoteAddress) - throws NoValidProposalChosenException, ResourceUnavailableException, - SpiUnavailableException { - Pair<Proposal, Proposal> proposalPair = - getVerifiedNegotiatedProposalPair(reqSaPayload, respSaPayload); - ChildProposal reqProposal = (ChildProposal) proposalPair.first; - ChildProposal respProposal = (ChildProposal) proposalPair.second; - - try { - // Allocate initiator's inbound SPI as needed for remotely initiated Child SA creation - if (reqProposal.getChildSpiResource() == null) { - reqProposal.allocateResourceForRemoteChildSpi(ipSecManager, remoteAddress); - } - // Allocate responder's inbound SPI as needed for locally initiated Child SA creation - if (respProposal.getChildSpiResource() == null) { - respProposal.allocateResourceForRemoteChildSpi(ipSecManager, remoteAddress); - } - return new Pair(reqProposal, respProposal); - } catch (Exception e) { - reqProposal.releaseSpiResourceIfExists(); - respProposal.releaseSpiResourceIfExists(); - throw e; + // In SA request payload, the first proposal MUST be 1, and subsequent proposals MUST be one + // more than the previous proposal. In SA response payload, the negotiated proposal number + // MUST match the selected proposal number in SA request Payload. + int negotiatedProposalNum = respProposal.number; + List<Proposal> reqProposalList = reqSaPayload.proposalList; + if (negotiatedProposalNum < 1 || negotiatedProposalNum > reqProposalList.size()) { + throw new NoValidProposalChosenException( + "Negotiated proposal has invalid proposal number."); } - } - private static Pair<Proposal, Proposal> getVerifiedNegotiatedProposalPair( - IkeSaPayload reqSaPayload, IkeSaPayload respSaPayload) - throws NoValidProposalChosenException { - try { - // If negotiated proposal has an unrecognized Transform, throw an exception. - Proposal respProposal = respSaPayload.proposalList.get(0); - if (respProposal.hasUnrecognizedTransform) { - throw new NoValidProposalChosenException( - "Negotiated proposal has unrecognized Transform."); - } - - // In SA request payload, the first proposal MUST be 1, and subsequent proposals MUST be - // one more than the previous proposal. In SA response payload, the negotiated proposal - // number MUST match the selected proposal number in SA request Payload. - int negotiatedProposalNum = respProposal.number; - List<Proposal> reqProposalList = reqSaPayload.proposalList; - if (negotiatedProposalNum < 1 || negotiatedProposalNum > reqProposalList.size()) { - throw new NoValidProposalChosenException( - "Negotiated proposal has invalid proposal number."); - } - - Proposal reqProposal = reqProposalList.get(negotiatedProposalNum - 1); - if (!respProposal.isNegotiatedFrom(reqProposal)) { - throw new NoValidProposalChosenException("Invalid negotiated proposal."); - } - - // In a locally-initiated creation, release locally generated SPIs in unselected request - // Proposals. In remotely-initiated SA creation, unused proposals do not have SPIs, and - // will silently succeed. - for (Proposal p : reqProposalList) { - if (reqProposal != p) p.releaseSpiResourceIfExists(); - } - - return new Pair<Proposal, Proposal>(reqProposal, respProposal); - } catch (Exception e) { - // In a locally-initiated case, release all locally generated SPIs in the SA request - // payload. - for (Proposal p : reqSaPayload.proposalList) p.releaseSpiResourceIfExists(); - throw e; + Proposal reqProposal = reqProposalList.get(negotiatedProposalNum - 1); + if (!respProposal.isNegotiatedFrom(reqProposal)) { + throw new NoValidProposalChosenException("Invalid negotiated proposal."); } + return respProposal.saProposal; } @VisibleForTesting interface TransformDecoder { - Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) throws IkeProtocolException; + Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) throws IkeException; } // TODO: Add another constructor for building outbound message. /** - * This class represents the common information of an IKE Proposal and a Child Proposal. - * - * <p>Proposal represents a set contains cryptographic algorithms and key generating materials. - * It contains multiple {@link Transform}. + * Proposal represents a set contains cryptographic algorithms and key generating materials. It + * contains multiple {@link Transform}. * * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.1">RFC 7296, Internet Key * Exchange Protocol Version 2 (IKEv2)</a> @@ -485,7 +188,7 @@ public final class IkeSaPayload extends IkePayload { * or lacking a necessary Transform Type shall be ignored when processing a received SA * Payload. */ - public abstract static class Proposal { + public static final class Proposal { private static final byte LAST_PROPOSAL = 0; private static final byte NOT_LAST_PROPOSAL = 2; @@ -497,7 +200,7 @@ public final class IkeSaPayload extends IkePayload { new TransformDecoder() { @Override public Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) - throws IkeProtocolException { + throws IkeException { Transform[] transformArray = new Transform[count]; for (int i = 0; i < count; i++) { Transform transform = Transform.readFrom(inputBuffer); @@ -516,24 +219,30 @@ public final class IkeSaPayload extends IkePayload { public final byte spiSize; public final long spi; + public final SaProposal saProposal; + public final boolean hasUnrecognizedTransform; + // TODO: Validate this proposal + @VisibleForTesting Proposal( byte number, int protocolId, byte spiSize, long spi, + SaProposal saProposal, boolean hasUnrecognizedTransform) { this.number = number; this.protocolId = protocolId; this.spiSize = spiSize; this.spi = spi; + this.saProposal = saProposal; this.hasUnrecognizedTransform = hasUnrecognizedTransform; } @VisibleForTesting - static Proposal readFrom(ByteBuffer inputBuffer) throws IkeProtocolException { + static Proposal readFrom(ByteBuffer inputBuffer) throws IkeException { byte isLast = inputBuffer.get(); if (isLast != LAST_PROPOSAL && isLast != NOT_LAST_PROPOSAL) { throw new InvalidSyntaxException( @@ -602,37 +311,31 @@ public final class IkeSaPayload extends IkePayload { } } - if (protocolId == PROTOCOL_ID_IKE) { - IkeSaProposal saProposal = - new IkeSaProposal( - encryptAlgoList.toArray( - new EncryptionTransform[encryptAlgoList.size()]), - prfList.toArray(new PrfTransform[prfList.size()]), - integAlgoList.toArray(new IntegrityTransform[integAlgoList.size()]), - dhGroupList.toArray(new DhGroupTransform[dhGroupList.size()])); - return new IkeProposal(number, spiSize, spi, saProposal, hasUnrecognizedTransform); - } else { - ChildSaProposal saProposal = - new ChildSaProposal( - encryptAlgoList.toArray( - new EncryptionTransform[encryptAlgoList.size()]), - integAlgoList.toArray(new IntegrityTransform[integAlgoList.size()]), - dhGroupList.toArray(new DhGroupTransform[dhGroupList.size()]), - esnList.toArray(new EsnTransform[esnList.size()])); - return new ChildProposal(number, spi, saProposal, hasUnrecognizedTransform); - } + SaProposal saProposal = + new SaProposal( + protocolId, + encryptAlgoList.toArray( + new EncryptionTransform[encryptAlgoList.size()]), + prfList.toArray(new PrfTransform[prfList.size()]), + integAlgoList.toArray(new IntegrityTransform[integAlgoList.size()]), + dhGroupList.toArray(new DhGroupTransform[dhGroupList.size()]), + esnList.toArray(new EsnTransform[esnList.size()])); + + return new Proposal( + number, protocolId, spiSize, spi, saProposal, hasUnrecognizedTransform); } + // TODO: Add another contructor for encoding. /** Package private */ boolean isNegotiatedFrom(Proposal reqProposal) { if (protocolId != reqProposal.protocolId || number != reqProposal.number) { return false; } - return getSaProposal().isNegotiatedFrom(reqProposal.getSaProposal()); + return saProposal.isNegotiatedFrom(reqProposal.saProposal); } protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { - Transform[] allTransforms = getSaProposal().getAllTransforms(); + Transform[] allTransforms = saProposal.getAllTransforms(); byte isLastIndicator = isLast ? LAST_PROPOSAL : NOT_LAST_PROPOSAL; byteBuffer @@ -669,202 +372,15 @@ public final class IkeSaPayload extends IkePayload { protected int getProposalLength() { int len = PROPOSAL_HEADER_LEN + spiSize; - Transform[] allTransforms = getSaProposal().getAllTransforms(); + Transform[] allTransforms = saProposal.getAllTransforms(); for (Transform t : allTransforms) len += t.getTransformLength(); return len; } - - @Override - @NonNull - public String toString() { - return "Proposal(" + number + ") " + getSaProposal().toString(); - } - - /** Package private method for releasing SPI resource in this unselected Proposal. */ - abstract void releaseSpiResourceIfExists(); - - /** Package private method for getting SaProposal */ - abstract SaProposal getSaProposal(); - } - - /** This class represents a Proposal for IKE SA negotiation. */ - public static final class IkeProposal extends Proposal { - private IkeSecurityParameterIndex mIkeSpiResource; - - public final IkeSaProposal saProposal; - - /** - * Construct IkeProposal from a decoded inbound message for IKE negotiation. - * - * <p>Package private - */ - IkeProposal( - byte number, - byte spiSize, - long spi, - IkeSaProposal saProposal, - boolean hasUnrecognizedTransform) { - super(number, PROTOCOL_ID_IKE, spiSize, spi, hasUnrecognizedTransform); - this.saProposal = saProposal; - } - - /** Construct IkeProposal for an outbound message for IKE negotiation. */ - private IkeProposal( - byte number, - byte spiSize, - IkeSecurityParameterIndex ikeSpiResource, - IkeSaProposal saProposal) { - super( - number, - PROTOCOL_ID_IKE, - spiSize, - ikeSpiResource == null ? SPI_NOT_INCLUDED : ikeSpiResource.getSpi(), - false /*hasUnrecognizedTransform*/); - mIkeSpiResource = ikeSpiResource; - this.saProposal = saProposal; - } - - /** - * Construct IkeProposal for an outbound message for IKE negotiation. - * - * <p>Package private - */ - @VisibleForTesting - static IkeProposal createIkeProposal( - byte number, byte spiSize, IkeSaProposal saProposal, InetAddress localAddress) - throws IOException { - // IKE_INIT uses SPI_LEN_NOT_INCLUDED, while rekeys use SPI_LEN_IKE - IkeSecurityParameterIndex spiResource = - (spiSize == SPI_LEN_NOT_INCLUDED - ? null - : IkeSecurityParameterIndex.allocateSecurityParameterIndex( - localAddress)); - return new IkeProposal(number, spiSize, spiResource, saProposal); - } - - /** Package private method for releasing SPI resource in this unselected Proposal. */ - void releaseSpiResourceIfExists() { - // mIkeSpiResource is null when doing IKE initial exchanges. - if (mIkeSpiResource == null) return; - mIkeSpiResource.close(); - mIkeSpiResource = null; - } - - /** - * Package private method for allocating SPI resource for a validated remotely generated IKE - * SA proposal. - */ - void allocateResourceForRemoteIkeSpi(InetAddress remoteAddress) throws IOException { - mIkeSpiResource = - IkeSecurityParameterIndex.allocateSecurityParameterIndex(remoteAddress, spi); - } - - @Override - public SaProposal getSaProposal() { - return saProposal; - } - - /** - * Get the IKE SPI resource. - * - * @return the IKE SPI resource or null for IKE initial exchanges. - */ - public IkeSecurityParameterIndex getIkeSpiResource() { - return mIkeSpiResource; - } - } - - /** This class represents a Proposal for Child SA negotiation. */ - public static final class ChildProposal extends Proposal { - private SecurityParameterIndex mChildSpiResource; - - public final ChildSaProposal saProposal; - - /** - * Construct ChildProposal from a decoded inbound message for Child SA negotiation. - * - * <p>Package private - */ - ChildProposal( - byte number, - long spi, - ChildSaProposal saProposal, - boolean hasUnrecognizedTransform) { - super( - number, - PROTOCOL_ID_ESP, - SPI_LEN_IPSEC, - spi, - hasUnrecognizedTransform); - this.saProposal = saProposal; - } - - /** Construct ChildProposal for an outbound message for Child SA negotiation. */ - private ChildProposal( - byte number, SecurityParameterIndex childSpiResource, ChildSaProposal saProposal) { - super( - number, - PROTOCOL_ID_ESP, - SPI_LEN_IPSEC, - (long) childSpiResource.getSpi(), - false /*hasUnrecognizedTransform*/); - mChildSpiResource = childSpiResource; - this.saProposal = saProposal; - } - - /** - * Construct ChildProposal for an outbound message for Child SA negotiation. - * - * <p>Package private - */ - @VisibleForTesting - static ChildProposal createChildProposal( - byte number, - ChildSaProposal saProposal, - IpSecManager ipSecManager, - InetAddress localAddress) - throws ResourceUnavailableException { - return new ChildProposal( - number, ipSecManager.allocateSecurityParameterIndex(localAddress), saProposal); - } - - /** Package private method for releasing SPI resource in this unselected Proposal. */ - void releaseSpiResourceIfExists() { - if (mChildSpiResource == null) return; - - mChildSpiResource.close(); - mChildSpiResource = null; - } - - /** - * Package private method for allocating SPI resource for a validated remotely generated - * Child SA proposal. - */ - void allocateResourceForRemoteChildSpi(IpSecManager ipSecManager, InetAddress remoteAddress) - throws ResourceUnavailableException, SpiUnavailableException { - mChildSpiResource = - ipSecManager.allocateSecurityParameterIndex(remoteAddress, (int) spi); - } - - @Override - public SaProposal getSaProposal() { - return saProposal; - } - - /** - * Get the IPsec SPI resource. - * - * @return the IPsec SPI resource. - */ - public SecurityParameterIndex getChildSpiResource() { - return mChildSpiResource; - } } @VisibleForTesting interface AttributeDecoder { - List<Attribute> decodeAttributes(int length, ByteBuffer inputBuffer) - throws IkeProtocolException; + List<Attribute> decodeAttributes(int length, ByteBuffer inputBuffer) throws IkeException; } /** @@ -909,7 +425,7 @@ public final class IkeSaPayload extends IkePayload { static AttributeDecoder sAttributeDecoder = new AttributeDecoder() { public List<Attribute> decodeAttributes(int length, ByteBuffer inputBuffer) - throws IkeProtocolException { + throws IkeException { List<Attribute> list = new LinkedList<>(); int parsedLength = BASIC_TRANSFORM_LEN; while (parsedLength < length) { @@ -947,7 +463,7 @@ public final class IkeSaPayload extends IkePayload { } @VisibleForTesting - static Transform readFrom(ByteBuffer inputBuffer) throws IkeProtocolException { + static Transform readFrom(ByteBuffer inputBuffer) throws IkeException { byte isLast = inputBuffer.get(); if (isLast != LAST_TRANSFORM && isLast != NOT_LAST_TRANSFORM) { throw new InvalidSyntaxException( @@ -988,7 +504,7 @@ public final class IkeSaPayload extends IkePayload { // Throw InvalidSyntaxException if there are multiple Attributes of the same type private static void validateAttributeUniqueness(List<Attribute> attributeList) - throws IkeProtocolException { + throws IkeException { Set<Integer> foundTypes = new ArraySet<>(); for (Attribute attr : attributeList) { if (!foundTypes.add(attr.type)) { @@ -1039,7 +555,7 @@ public final class IkeSaPayload extends IkePayload { * Exchange Protocol Version 2 (IKEv2)</a> */ public static final class EncryptionTransform extends Transform { - public static final int KEY_LEN_UNSPECIFIED = 0; + private static final int KEY_LEN_UNSPECIFIED = 0; // When using encryption algorithm with variable-length keys, mSpecifiedKeyLength MUST be // set and a KeyLengthAttribute MUST be attached. Otherwise, mSpecifiedKeyLength MUST NOT be @@ -1097,15 +613,6 @@ public final class IkeSaPayload extends IkePayload { } } - /** - * Get the specified key length. - * - * @return the specified key length. - */ - public int getSpecifiedKeyLength() { - return mSpecifiedKeyLength; - } - @Override public int hashCode() { return Objects.hash(type, id, mSpecifiedKeyLength); @@ -1211,15 +718,6 @@ public final class IkeSaPayload extends IkePayload { public String getTransformTypeString() { return "Encryption Algorithm"; } - - @Override - @NonNull - public String toString() { - return SaProposal.getEncryptionAlgorithmString(id) - + "(" - + getSpecifiedKeyLength() - + ")"; - } } /** @@ -1287,12 +785,6 @@ public final class IkeSaPayload extends IkePayload { public String getTransformTypeString() { return "Pseudorandom Function"; } - - @Override - @NonNull - public String toString() { - return SaProposal.getPseudorandomFunctionString(id); - } } /** @@ -1364,12 +856,6 @@ public final class IkeSaPayload extends IkePayload { public String getTransformTypeString() { return "Integrity Algorithm"; } - - @Override - @NonNull - public String toString() { - return SaProposal.getIntegrityAlgorithmString(id); - } } /** @@ -1441,12 +927,6 @@ public final class IkeSaPayload extends IkePayload { public String getTransformTypeString() { return "Diffie-Hellman Group"; } - - @Override - @NonNull - public String toString() { - return SaProposal.getDhGroupString(id); - } } /** @@ -1524,15 +1004,6 @@ public final class IkeSaPayload extends IkePayload { public String getTransformTypeString() { return "Extended Sequence Numbers"; } - - @Override - @NonNull - public String toString() { - if (id == ESN_POLICY_NO_EXTENDED) { - return "ESN_No_Extended"; - } - return "ESN_Extended"; - } } /** @@ -1622,8 +1093,7 @@ public final class IkeSaPayload extends IkePayload { } @VisibleForTesting - static Pair<Attribute, Integer> readFrom(ByteBuffer inputBuffer) - throws IkeProtocolException { + static Pair<Attribute, Integer> readFrom(ByteBuffer inputBuffer) throws IkeException { short formatAndType = inputBuffer.getShort(); int format = formatAndType & ATTRIBUTE_FORMAT_MASK; int type = formatAndType & ATTRIBUTE_TYPE_MASK; @@ -1749,25 +1219,6 @@ public final class IkeSaPayload extends IkePayload { */ @Override public String getTypeString() { - return "SA"; - } - - @Override - @NonNull - public String toString() { - StringBuilder sb = new StringBuilder(); - if (isSaResponse) { - sb.append("SA Response: "); - } else { - sb.append("SA Request: "); - } - - int len = proposalList.size(); - for (int i = 0; i < len; i++) { - sb.append(proposalList.get(i).toString()); - if (i < len - 1) sb.append(", "); - } - - return sb.toString(); + return "SA Payload"; } } diff --git a/src/java/com/android/ike/ikev2/message/IkeSkPayload.java b/src/java/com/android/ike/ikev2/message/IkeSkPayload.java new file mode 100644 index 00000000..d753ee83 --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeSkPayload.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import com.android.ike.ikev2.exceptions.IkeException; + +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; + +/** + * IkeSkPayload represents a Encrypted Payload. + * + * <p>It contains other payloads in encrypted form. It is must be the last payload in the message. + * It should be the only payload in this implementation. + * + * <p>Critical bit must be ignored when doing decoding. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#page-105">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public final class IkeSkPayload extends IkePayload { + + private final IkeEncryptedPayloadBody mIkeEncryptedPayloadBody; + + /** + * Construct an instance of IkeSkPayload from decrypting an incoming packet. + * + * @param critical indicates if it is a critical payload. + * @param message the byte array contains the whole IKE message. + * @param integrityMac the initialized Mac for integrity check. + * @param checksumLen the checksum length of negotiated integrity algorithm. + * @param decryptCipher the uninitialized Cipher for doing decryption. + * @param dKey the decryption key. + */ + IkeSkPayload( + boolean critical, + byte[] message, + Mac integrityMac, + int checksumLen, + Cipher decryptCipher, + SecretKey dKey) + throws IkeException, GeneralSecurityException { + super(PAYLOAD_TYPE_SK, critical); + + mIkeEncryptedPayloadBody = + new IkeEncryptedPayloadBody( + message, integrityMac, checksumLen, decryptCipher, dKey); + } + + /** + * Construct an instance of IkeSkPayload for building outbound packet. + * + * @param ikeHeader the IKE header. + * @param firstPayloadType the type of first payload nested in SkPayload. + * @param unencryptedPayloads the encoded payload list to protect. + * @param integrityMac the initialized Mac for calculating integrity checksum + * @param checksumLen the checksum length of negotiated integrity algorithm. + * @param encryptCipher the uninitialized Cipher for doing encryption. + * @param eKey the encryption key. + */ + IkeSkPayload( + IkeHeader ikeHeader, + @PayloadType int firstPayloadType, + byte[] unencryptedPayloads, + Mac integrityMac, + int checksumLen, + Cipher encryptCipher, + SecretKey eKey) { + super(PAYLOAD_TYPE_SK, false); + + mIkeEncryptedPayloadBody = + new IkeEncryptedPayloadBody( + ikeHeader, + firstPayloadType, + unencryptedPayloads, + integrityMac, + checksumLen, + encryptCipher, + eKey); + } + + /** + * Return unencrypted payload list + * + * @return unencrypted payload list in a byte array. + */ + public byte[] getUnencryptedPayloads() { + return mIkeEncryptedPayloadBody.getUnencryptedData(); + } + + // TODO: Add another constructor for AEAD protected payload. + + /** + * Encode this payload to a ByteBuffer. + * + * @param nextPayload type of payload that follows this payload. + * @param byteBuffer destination ByteBuffer that stores encoded payload. + */ + @Override + protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { + encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); + byteBuffer.put(mIkeEncryptedPayloadBody.encode()); + } + + /** + * Get entire payload length. + * + * @return entire payload length. + */ + @Override + protected int getPayloadLength() { + return GENERIC_HEADER_LENGTH + mIkeEncryptedPayloadBody.getLength(); + } + + /** + * Return the payload type as a String. + * + * @return the payload type as a String. + */ + @Override + public String getTypeString() { + return "Encrypted and Authenticated Payload"; + } +} diff --git a/src/java/com/android/ike/ikev2/message/IkeTsPayload.java b/src/java/com/android/ike/ikev2/message/IkeTsPayload.java new file mode 100644 index 00000000..8231e29c --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeTsPayload.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import com.android.ike.ikev2.IkeTrafficSelector; +import com.android.ike.ikev2.exceptions.IkeException; + +import java.nio.ByteBuffer; + +/** + * IkeTsPayload represents an Traffic Selector Initiator Payload or an Traffic Selector Responder + * Payload. + * + * <p>Traffic Selector Initiator Payload and Traffic Selector Responder Payload have same format but + * different payload types. They describe the address ranges and port ranges of Child SA initiator + * and Child SA responder. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.13">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public final class IkeTsPayload extends IkePayload { + // Length of reserved field in octets. + private static final int TS_HEADER_RESERVED_LEN = 3; + + /** Number of Traffic Selectors */ + public final int numTs; + public final IkeTrafficSelector[] trafficSelectors; + + IkeTsPayload(boolean critical, byte[] payloadBody, boolean isInitiator) throws IkeException { + super((isInitiator ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER), critical); + + ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); + numTs = Byte.toUnsignedInt(inputBuffer.get()); + // Skip RESERVED byte + inputBuffer.get(new byte[TS_HEADER_RESERVED_LEN]); + + // Decode Traffic Selectors + byte[] tsBytes = new byte[inputBuffer.remaining()]; + inputBuffer.get(tsBytes); + trafficSelectors = IkeTrafficSelector.decodeIkeTrafficSelectors(numTs, tsBytes); + } + + /** + * Encode Traffic Selector Payload to ByteBuffer. + * + * @param nextPayload type of payload that follows this payload. + * @param byteBuffer destination ByteBuffer that stores encoded payload. + */ + @Override + protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { + throw new UnsupportedOperationException( + "It is not supported to encode a " + getTypeString()); + //TODO: Implement it. + } + + /** + * Get entire payload length. + * + * @return entire payload length. + */ + @Override + protected int getPayloadLength() { + throw new UnsupportedOperationException( + "It is not supported to get payload length of " + getTypeString()); + //TODO: Implement it. + } + + /** + * Return the payload type as a String. + * + * @return the payload type as a String. + */ + @Override + public String getTypeString() { + switch (payloadType) { + case PAYLOAD_TYPE_ID_INITIATOR: + return "Traffic Selector Initiator Payload"; + case PAYLOAD_TYPE_ID_RESPONDER: + return "Traffic Selector Responder Payload"; + default: + // Won't reach here. + throw new IllegalArgumentException( + "Invalid Payload Type for Traffic Selector Payload."); + } + } +} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeUnsupportedPayload.java b/src/java/com/android/ike/ikev2/message/IkeUnsupportedPayload.java index ecfe1e9c..4506621a 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeUnsupportedPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeUnsupportedPayload.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import java.nio.ByteBuffer; - /** * IkeUnsupportedPayload represents anunsupported payload. * @@ -66,6 +65,6 @@ final class IkeUnsupportedPayload extends IkePayload { */ @Override public String getTypeString() { - return String.valueOf(payloadType); + return "Unsupported Payload"; } } diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeVendorPayload.java b/src/java/com/android/ike/ikev2/message/IkeVendorPayload.java index d3464071..85996535 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeVendorPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeVendorPayload.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import java.nio.ByteBuffer; @@ -71,6 +71,6 @@ public final class IkeVendorPayload extends IkePayload { */ @Override public String getTypeString() { - return "Vendor"; + return "Vendor ID Payload"; } } diff --git a/src/java/com/android/internal/net/utils/BigIntegerUtils.java b/src/java/com/android/ike/ikev2/utils/BigIntegerUtils.java index 09dad74b..6104834c 100644 --- a/src/java/com/android/internal/net/utils/BigIntegerUtils.java +++ b/src/java/com/android/ike/ikev2/utils/BigIntegerUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.net.utils; +package com.android.ike.ikev2.utils; import java.math.BigInteger; diff --git a/src/java/com/android/internal/net/crypto/KeyGenerationUtils.java b/src/java/com/android/internal/net/crypto/KeyGenerationUtils.java deleted file mode 100644 index c9ade7d5..00000000 --- a/src/java/com/android/internal/net/crypto/KeyGenerationUtils.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.crypto; - -import java.nio.ByteBuffer; - -/** - * KeyGenerationUtils is a util class that contains utils for key generation needed by IKEv2 and - * EAP. - */ -public class KeyGenerationUtils { - /** - * Returns the derived pseudorandom number with the specified length by iteratively applying a - * PRF. - * - * <p>prf+(K, S) outputs a pseudorandom stream by using the PRF iteratively. In this way it can - * generate long enough keying material containing all the keys. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296 Internet Key - * Exchange Protocol Version 2 (IKEv2) 2.13. Generating Keying Material </a> - * @param byteSigner the PRF used to sign the given data using the given key. - * @param keyBytes the key to sign data. - * @param dataToSign the data to be signed. - * @param keyMaterialLen the length of keying materials to be generated. - * @return the byte array of keying materials - */ - public static byte[] prfPlus( - ByteSigner byteSigner, byte[] keyBytes, byte[] dataToSign, int keyMaterialLen) { - ByteBuffer keyMatBuffer = ByteBuffer.allocate(keyMaterialLen); - - byte[] previousMac = new byte[0]; - final int padLen = 1; - byte padValue = 1; - - while (keyMatBuffer.remaining() > 0) { - ByteBuffer dataToSignBuffer = - ByteBuffer.allocate(previousMac.length + dataToSign.length + padLen); - dataToSignBuffer.put(previousMac).put(dataToSign).put(padValue); - - previousMac = byteSigner.signBytes(keyBytes, dataToSignBuffer.array()); - - keyMatBuffer.put( - previousMac, 0, Math.min(previousMac.length, keyMatBuffer.remaining())); - - padValue++; - } - - return keyMatBuffer.array(); - } - - /** - * ByteSigner is an interface to be used for implementing the byte-signing for generating keys - * using {@link KeyGenerationUtils#prfPlus(ByteSigner, byte[], byte[], int)}. - */ - public interface ByteSigner { - /** - * Signs the given data using the key given. - * - * <p>Caller is responsible for providing a valid key according to their use cases. - * - * @param keyBytes the key to sign data. - * @param dataToSign the data to be signed. - * @return the signed value. - */ - byte[] signBytes(byte[] keyBytes, byte[] dataToSign); - } -} diff --git a/src/java/com/android/internal/net/eap/EapAuthenticator.java b/src/java/com/android/internal/net/eap/EapAuthenticator.java deleted file mode 100644 index a6869d7d..00000000 --- a/src/java/com/android/internal/net/eap/EapAuthenticator.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import android.content.Context; -import android.net.eap.EapSessionConfig; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.statemachine.EapStateMachine; -import com.android.internal.net.utils.Log; - -import java.security.SecureRandom; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeoutException; - -/** - * EapAuthenticator represents an EAP peer implementation. - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public class EapAuthenticator extends Handler { - private static final String EAP_TAG = "EAP"; - private static final boolean LOG_SENSITIVE = false; - public static final Log LOG = new Log(EAP_TAG, LOG_SENSITIVE); - - private static final String TAG = EapAuthenticator.class.getSimpleName(); - private static final long DEFAULT_TIMEOUT_MILLIS = 7000L; - - private final Executor mWorkerPool; - private final EapStateMachine mStateMachine; - private final IEapCallback mCb; - private final long mTimeoutMillis; - private boolean mCallbackFired = false; - - /** - * Constructor for EapAuthenticator - * - * @param looper Looper for running a message loop - * @param cb IEapCallback for callbacks to the client - * @param context Context for this EapAuthenticator - * @param eapSessionConfig Configuration for an EapAuthenticator - */ - public EapAuthenticator( - Looper looper, - IEapCallback cb, - Context context, - EapSessionConfig eapSessionConfig) { - this( - looper, - cb, - new EapStateMachine(context, eapSessionConfig, new SecureRandom()), - Executors.newSingleThreadExecutor(), - DEFAULT_TIMEOUT_MILLIS); - } - - @VisibleForTesting - EapAuthenticator( - Looper looper, - IEapCallback cb, - EapStateMachine eapStateMachine, - Executor executor, - long timeoutMillis) { - super(looper); - - mCb = cb; - mStateMachine = eapStateMachine; - mWorkerPool = executor; - mTimeoutMillis = timeoutMillis; - } - - @Override - public void handleMessage(Message msg) { - // No messages processed here. Only runnables. Drop all messages. - } - - /** - * Processes the given msgBytes within the context of the current EAP Session. - * - * <p>If the given message is successfully processed, the relevant {@link IEapCallback} function - * is used. Otherwise, {@link IEapCallback#onError(Throwable)} is called. - * - * @param msgBytes the byte-array encoded EAP message to be processed - */ - public void processEapMessage(byte[] msgBytes) { - // reset - mCallbackFired = false; - - postDelayed( - () -> { - if (!mCallbackFired) { - // Fire failed callback - mCallbackFired = true; - LOG.e(TAG, "Timeout occurred in EapStateMachine"); - mCb.onError(new TimeoutException("Timeout while processing message")); - } - }, - EapAuthenticator.this, - mTimeoutMillis); - - // proxy to worker thread for async processing - mWorkerPool.execute( - () -> { - // Any unhandled exceptions within the state machine are caught here to make - // sure that the caller does not wait for the full timeout duration before being - // notified of a failure. - EapResult processResponse; - try { - processResponse = mStateMachine.process(msgBytes); - } catch (Exception ex) { - LOG.e(TAG, "Exception thrown while processing message", ex); - processResponse = new EapError(ex); - } - - final EapResult finalProcessResponse = processResponse; - EapAuthenticator.this.post( - () -> { - // No synchronization needed, since Handler serializes - if (!mCallbackFired) { - LOG.i( - TAG, - "EapStateMachine returned " - + finalProcessResponse - .getClass() - .getSimpleName()); - - if (finalProcessResponse instanceof EapResponse) { - mCb.onResponse(((EapResponse) finalProcessResponse).packet); - } else if (finalProcessResponse instanceof EapError) { - EapError eapError = (EapError) finalProcessResponse; - LOG.e( - TAG, - "EapError returned with cause=" + eapError.cause); - mCb.onError(eapError.cause); - } else if (finalProcessResponse instanceof EapSuccess) { - EapSuccess eapSuccess = (EapSuccess) finalProcessResponse; - LOG.d( - TAG, - "EapSuccess with" - + " MSK=" + LOG.pii(eapSuccess.msk) - + " EMSK=" + LOG.pii(eapSuccess.msk)); - mCb.onSuccess(eapSuccess.msk, eapSuccess.emsk); - } else { // finalProcessResponse instanceof EapFailure - mCb.onFail(); - } - - mCallbackFired = true; - - // Ensure delayed timeout runnable does not fire - EapAuthenticator.this.removeCallbacksAndMessages( - EapAuthenticator.this); - } - }); - }); - } -} diff --git a/src/java/com/android/internal/net/eap/EapResult.java b/src/java/com/android/internal/net/eap/EapResult.java deleted file mode 100644 index 894dd475..00000000 --- a/src/java/com/android/internal/net/eap/EapResult.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.exceptions.InvalidEapResponseException; -import com.android.internal.net.eap.message.EapMessage; - -/** - * EapResult represents the return type R for a process operation within the EapStateMachine. - */ -public abstract class EapResult { - - /** - * EapSuccess represents a success response from the EapStateMachine. - * - * @see <a href="https://tools.ietf.org/html/rfc3748">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ - public static class EapSuccess extends EapResult { - public final byte[] msk; - public final byte[] emsk; - - public EapSuccess(@NonNull byte[] msk, @NonNull byte[] emsk) { - if (msk == null || emsk == null) { - throw new IllegalArgumentException("msk and emsk must not be null"); - } - this.msk = msk; - this.emsk = emsk; - } - } - - /** - * EapFailure represents a failure response from the EapStateMachine. - * - * @see <a href="https://tools.ietf.org/html/rfc3748">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ - public static class EapFailure extends EapResult {} - - /** - * EapResponse represents an outgoing message from the EapStateMachine. - * - * @see <a href="https://tools.ietf.org/html/rfc3748">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ - public static class EapResponse extends EapResult { - public final byte[] packet; - - @VisibleForTesting - protected EapResponse(byte[] packet) { - this.packet = packet; - } - - /** - * Constructs and returns an EapResult for the given EapMessage. - * - * <p>If the given EapMessage is not of type EAP-Response, an EapError object will be - * returned. - * - * @param message the EapMessage to be encoded in the EapResponse instance. - * @return an EapResponse instance for the given message. If message.eapCode != {@link - * EapMessage#EAP_CODE_RESPONSE}, an EapError instance is returned. - */ - public static EapResult getEapResponse(@NonNull EapMessage message) { - if (message == null) { - throw new IllegalArgumentException("EapMessage should not be null"); - } else if (message.eapCode != EapMessage.EAP_CODE_RESPONSE) { - return new EapError(new InvalidEapResponseException( - "Cannot construct an EapResult from a non-EAP-Response message")); - } - - return new EapResponse(message.encode()); - } - } - - /** - * EapError represents an error that occurred in the EapStateMachine. - * - * @see <a href="https://tools.ietf.org/html/rfc3748">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ - public static class EapError extends EapResult { - public final Exception cause; - - /** - * Constructs an EapError instance for the given cause. - * - * @param cause the Exception that caused the EapError to be returned from the - * EapStateMachine - */ - public EapError(Exception cause) { - this.cause = cause; - } - } -} diff --git a/src/java/com/android/internal/net/eap/IEapCallback.java b/src/java/com/android/internal/net/eap/IEapCallback.java deleted file mode 100644 index fae3edf8..00000000 --- a/src/java/com/android/internal/net/eap/IEapCallback.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -/** - * IEapCallback represents a Callback interface to be implemented by clients of the - * {@link EapAuthenticator}. - * - * <p>Exactly one of these callbacks will be called for each message processed by the - * {@link EapAuthenticator}. - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public interface IEapCallback { - /** - * Callback used to indicate that the EAP Authentication session was successful. - * - * @param msk The Master Session Key (MSK) generated in the session - * @param emsk The Extended Master Session Key (EMSK) generated in the session - */ - void onSuccess(byte[] msk, byte[] emsk); - - /** - * Callback used to indicate that the EAP Authentication Session was unsuccessful. - */ - void onFail(); - - /** - * Callback used to return an EAP-Response message for the message being processed. - * - * @param eapMsg byte-array encoded EAP-Response message to be sent to the Authentication server - */ - void onResponse(byte[] eapMsg); - - /** - * Callback used to indicate that there was an error processing the current EAP message. - * - * @param cause The cause of the processing error - */ - void onError(Throwable cause); -} diff --git a/src/java/com/android/internal/net/eap/crypto/Fips186_2Prf.java b/src/java/com/android/internal/net/eap/crypto/Fips186_2Prf.java deleted file mode 100644 index fb1268c0..00000000 --- a/src/java/com/android/internal/net/eap/crypto/Fips186_2Prf.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.crypto; - -import static com.android.internal.net.utils.BigIntegerUtils.bigIntegerToUnsignedByteArray; -import static com.android.internal.net.utils.BigIntegerUtils.unsignedByteArrayToBigInteger; - -import com.android.org.bouncycastle.crypto.digests.SHA1Digest; - -import java.math.BigInteger; -import java.nio.ByteBuffer; - -/** - * This class implements the Pseudo-Random Number Generator as specified by FIPS 186-2, change - * notice 1, as required by EAP-SIM (RFC 4186) and EAP-AKA (RFC 4187). - * - * <p>Simplifying constraints allow for some parameters to be permanently fixed: The "b" parameter - * is specified to always be 160 by RFC 4186. Likewise, the seed must be 20 bytes and therefore can - * never exceed 2^b. - */ -public class Fips186_2Prf { - private static final int SEED_LEN_BYTES = 20; - private static final int SHA_OUTPUT_LEN_BYTES = 40; - - /** - * Gets the next random based on the PRF described in FIPS 186-2, Change Notice 1. - * - * @param seed the seed value - * @param outputLenBytes the output byte count required. Will run multiple iterations as needed. - * @return the byte-array random result - */ - public byte[] getRandom(byte[] seed, int outputLenBytes) { - if (seed.length != SEED_LEN_BYTES) { - throw new IllegalArgumentException("Invalid seed length. Must be 160b (20B)"); - } - - BigInteger xkey = unsignedByteArrayToBigInteger(seed); - BigInteger exp_b = new BigInteger("2").pow(SEED_LEN_BYTES * 8); - - ByteBuffer buffer = ByteBuffer.allocate(outputLenBytes); - - // RFC 4186, Appendix B, Step 3: - // M is the number of generation runs, based on the desired output bytes (rounded up) - // EAP SIM/AKA require at least 16 (K_ENCR) + 16 (K_AUTH) + 64 (MSK) + 64 (EMSK) bytes - // (total 160 Bytes) - int numIterations = (outputLenBytes + SHA_OUTPUT_LEN_BYTES - 1) / SHA_OUTPUT_LEN_BYTES; - for (int j = 0; j < numIterations; j++) { - for (int i = 0; i < 2; i++) { - // RFC 4186, Appendix B, Step 3.1 XSEED_j = 0 - // (XSEED_j unused, omitted) - - // RFC 4186, Appendix B, Step 3.2a: XVAL = (XKEY + XSEED_j) mod 2^b - // (XSEED_j unused, omitted) - BigInteger xval = xkey.mod(exp_b); - - // RFC 4186, Appendix B, Step 3.2b: - // w_i = G(t, XVAL) - byte[] w_i = new byte[SEED_LEN_BYTES]; - Sha1_186_2_FunctionG digest = new Sha1_186_2_FunctionG(); - digest.update( - bigIntegerToUnsignedByteArray(xval, SEED_LEN_BYTES), 0, SEED_LEN_BYTES); - digest.doFinal(w_i, 0); - - // RFC 4186, Appendix B, Step 3.2c: - // XKEY = (1 + XKEY + w_i) mod 2^b - xkey = xkey.add(BigInteger.ONE).add(unsignedByteArrayToBigInteger(w_i)); - xkey = xkey.mod(exp_b); - - // RFC 4186, Appendix B, Step 3.3: - // x_j = w_0|w_1 - buffer.put(w_i, 0, Math.min(buffer.remaining(), w_i.length)); - } - } - - return buffer.array(); - } - - /** - * This inner class represents the modifications to the SHA1 digest required for FIPS 186-2 PRFs - * - * <p>FIPS 186-2 requires the use of a hashing function with exactly the same transforms as - * SHA1, but with a slightly different padding (all 0s instead of appending additional metadata - * for entropy). - * - * <p>Specifically, this function extends BouncyCastle's SHA1Digest in order to override the - * finish method, preventing the additional padding bytes from being appended (leaving them all - * 0). - */ - private static class Sha1_186_2_FunctionG extends SHA1Digest { - // IV (t) specified by SHA-1; Use default. - - @Override - public void finish() { - // Don't do any other processing. We only care about the processBlock() functionality. - processBlock(); - } - } -} diff --git a/src/java/com/android/internal/net/eap/crypto/HmacSha256ByteSigner.java b/src/java/com/android/internal/net/eap/crypto/HmacSha256ByteSigner.java deleted file mode 100644 index db6e4b30..00000000 --- a/src/java/com/android/internal/net/eap/crypto/HmacSha256ByteSigner.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.crypto; - -import com.android.internal.net.crypto.KeyGenerationUtils; -import com.android.internal.net.crypto.KeyGenerationUtils.ByteSigner; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * HmacSha256ByteSigner is a {@link ByteSigner} to be used for computing HMAC-SHA-256 values for - * specific keys and data. - */ -public class HmacSha256ByteSigner implements KeyGenerationUtils.ByteSigner { - private static final String TAG = HmacSha256ByteSigner.class.getSimpleName(); - private static final String MAC_ALGORITHM_STRING = "HmacSHA256"; - private static final HmacSha256ByteSigner sInstance = new HmacSha256ByteSigner(); - - /** - * Gets instance of HmacSha256ByteSigner. - * - * @return HmacSha256ByteSigner instance. - */ - public static HmacSha256ByteSigner getInstance() { - return sInstance; - } - - @Override - public byte[] signBytes(byte[] keyBytes, byte[] dataToSign) { - try { - Mac mac = Mac.getInstance(MAC_ALGORITHM_STRING); - mac.init(new SecretKeySpec(keyBytes, MAC_ALGORITHM_STRING)); - return mac.doFinal(dataToSign); - } catch (NoSuchAlgorithmException | InvalidKeyException ex) { - throw new IllegalArgumentException(ex); - } - } -} diff --git a/src/java/com/android/internal/net/eap/crypto/ParityBitUtil.java b/src/java/com/android/internal/net/eap/crypto/ParityBitUtil.java deleted file mode 100644 index 6413aded..00000000 --- a/src/java/com/android/internal/net/eap/crypto/ParityBitUtil.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.crypto; - -import com.android.internal.annotations.VisibleForTesting; - -/** - * This class sets parity-bits for a given byte-array as specified by the MSCHAPv2 standard. - * - * @see <a href="https://tools.ietf.org/html/rfc2759">RFC 2759, Microsoft PPP CHAP Extensions, - * Version 2 (MSCHAPv2)</a> - */ -public class ParityBitUtil { - private static final int INPUT_LENGTH = 7; - private static final int OUTPUT_LENGTH = 8; - private static final int BITS_PER_BYTE = 8; - private static final int BITS_PER_PARITY_BIT = 7; - private static final byte MASK = (byte) 0xFE; - - /** - * Computes and returns a byte[] with the odd-parity bits set. - * - * <p>Parity bits are set for the 8th, 16th, 24th, etc. bits as the LSB for each byte. - * - * @param input byte[] the input data requiring parity bits - * @return byte[] the input byte[] with its parity-bits set - * @throws IllegalArgumentException iff the given data array does not contain 7 bytes - */ - public static byte[] addParityBits(byte[] input) { - if (input.length != INPUT_LENGTH) { - throw new IllegalArgumentException("Data must be 7B long"); - } - byte[] output = new byte[OUTPUT_LENGTH]; - - long allBits = byteArrayToLong(input) << 1; // make room for parity bit - - // allBits stores input bits as [b56, ..., b1, 0]. Consuming allBits in reverse populates - // output with a right shift - for (int i = output.length - 1; i >= 0; i--) { - output[i] = getByteWithParityBit((byte) allBits); - allBits >>= BITS_PER_PARITY_BIT; - } - - return output; - } - - @VisibleForTesting - static byte getByteWithParityBit(byte b) { - // Parity bits per RFC 2759#8.6 - byte parity = - (byte) ((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)); - - // If we have an odd number of bits, b1 should be 0. - // If we have an even number of bits, b1 should be 1. - byte parityBit = (byte) (~parity & 1); - return (byte) ((b & MASK) | parityBit); - } - - @VisibleForTesting - static long byteArrayToLong(byte[] input) { - long result = 0; - for (int i = 0; i < input.length; i++) { - result = (result << BITS_PER_BYTE) | Byte.toUnsignedInt(input[i]); - } - return result; - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/EapInvalidPacketLengthException.java b/src/java/com/android/internal/net/eap/exceptions/EapInvalidPacketLengthException.java deleted file mode 100644 index ca6d4da4..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/EapInvalidPacketLengthException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions; - -import com.android.internal.net.eap.message.EapMessage; - -/** - * This exception is thrown when the Packet Length for an {@link EapMessage} is invalid. - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public class EapInvalidPacketLengthException extends EapSilentException { - /** - * Construct an instance of EapInvalidPacketLengthException with the specified detail message. - * - * @param message the detail message. - */ - public EapInvalidPacketLengthException(String message) { - super(message); - } - - /** - * Construct an instance of EapInvalidPacketLengthException with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapInvalidPacketLengthException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/EapInvalidRequestException.java b/src/java/com/android/internal/net/eap/exceptions/EapInvalidRequestException.java deleted file mode 100644 index 8dee31ae..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/EapInvalidRequestException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions; - -import com.android.internal.net.eap.statemachine.EapStateMachine; - -/** - * EapInvalidRequestException is thrown when invalid EapMessages are attempted to be processed by - * the {@link EapStateMachine}. - */ -public class EapInvalidRequestException extends Exception { - /** - * Construct an instance of EapInvalidRequestException with the specified detail message. - * - * @param message the detail message. - */ - public EapInvalidRequestException(String message) { - super(message); - } - - /** - * Construct an instance of EapInvalidRequestException with the specified message and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapInvalidRequestException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/EapSilentException.java b/src/java/com/android/internal/net/eap/exceptions/EapSilentException.java deleted file mode 100644 index 3cb9211d..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/EapSilentException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions; - -/** - * EapSilentException represents a category of EAP errors that should be silently discarded by - * authenticators and peers. - * - * <p>EapSilentException is so-named due to its role in the EAP standard. RFC 3748 requires that - * several error cases be "silently discarded". In these cases, no EAP-Response message will be sent - * to the Authenticator. However, when thrown SilentExceptions will still signal an EAP failure to - * the IKE library calling it, resulting in an Authentication failure. - * - * Each type of silent error should implement its own subclass. - */ -public abstract class EapSilentException extends Exception { - /** - * Construct an instance of EapSilentException with the specified detail message. - * - * @param message the detail message. - */ - public EapSilentException(String message) { - super(message); - } - - /** - * Construct an instance of EapSilentException with the specified message and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSilentException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/InvalidEapCodeException.java b/src/java/com/android/internal/net/eap/exceptions/InvalidEapCodeException.java deleted file mode 100644 index 17a0c4df..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/InvalidEapCodeException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions; - -import com.android.internal.net.eap.message.EapMessage; - -/** - * This exception is thrown when the EAP Code for an {@link EapMessage} is invalid. - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public class InvalidEapCodeException extends EapSilentException { - /** - * Construct an instance of InvalidEapCodeException with the specified code. - * - * @param code The invalid code type - */ - public InvalidEapCodeException(int code) { - super("Invalid Code included in EapMessage: " + code); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/InvalidEapResponseException.java b/src/java/com/android/internal/net/eap/exceptions/InvalidEapResponseException.java deleted file mode 100644 index c0a12e2c..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/InvalidEapResponseException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions; - -/** - * InvalidEapResponseException is thrown when an invalid EapResponse is attempted to be constructed. - * - * <p>EapResponses can only be constructed from EapMessages with the EAP Code for Responses (2). - */ -public class InvalidEapResponseException extends Exception { - - /** - * Construct an instance of InvalidEapResponseException with the specified detail message. - * - * @param message the detail message. - */ - public InvalidEapResponseException(String message) { - super(message); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/UnsupportedEapTypeException.java b/src/java/com/android/internal/net/eap/exceptions/UnsupportedEapTypeException.java deleted file mode 100644 index f128385b..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/UnsupportedEapTypeException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions; - -import com.android.internal.net.eap.message.EapData.EapType; -import com.android.internal.net.eap.message.EapMessage; - -/** - * UnsupportedEapTypeException is thrown when an {@link EapMessage} is constructed with an - * unsupported {@link EapType} value. - */ -public class UnsupportedEapTypeException extends EapSilentException { - public final int eapIdentifier; - - /** - * Construct an instance of UnsupportedEapTypeException with the specified detail message. - * - * @param eapIdentifier the EAP Identifier for the message that contained the unsupported - * EapType - * @param message the detail message. - */ - public UnsupportedEapTypeException(int eapIdentifier, String message) { - super(message); - this.eapIdentifier = eapIdentifier; - } - - /** - * Construct an instance of UnsupportedEapTypeException with the specified message and cause. - * - * @param eapIdentifier the EAP Identifier for the message that contained the unsupported - * EapType - * @param message the detail message. - * @param cause the cause. - */ - public UnsupportedEapTypeException(int eapIdentifier, String message, Throwable cause) { - super(message, cause); - this.eapIdentifier = eapIdentifier; - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/mschapv2/EapMsChapV2ParsingException.java b/src/java/com/android/internal/net/eap/exceptions/mschapv2/EapMsChapV2ParsingException.java deleted file mode 100644 index 09f1faea..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/mschapv2/EapMsChapV2ParsingException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.mschapv2; - -/** - * EapMsChapV2ParsingException is thrown when an invalid MS-CHAPv2 Type Data is attempted to be - * processed. - */ -public class EapMsChapV2ParsingException extends Exception { - /** - * Construct an instance of EapMsChapV2ParsingException with the specified detail message. - * - * @param message the detail message. - */ - public EapMsChapV2ParsingException(String message) { - super(message); - } - - /** - * Construct an instance of EapMsChapV2ParsingException with the specified message and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapMsChapV2ParsingException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapAkaInvalidAuthenticationResponse.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapAkaInvalidAuthenticationResponse.java deleted file mode 100644 index 7310d787..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapAkaInvalidAuthenticationResponse.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -/** - * EapAkaInvalidAuthenticationResponse is thrown when a UICC Challenge is processed during an - * EAP-AKA session and an invalid response format is returned. - */ -public class EapAkaInvalidAuthenticationResponse extends EapSimAkaAuthenticationFailureException { - /** - * Construct an instance of EapAkaInvalidAuthenticationResponse with the specified detail - * message. - * - * @param message the detail message. - */ - public EapAkaInvalidAuthenticationResponse(String message) { - super(message); - } - - /** - * Construct an instance of EapAkaInvalidAuthenticationResponse with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapAkaInvalidAuthenticationResponse(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaAuthenticationFailureException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaAuthenticationFailureException.java deleted file mode 100644 index 699f22e4..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaAuthenticationFailureException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -/** - * EapSimAkaAuthenticationFailureException is thrown when an invalid Uicc Challenge is processed - * during an EAP-SIM or EAP-AKA session. - */ -public class EapSimAkaAuthenticationFailureException extends Exception { - /** - * Construct an instance of EapSimAkaAuthenticationFailureException with the specified detail - * message. - * - * @param message the detail message. - */ - public EapSimAkaAuthenticationFailureException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimAkaAuthenticationFailureException with the specified message - * and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimAkaAuthenticationFailureException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaIdentityUnavailableException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaIdentityUnavailableException.java deleted file mode 100644 index 6a421719..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaIdentityUnavailableException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import android.telephony.TelephonyManager; - -/** - * EapSimAkaIdentityUnavailableException is thrown when the client's IMSI is unavailable from - * {@link TelephonyManager#getSubscriberId()}. - */ -public class EapSimAkaIdentityUnavailableException extends Exception { - /** - * Construct an instance of EapSimAkaIdentityUnavailableException with the specified detail - * message. - * - * @param message the detail message. - */ - public EapSimAkaIdentityUnavailableException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimAkaIdentityUnavailableException with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimAkaIdentityUnavailableException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidAtPaddingException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidAtPaddingException.java deleted file mode 100644 index 34a8f046..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidAtPaddingException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPadding; - -/** - * EapSimAkaInvalidAtPaddingException is thrown when an {@link AtPadding} with invalid padding is - * parsed. Per RFC 4186#10.12 and RFC 4187#10.12, all padding bytes must be 0x00. - * - * @see <a href="https://tools.ietf.org/html/rfc4186#section-10.12">RFC 4186, EAP-SIM, Section - * 10.12</a> - * @see <a href="https://tools.ietf.org/html/rfc4187#section-10.12">RFC 4187, EAP-AKA, Section - * 10.12</a> - */ -public class EapSimAkaInvalidAtPaddingException extends EapSimAkaInvalidAttributeException { - /** - * Construct an instance of EapSimAkaInvalidAtPaddingException with the specified detail - * message. - * - * @param message the detail message. - */ - public EapSimAkaInvalidAtPaddingException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimAkaInvalidAtPaddingException with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimAkaInvalidAtPaddingException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidAttributeException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidAttributeException.java deleted file mode 100644 index eff0c0c2..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidAttributeException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; - -/** - * EapSimAkaInvalidAttributeException is thrown when an invalid {@link EapSimAkaAttribute} is - * attempted to be parsed. - */ -public class EapSimAkaInvalidAttributeException extends Exception { - /** - * Construct an instance of EapSimAkaInvalidAttributeException with the specified detail - * message. - * - * @param message the detail message. - */ - public EapSimAkaInvalidAttributeException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimAkaInvalidAttributeException with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimAkaInvalidAttributeException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidLengthException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidLengthException.java deleted file mode 100644 index c484b112..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaInvalidLengthException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import android.telephony.TelephonyManager; - -/** - * EapSimAkaInvalidLengthException is thrown when - * {@link TelephonyManager#getIccAuthentication(int, int, String)} returns responses with the wrong - * lengths. - */ -public class EapSimAkaInvalidLengthException extends Exception { - /** - * Construct an instance of EapSimAkaInvalidLengthException with the specified detail message. - * - * @param message the detail message. - */ - public EapSimAkaInvalidLengthException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimAkaInvalidLengthException with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimAkaInvalidLengthException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaUnsupportedAttributeException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaUnsupportedAttributeException.java deleted file mode 100644 index 11289175..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimAkaUnsupportedAttributeException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; - -/** - * EapSimAkaUnsupportedAttributeException is thrown when an unsupported {@link EapSimAkaAttribute} - * is attempted to be decoded. - * - * <p>Attributes are supported or not supported per the EAP method being used. For example, an - * {@link AtVersionList} attribute would be considered "supported" when decoded from an EAP-SIM - * context, but would be considered "unsupported" from an EAP-AKA context. - */ -public class EapSimAkaUnsupportedAttributeException extends Exception { - /** - * Construct an instance of EapSimAkaUnsupportedAttributeException with the specified detail - * message. - * - * @param message the detail message. - */ - public EapSimAkaUnsupportedAttributeException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimAkaUnsupportedAttributeException with the specified message - * and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimAkaUnsupportedAttributeException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimInvalidAtRandException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimInvalidAtRandException.java deleted file mode 100644 index c1b92004..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimInvalidAtRandException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandSim; - -/** - * EapSimInvalidAtRandException is thrown when an {@link AtRandSim} with an invalid number of RAND - * values is parsed. When this error is encountered, an EAP-Response/SIM/Client-Error response must - * be used. Note that there MUST BE 2 or 3 RAND values for the AtRandSim to be considered valid. - * - * @see <a href="https://tools.ietf.org/html/rfc4186#section-10.9">RFC 4186, EAP-SIM, Section - * 10.9</a> - */ -public class EapSimInvalidAtRandException extends EapSimAkaInvalidAttributeException { - /** - * Construct an instance of EapSimInvalidAtRandException with the specified detail message. - * - * @param message the detail message. - */ - public EapSimInvalidAtRandException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimInvalidAtRandException with the specified message and - * cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimInvalidAtRandException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimInvalidTypeDataException.java b/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimInvalidTypeDataException.java deleted file mode 100644 index a6a2d457..00000000 --- a/src/java/com/android/internal/net/eap/exceptions/simaka/EapSimInvalidTypeDataException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.exceptions.simaka; - -import com.android.internal.net.eap.message.simaka.EapSimTypeData; - -/** - * EapSimInvalidTypeDataException is thrown when invalid {@link EapSimTypeData} are attempted to be - * parsed. - */ -public class EapSimInvalidTypeDataException extends Exception { - /** - * Construct an instance of EapSimInvalidTypeDataException with the specified detail message. - * - * @param message the detail message. - */ - public EapSimInvalidTypeDataException(String message) { - super(message); - } - - /** - * Construct an instance of EapSimInvalidTypeDataException with the specified message and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public EapSimInvalidTypeDataException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/java/com/android/internal/net/eap/message/EapData.java b/src/java/com/android/internal/net/eap/message/EapData.java deleted file mode 100644 index 86dc43b2..00000000 --- a/src/java/com/android/internal/net/eap/message/EapData.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message; - -import android.annotation.IntDef; -import android.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * EapData represents the data-bytes of an EAP-Packet. - * - * <p>EapData objects will always have a Type-value and the Type-Data bytes that follow. - * - * EapData objects should be parsed from the Type and Type-Data sections of an EAP Packet, as shown - * below: - * - * +-----------------+-----------------+----------------------------------+ - * | Code (1B) | Identifier (1B) | Length (2B) | - * +-----------------+-----------------+----------------------------------+ - * | Type (1B) | Type-Data ... - * +-----------------+----- - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public class EapData { - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - EAP_IDENTITY, - EAP_NOTIFICATION, - EAP_NAK, - EAP_TYPE_SIM, - EAP_TYPE_AKA, - EAP_TYPE_MSCHAP_V2, - EAP_TYPE_AKA_PRIME - }) - public @interface EapType {} - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - EAP_TYPE_SIM, - EAP_TYPE_AKA, - EAP_TYPE_MSCHAP_V2, - EAP_TYPE_AKA_PRIME - }) - public @interface EapMethod {} - - // EAP Type values defined by IANA - // https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml - public static final int EAP_IDENTITY = 1; - public static final int EAP_NOTIFICATION = 2; - public static final int EAP_NAK = 3; - // EAP_MD5_CHALLENGE unsupported, allowable based on RFC 3748, Section 5.4 - public static final int EAP_TYPE_SIM = 18; - public static final int EAP_TYPE_AKA = 23; - public static final int EAP_TYPE_MSCHAP_V2 = 26; - public static final int EAP_TYPE_AKA_PRIME = 50; - - public static final Map<Integer, String> EAP_TYPE_STRING = new HashMap<>(); - static { - EAP_TYPE_STRING.put(EAP_IDENTITY, "Identity"); - EAP_TYPE_STRING.put(EAP_NOTIFICATION, "Notification"); - EAP_TYPE_STRING.put(EAP_NAK, "Nak"); - EAP_TYPE_STRING.put(EAP_TYPE_SIM, "EAP-SIM"); - EAP_TYPE_STRING.put(EAP_TYPE_AKA, "EAP-AKA"); - EAP_TYPE_STRING.put(EAP_TYPE_MSCHAP_V2, "EAP-MSCHAP-V2"); - EAP_TYPE_STRING.put(EAP_TYPE_AKA_PRIME, "EAP-AKA-PRIME"); - } - - private static final Set<Integer> SUPPORTED_TYPES = new HashSet<>(); - static { - SUPPORTED_TYPES.add(EAP_IDENTITY); - SUPPORTED_TYPES.add(EAP_NOTIFICATION); - SUPPORTED_TYPES.add(EAP_NAK); - - // supported EAP Method types - SUPPORTED_TYPES.add(EAP_TYPE_SIM); - SUPPORTED_TYPES.add(EAP_TYPE_AKA); - SUPPORTED_TYPES.add(EAP_TYPE_MSCHAP_V2); - SUPPORTED_TYPES.add(EAP_TYPE_AKA_PRIME); - } - - @EapType public final int eapType; - public final byte[] eapTypeData; - - public static final EapData NOTIFICATION_DATA = new EapData(EAP_NOTIFICATION, new byte[0]); - - /** - * Constructs a new EapData instance. - * - * @param eapType the {@link EapType} for this EapData instance - * @param eapTypeData the Type-Data for this EapData instance - * @throws IllegalArgumentException if eapTypeData is null or if - * {@link EapData#isSupportedEapType} is false for the given eapType - */ - public EapData(@EapType int eapType, @NonNull byte[] eapTypeData) { - this.eapType = eapType; - this.eapTypeData = eapTypeData; - - validate(); - } - - /** - * Gets the length of this EapData object. - * - * @return int for the number of bytes this EapData object represents - */ - public int getLength() { - // length of byte-array + 1 (for 1B type field) - return eapTypeData.length + 1; - } - - /** - * Returns whether this instance is equal to the given Object o. - * - * @param o the Object to be tested for equality - * @return true iff this instance is equal to the given o - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || !(o instanceof EapData)) { - return false; - } - EapData eapData = (EapData) o; - return eapType == eapData.eapType - && Arrays.equals(eapTypeData, eapData.eapTypeData); - } - - /** - * Returns the hashCode value for this instance. - * - * @return the hashCode value for this instance - */ - @Override - public int hashCode() { - return Objects.hash(eapType, Arrays.hashCode(eapTypeData)); - } - - /** - * Puts the byte-encoding for this EapData instance into the given ByteBuffer. - * - * @param b the ByteBuffer to write this EapData instance to - */ - public void encodeToByteBuffer(ByteBuffer b) { - b.put((byte) eapType); - b.put(eapTypeData); - } - - /** - * Returns whether the given eapType is a supported {@link EapType} value. - * - * @param eapType the value to be checked - * @return true iff the given eapType is a supported EAP Type - */ - public static boolean isSupportedEapType(int eapType) { - return SUPPORTED_TYPES.contains(eapType); - } - - private void validate() { - if (this.eapTypeData == null) { - throw new IllegalArgumentException("EapTypeData byte[] must be non-null"); - } - if (!isSupportedEapType(this.eapType)) { - throw new IllegalArgumentException("eapType must be be a valid @EapType value"); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/EapMessage.java b/src/java/com/android/internal/net/eap/message/EapMessage.java deleted file mode 100644 index bb1c6329..00000000 --- a/src/java/com/android/internal/net/eap/message/EapMessage.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_NAK; -import static com.android.internal.net.eap.message.EapData.NOTIFICATION_DATA; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidPacketLengthException; -import com.android.internal.net.eap.exceptions.EapSilentException; -import com.android.internal.net.eap.exceptions.InvalidEapCodeException; -import com.android.internal.net.eap.exceptions.UnsupportedEapTypeException; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * EapMessage represents an EAP Message. - * - * <p>EapMessages will be of type: - * <ul> - * <li>@{link EAP_CODE_REQUEST}</li> - * <li>@{link EAP_CODE_RESPONSE}</li> - * <li>@{link EAP_CODE_SUCCESS}</li> - * <li>@{link EAP_CODE_FAILURE}</li> - * </ul> - * - * Per RFC 3748 Section 4, EAP-Request and EAP-Response packets should be in the format: - * - * +-----------------+-----------------+----------------------------------+ - * | Code (1B) | Identifier (1B) | Length (2B) | - * +-----------------+-----------------+----------------------------------+ - * | Type (1B) | Type-Data ... - * +-----------------+----- - * - * EAP-Success and EAP-Failure packets should be in the format: - * - * +-----------------+-----------------+----------------------------------+ - * | Code (1B) | Identifier (1B) | Length (2B) = '0004' | - * +-----------------+-----------------+----------------------------------+ - * - * Note that Length includes the EAP Header bytes. - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public class EapMessage { - private static final String TAG = EapMessage.class.getSimpleName(); - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - EAP_CODE_REQUEST, - EAP_CODE_RESPONSE, - EAP_CODE_SUCCESS, - EAP_CODE_FAILURE - }) - public @interface EapCode {} - - public static final int EAP_CODE_REQUEST = 1; - public static final int EAP_CODE_RESPONSE = 2; - public static final int EAP_CODE_SUCCESS = 3; - public static final int EAP_CODE_FAILURE = 4; - - public static final Map<Integer, String> EAP_CODE_STRING = new HashMap<>(); - static { - EAP_CODE_STRING.put(EAP_CODE_REQUEST, "REQUEST"); - EAP_CODE_STRING.put(EAP_CODE_RESPONSE, "RESPONSE"); - EAP_CODE_STRING.put(EAP_CODE_SUCCESS, "SUCCESS"); - EAP_CODE_STRING.put(EAP_CODE_FAILURE, "FAILURE"); - } - - public static final int EAP_HEADER_LENGTH = 4; - - @EapCode public final int eapCode; - public final int eapIdentifier; - public final int eapLength; - public final EapData eapData; - - public EapMessage(@EapCode int eapCode, int eapIdentifier, @Nullable EapData eapData) - throws EapSilentException { - this.eapCode = eapCode; - this.eapIdentifier = eapIdentifier; - this.eapLength = EAP_HEADER_LENGTH + ((eapData == null) ? 0 : eapData.getLength()); - this.eapData = eapData; - - validate(); - } - - /** - * Decodes and returns an EapMessage from the given byte array. - * - * @param packet byte array containing a byte-encoded EapMessage - * @return the EapMessage instance representing the given {@param packet} - * @throws EapSilentException for decoding errors that must be discarded silently - */ - public static EapMessage decode(@NonNull byte[] packet) throws EapSilentException { - ByteBuffer buffer = ByteBuffer.wrap(packet); - int eapCode; - int eapIdentifier; - int eapLength; - EapData eapData; - try { - eapCode = Byte.toUnsignedInt(buffer.get()); - eapIdentifier = Byte.toUnsignedInt(buffer.get()); - eapLength = Short.toUnsignedInt(buffer.getShort()); - - if (eapCode == EAP_CODE_REQUEST || eapCode == EAP_CODE_RESPONSE) { - int eapType = Byte.toUnsignedInt(buffer.get()); - if (!EapData.isSupportedEapType(eapType)) { - LOG.e(TAG, "Decoding EAP packet with unsupported EAP-Type: " + eapType); - throw new UnsupportedEapTypeException(eapIdentifier, - "Unsupported eapType=" + eapType); - } - - byte[] eapDataBytes = new byte[buffer.remaining()]; - buffer.get(eapDataBytes); - eapData = new EapData(eapType, eapDataBytes); - } else { - eapData = null; - } - } catch (BufferUnderflowException ex) { - String msg = "EAP packet is missing required values"; - LOG.e(TAG, msg, ex); - throw new EapInvalidPacketLengthException(msg, ex); - } - - int eapDataLength = (eapData == null) ? 0 : eapData.getLength(); - if (eapLength > EAP_HEADER_LENGTH + eapDataLength) { - String msg = "Packet is shorter than specified length"; - LOG.e(TAG, msg); - throw new EapInvalidPacketLengthException(msg); - } - - return new EapMessage(eapCode, eapIdentifier, eapData); - } - - /** - * Converts this EapMessage instance to its byte-encoded representation. - * - * @return byte[] representing the byte-encoded EapMessage - */ - public byte[] encode() { - ByteBuffer byteBuffer = ByteBuffer.allocate(eapLength); - byteBuffer.put((byte) eapCode); - byteBuffer.put((byte) eapIdentifier); - byteBuffer.putShort((short) eapLength); - - if (eapData != null) { - eapData.encodeToByteBuffer(byteBuffer); - } - - return byteBuffer.array(); - } - - /** - * Creates and returns an EAP-Response/Notification message for the given EAP Identifier wrapped - * in an EapResponse object. - * - * @param eapIdentifier the identifier for the message being responded to - * @return an EapResponse object containing an EAP-Response/Notification message with an - * identifier matching the given identifier, or an EapError if an exception was thrown - */ - public static EapResult getNotificationResponse(int eapIdentifier) { - try { - return EapResponse.getEapResponse( - new EapMessage(EAP_CODE_RESPONSE, eapIdentifier, NOTIFICATION_DATA)); - } catch (EapSilentException ex) { - // this should never happen - the only variable value is the identifier - LOG.wtf(TAG, "Failed to create Notification Response for message with identifier=" - + eapIdentifier); - return new EapError(ex); - } - } - - /** - * Creates and returns an EAP-Response/Nak message for the given EAP Identifier wrapped in an - * EapResponse object. - * - * @param eapIdentifier the identifier for the message being responded to - * @param supportedEapTypes Collection of EAP Method types supported by the EAP Session - * @return an EapResponse object containing an EAP-Response/Nak message with an identifier - * matching the given identifier, or an EapError if an exception was thrown - */ - public static EapResult getNakResponse( - int eapIdentifier, - Collection<Integer> supportedEapTypes) { - try { - ByteBuffer buffer = ByteBuffer.allocate(supportedEapTypes.size()); - for (int eapMethodType : supportedEapTypes) { - buffer.put((byte) eapMethodType); - } - EapData nakData = new EapData(EAP_NAK, buffer.array()); - - return EapResponse.getEapResponse( - new EapMessage(EAP_CODE_RESPONSE, eapIdentifier, nakData)); - } catch (EapSilentException ex) { - // this should never happen - the only variable value is the identifier - LOG.wtf(TAG, "Failed to create Nak for message with identifier=" - + eapIdentifier); - return new EapError(ex); - } - } - - private void validate() throws EapSilentException { - if (eapCode != EAP_CODE_REQUEST - && eapCode != EAP_CODE_RESPONSE - && eapCode != EAP_CODE_SUCCESS - && eapCode != EAP_CODE_FAILURE) { - LOG.e(TAG, "Invalid EAP Code: " + eapCode); - throw new InvalidEapCodeException(eapCode); - } - - if ((eapCode == EAP_CODE_SUCCESS || eapCode == EAP_CODE_FAILURE) - && eapLength != EAP_HEADER_LENGTH) { - LOG.e(TAG, "Invalid length for EAP-Success/EAP-Failure. Length: " + eapLength); - throw new EapInvalidPacketLengthException( - "EAP Success/Failure packets must be length 4"); - } - - if ((eapCode == EAP_CODE_REQUEST || eapCode == EAP_CODE_RESPONSE) && eapData == null) { - LOG.e(TAG, "No Type value included for EAP-Request/EAP-Response"); - throw new EapInvalidPacketLengthException( - "EAP Request/Response packets must include a Type value"); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2TypeData.java b/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2TypeData.java deleted file mode 100644 index 985e0dac..00000000 --- a/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2TypeData.java +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.EapMessage; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * EapMsChapV2TypeData represents the Type Data for an {@link EapMessage} during an EAP MSCHAPv2 - * session. - */ -public class EapMsChapV2TypeData { - private static final int LABEL_VALUE_LENGTH = 2; - private static final String ASCII_CHARSET_NAME = "US-ASCII"; - private static final String MESSAGE_PREFIX = "M="; - private static final String MESSAGE_LABEL = "M"; - - // EAP MSCHAPv2 OpCode values (EAP MSCHAPv2#2) - public static final int EAP_MSCHAP_V2_CHALLENGE = 1; - public static final int EAP_MSCHAP_V2_RESPONSE = 2; - public static final int EAP_MSCHAP_V2_SUCCESS = 3; - public static final int EAP_MSCHAP_V2_FAILURE = 4; - public static final int EAP_MSCHAP_V2_CHANGE_PASSWORD = 7; - - public static final Map<Integer, String> EAP_OP_CODE_STRING = new HashMap<>(); - static { - EAP_OP_CODE_STRING.put(EAP_MSCHAP_V2_CHALLENGE, "Challenge"); - EAP_OP_CODE_STRING.put(EAP_MSCHAP_V2_RESPONSE, "Response"); - EAP_OP_CODE_STRING.put(EAP_MSCHAP_V2_SUCCESS, "Success"); - EAP_OP_CODE_STRING.put(EAP_MSCHAP_V2_FAILURE, "Failure"); - EAP_OP_CODE_STRING.put(EAP_MSCHAP_V2_CHANGE_PASSWORD, "Change-Password"); - } - - private static final Set<Integer> SUPPORTED_OP_CODES = new HashSet<>(); - static { - SUPPORTED_OP_CODES.add(EAP_MSCHAP_V2_CHALLENGE); - SUPPORTED_OP_CODES.add(EAP_MSCHAP_V2_RESPONSE); - SUPPORTED_OP_CODES.add(EAP_MSCHAP_V2_SUCCESS); - SUPPORTED_OP_CODES.add(EAP_MSCHAP_V2_FAILURE); - } - - public final int opCode; - - EapMsChapV2TypeData(int opCode) throws EapMsChapV2ParsingException { - this.opCode = opCode; - - if (!SUPPORTED_OP_CODES.contains(opCode)) { - throw new EapMsChapV2ParsingException("Unsupported opCode provided: " + opCode); - } - } - - /** - * Encodes this EapMsChapV2TypeData instance as a byte[]. - * - * @return byte[] representing the encoded value of this EapMsChapV2TypeData instance. - */ - public byte[] encode() { - throw new UnsupportedOperationException( - "encode() not supported by " + this.getClass().getSimpleName()); - } - - abstract static class EapMsChapV2VariableTypeData extends EapMsChapV2TypeData { - public final int msChapV2Id; - public final int msLength; - - EapMsChapV2VariableTypeData(int opCode, int msChapV2Id, int msLength) - throws EapMsChapV2ParsingException { - super(opCode); - - this.msChapV2Id = msChapV2Id; - this.msLength = msLength; - } - } - - /** - * EapMsChapV2ChallengeRequest represents the EAP MSCHAPv2 Challenge Packet (EAP MSCHAPv2#2.1). - */ - public static class EapMsChapV2ChallengeRequest extends EapMsChapV2VariableTypeData { - public static final int VALUE_SIZE = 16; - public static final int TYPE_DATA_HEADER_SIZE = 5; - - public final byte[] challenge = new byte[VALUE_SIZE]; - public final byte[] name; - - EapMsChapV2ChallengeRequest(ByteBuffer buffer) throws EapMsChapV2ParsingException { - super( - EAP_MSCHAP_V2_CHALLENGE, - Byte.toUnsignedInt(buffer.get()), - Short.toUnsignedInt(buffer.getShort())); - - int valueSize = Byte.toUnsignedInt(buffer.get()); - if (valueSize != VALUE_SIZE) { - throw new EapMsChapV2ParsingException("Challenge Value-Size must be 16"); - } - buffer.get(challenge); - - int nameLenBytes = msLength - VALUE_SIZE - TYPE_DATA_HEADER_SIZE; - if (nameLenBytes < 0) { - throw new EapMsChapV2ParsingException("Invalid MS-Length specified"); - } - - name = new byte[nameLenBytes]; - buffer.get(name); - } - - @VisibleForTesting - public EapMsChapV2ChallengeRequest( - int msChapV2Id, int msLength, byte[] challenge, byte[] name) - throws EapMsChapV2ParsingException { - super(EAP_MSCHAP_V2_CHALLENGE, msChapV2Id, msLength); - - if (challenge.length != VALUE_SIZE) { - throw new EapMsChapV2ParsingException("Challenge length must be 16"); - } - - System.arraycopy(challenge, 0, this.challenge, 0, VALUE_SIZE); - this.name = name; - } - } - - /** - * EapMsChapV2ChallengeResponse represents the EAP MSCHAPv2 Response Packet (EAP MSCHAPv2#2.2). - */ - public static class EapMsChapV2ChallengeResponse extends EapMsChapV2VariableTypeData { - public static final int VALUE_SIZE = 49; - public static final int PEER_CHALLENGE_SIZE = 16; - public static final int RESERVED_BYTES = 8; - public static final int NT_RESPONSE_SIZE = 24; - public static final int TYPE_DATA_HEADER_SIZE = 5; - - public final byte[] peerChallenge = new byte[PEER_CHALLENGE_SIZE]; - public final byte[] ntResponse = new byte[NT_RESPONSE_SIZE]; - public final int flags; - public final byte[] name; - - public EapMsChapV2ChallengeResponse( - int msChapV2Id, byte[] peerChallenge, byte[] ntResponse, int flags, byte[] name) - throws EapMsChapV2ParsingException { - super( - EAP_MSCHAP_V2_RESPONSE, - msChapV2Id, - TYPE_DATA_HEADER_SIZE + VALUE_SIZE + name.length); - - if (peerChallenge.length != PEER_CHALLENGE_SIZE) { - throw new EapMsChapV2ParsingException("Peer-Challenge must be 16B"); - } else if (ntResponse.length != NT_RESPONSE_SIZE) { - throw new EapMsChapV2ParsingException("NT-Response must be 24B"); - } else if (flags != 0) { - throw new EapMsChapV2ParsingException("Flags must be 0x00"); - } - - System.arraycopy(peerChallenge, 0, this.peerChallenge, 0, PEER_CHALLENGE_SIZE); - System.arraycopy(ntResponse, 0, this.ntResponse, 0, NT_RESPONSE_SIZE); - this.flags = flags; - this.name = name; - } - - @Override - public byte[] encode() { - ByteBuffer buffer = ByteBuffer.allocate(msLength); - buffer.put((byte) EAP_MSCHAP_V2_RESPONSE); - buffer.put((byte) msChapV2Id); - buffer.putShort((short) msLength); - buffer.put((byte) VALUE_SIZE); - buffer.put(peerChallenge); - buffer.put(new byte[RESERVED_BYTES]); - buffer.put(ntResponse); - buffer.put((byte) flags); - buffer.put(name); - - return buffer.array(); - } - } - - /** - * EapMsChapV2SuccessRequest represents the EAP MSCHAPv2 Success Request Packet - * (EAP MSCHAPv2#2.3). - */ - public static class EapMsChapV2SuccessRequest extends EapMsChapV2VariableTypeData { - private static final int AUTH_STRING_LEN_HEX = 40; - private static final int AUTH_STRING_LEN_BYTES = 20; - private static final int NUM_REQUIRED_ATTRIBUTES = 2; - private static final String AUTH_STRING_LABEL = "S"; - - public final byte[] authBytes = new byte[AUTH_STRING_LEN_BYTES]; - public final String message; - - EapMsChapV2SuccessRequest(ByteBuffer buffer) throws EapMsChapV2ParsingException { - super( - EAP_MSCHAP_V2_SUCCESS, - Byte.toUnsignedInt(buffer.get()), - Short.toUnsignedInt(buffer.getShort())); - - byte[] message = new byte[buffer.remaining()]; - buffer.get(message); - - // message formatting: "S=<auth_string> M=<message>" - Map<String, String> mappings = - getMessageMappings(new String(message, Charset.forName(ASCII_CHARSET_NAME))); - - if (!mappings.containsKey(AUTH_STRING_LABEL) - || mappings.size() != NUM_REQUIRED_ATTRIBUTES) { - throw new EapMsChapV2ParsingException( - "Auth message must be in the format: 'S=<auth_string> M=<message>'"); - } - - String authStringHex = mappings.get(AUTH_STRING_LABEL); - if (authStringHex.length() != AUTH_STRING_LEN_HEX) { - throw new EapMsChapV2ParsingException("Auth String must be 40 hex chars (20B)"); - } - byte[] authBytes = hexStringToByteArray(authStringHex); - System.arraycopy(authBytes, 0, this.authBytes, 0, AUTH_STRING_LEN_BYTES); - - this.message = mappings.get(MESSAGE_LABEL); - } - - @VisibleForTesting - public EapMsChapV2SuccessRequest( - int msChapV2Id, int msLength, byte[] authBytes, String message) - throws EapMsChapV2ParsingException { - super(EAP_MSCHAP_V2_SUCCESS, msChapV2Id, msLength); - - if (authBytes.length != AUTH_STRING_LEN_BYTES) { - throw new EapMsChapV2ParsingException("Auth String must be 20B"); - } - - System.arraycopy(authBytes, 0, this.authBytes, 0, AUTH_STRING_LEN_BYTES); - this.message = message; - } - } - - /** - * EapMsChapV2SuccessResponse represents the EAP MSCHAPv2 Success Response Packet - * (EAP MSCHAPv2#2.4). - */ - public static class EapMsChapV2SuccessResponse extends EapMsChapV2TypeData { - private EapMsChapV2SuccessResponse() throws EapMsChapV2ParsingException { - super(EAP_MSCHAP_V2_SUCCESS); - } - - /** - * Constructs and returns a new EAP MSCHAPv2 Success Response type data. - * - * @return a new EapMsChapV2SuccessResponse instance - */ - public static EapMsChapV2SuccessResponse getEapMsChapV2SuccessResponse() { - try { - return new EapMsChapV2SuccessResponse(); - } catch (EapMsChapV2ParsingException ex) { - // This should never happen - LOG.wtf( - EapMsChapV2SuccessResponse.class.getSimpleName(), - "ParsingException thrown while creating EapMsChapV2SuccessResponse", - ex); - return null; - } - } - - @Override - public byte[] encode() { - return new byte[] {(byte) EAP_MSCHAP_V2_SUCCESS}; - } - } - - /** - * EapMsChapV2FailureRequest represents the EAP MSCHAPv2 Failure Request Packet - * (EAP MSCHAPv2#2.5). - */ - public static class EapMsChapV2FailureRequest extends EapMsChapV2VariableTypeData { - private static final int NUM_REQUIRED_ATTRIBUTES = 5; - private static final int CHALLENGE_LENGTH = 16; - private static final String ERROR_LABEL = "E"; - private static final String RETRY_LABEL = "R"; - private static final String IS_RETRYABLE_FLAG = "1"; - private static final String CHALLENGE_LABEL = "C"; - private static final String PASSWORD_CHANGE_PROTOCOL_LABEL = "V"; - - // Error codes defined in EAP MSCHAPv2#2.5 - public static final Map<Integer, String> EAP_ERROR_CODE_STRING = new HashMap<>(); - static { - EAP_ERROR_CODE_STRING.put(646, "ERROR_RESTRICTED_LOGON_HOURS"); - EAP_ERROR_CODE_STRING.put(647, "ERROR_ACCT_DISABLED"); - EAP_ERROR_CODE_STRING.put(648, "ERROR_PASSWD_EXPIRED"); - EAP_ERROR_CODE_STRING.put(649, "ERROR_NO_DIALIN_PERMISSION"); - EAP_ERROR_CODE_STRING.put(691, "ERROR_AUTHENTICATION_FAILURE"); - EAP_ERROR_CODE_STRING.put(709, "ERROR_CHANGING_PASSWORD"); - } - - public final int errorCode; - public final boolean isRetryable; - public final byte[] challenge; - public final int passwordChangeProtocol; - public final String message; - - EapMsChapV2FailureRequest(ByteBuffer buffer) - throws EapMsChapV2ParsingException, NumberFormatException { - super( - EAP_MSCHAP_V2_FAILURE, - Byte.toUnsignedInt(buffer.get()), - Short.toUnsignedInt(buffer.getShort())); - - byte[] message = new byte[buffer.remaining()]; - buffer.get(message); - - // message formatting: - // "E=<error_code> R=<retry bit> C=<challenge> V=<password_change_protocol> M=<message>" - Map<String, String> mappings = - getMessageMappings(new String(message, Charset.forName(ASCII_CHARSET_NAME))); - if (!mappings.containsKey(ERROR_LABEL) - || !mappings.containsKey(RETRY_LABEL) - || !mappings.containsKey(CHALLENGE_LABEL) - || !mappings.containsKey(PASSWORD_CHANGE_PROTOCOL_LABEL) - || mappings.size() != NUM_REQUIRED_ATTRIBUTES) { - throw new EapMsChapV2ParsingException( - "Message must be formatted as: E=<error_code> R=<retry bit> C=<challenge>" - + " V=<password_change_protocol> M=<message>"); - } - - this.errorCode = Integer.parseInt(mappings.get(ERROR_LABEL)); - this.isRetryable = IS_RETRYABLE_FLAG.equals(mappings.get(RETRY_LABEL)); - this.challenge = hexStringToByteArray(mappings.get(CHALLENGE_LABEL)); - this.passwordChangeProtocol = Integer.parseInt(mappings.get( - PASSWORD_CHANGE_PROTOCOL_LABEL)); - this.message = mappings.get("M"); - - if (challenge.length != CHALLENGE_LENGTH) { - throw new EapMsChapV2ParsingException("Challenge must be 16B long"); - } - } - - @VisibleForTesting - public EapMsChapV2FailureRequest( - int msChapV2Id, - int msLength, - int errorCode, - boolean isRetryable, - byte[] challenge, - int passwordChangeProtocol, - String message) - throws EapMsChapV2ParsingException { - super(EAP_MSCHAP_V2_FAILURE, msChapV2Id, msLength); - - this.errorCode = errorCode; - this.isRetryable = isRetryable; - this.challenge = challenge; - this.passwordChangeProtocol = passwordChangeProtocol; - this.message = message; - - if (challenge.length != CHALLENGE_LENGTH) { - throw new EapMsChapV2ParsingException("Challenge length must be 16B"); - } - } - } - - /** - * EapMsChapV2FailureResponse represents the EAP MSCHAPv2 Failure Response Packet - * (EAP MSCHAPv2#2.6). - */ - public static class EapMsChapV2FailureResponse extends EapMsChapV2TypeData { - private EapMsChapV2FailureResponse() throws EapMsChapV2ParsingException { - super(EAP_MSCHAP_V2_FAILURE); - } - - /** - * Constructs and returns a new EAP MSCHAPv2 Failure Response type data. - * - * @return a new EapMsChapV2FailureResponse instance - */ - public static EapMsChapV2FailureResponse getEapMsChapV2FailureResponse() { - try { - return new EapMsChapV2FailureResponse(); - } catch (EapMsChapV2ParsingException ex) { - // This should never happen - LOG.wtf( - EapMsChapV2SuccessResponse.class.getSimpleName(), - "ParsingException thrown while creating EapMsChapV2FailureResponse", - ex); - return null; - } - } - - @Override - public byte[] encode() { - return new byte[] {(byte) EAP_MSCHAP_V2_FAILURE}; - } - } - - @VisibleForTesting - static Map<String, String> getMessageMappings(String message) - throws EapMsChapV2ParsingException { - Map<String, String> messageMappings = new HashMap<>(); - int mPos = message.indexOf(MESSAGE_PREFIX); - - String preMString; - if (mPos == -1) { - preMString = message; - messageMappings.put(MESSAGE_LABEL, "<omitted by authenticator>"); - } else { - preMString = message.substring(0, mPos); - messageMappings.put(MESSAGE_LABEL, message.substring(mPos + MESSAGE_PREFIX.length())); - } - - // preMString: "S=<auth string> " or "E=<error> R=r C=<challenge> V=<version> " - for (String value : preMString.split(" ")) { - String[] keyValue = value.split("="); - if (keyValue.length != LABEL_VALUE_LENGTH) { - throw new EapMsChapV2ParsingException( - "Message must be formatted <label character>=<value>"); - } else if (messageMappings.containsKey(keyValue[0])) { - throw new EapMsChapV2ParsingException( - "Duplicated key-value pair in message: " + LOG.pii(message)); - } - messageMappings.put(keyValue[0], keyValue[1]); - } - - return messageMappings; - } - - @VisibleForTesting - static byte[] hexStringToByteArray(String hexString) - throws EapMsChapV2ParsingException, NumberFormatException { - if (hexString.length() % 2 != 0) { - throw new EapMsChapV2ParsingException( - "Hex string must contain an even number of characters"); - } - - byte[] dataBytes = new byte[hexString.length() / 2]; - for (int i = 0; i < hexString.length(); i += 2) { - dataBytes[i / 2] = (byte) Integer.parseInt(hexString.substring(i, i + 2), 16); - } - return dataBytes; - } - - /** Class for decoding EAP MSCHAPv2 type data. */ - public static class EapMsChapV2TypeDataDecoder { - /** - * Returns the EAP MSCHAPv2 Op Code for the given EAP type data. - * - * @param eapTypeData byte[] type data to read the Op Code from - * @return the EAP MSCHAPv2 Op Code for the current type data - * @throws BufferUnderflowException iff eapTypeData.length == 0 - */ - public int getOpCode(byte[] eapTypeData) throws BufferUnderflowException { - return Byte.toUnsignedInt(ByteBuffer.wrap(eapTypeData).get()); - } - - /** - * Decodes and returns an EapMsChapV2ChallengeRequest for the specified eapTypeData. - * - * @param tag String for logging tag - * @param eapTypeData byte[] to be decoded as an EapMsChapV2ChallengeRequest instance - * @return DecodeResult wrapping an EapMsChapV2ChallengeRequest instance for the given - * eapTypeData iff the eapTypeData is formatted correctly. Otherwise, the DecodeResult - * wraps the appropriate EapError. - */ - public DecodeResult<EapMsChapV2ChallengeRequest> decodeChallengeRequest( - String tag, byte[] eapTypeData) { - try { - ByteBuffer buffer = ByteBuffer.wrap(eapTypeData); - int opCode = Byte.toUnsignedInt(buffer.get()); - - if (opCode != EAP_MSCHAP_V2_CHALLENGE) { - return new DecodeResult<>( - new EapError( - new EapMsChapV2ParsingException( - "Received type data with invalid opCode: " - + EAP_OP_CODE_STRING.getOrDefault( - opCode, "Unknown (" + opCode + ")")))); - } - - return new DecodeResult<>(new EapMsChapV2ChallengeRequest(buffer)); - } catch (BufferUnderflowException | EapMsChapV2ParsingException ex) { - LOG.e(tag, "Error parsing EAP MSCHAPv2 Challenge Request type data", ex); - return new DecodeResult<>(new EapError(ex)); - } - } - - /** - * Decodes and returns an EapMsChapV2SuccessRequest for the specified eapTypeData. - * - * @param tag String for logging tag - * @param eapTypeData byte[] to be decoded as an EapMsChapV2SuccessRequest instance - * @return DecodeResult wrapping an EapMsChapV2SuccessRequest instance for the given - * eapTypeData iff the eapTypeData is formatted correctly. Otherwise, the DecodeResult - * wraps the appropriate EapError. - */ - public DecodeResult<EapMsChapV2SuccessRequest> decodeSuccessRequest( - String tag, byte[] eapTypeData) { - try { - ByteBuffer buffer = ByteBuffer.wrap(eapTypeData); - int opCode = Byte.toUnsignedInt(buffer.get()); - - if (opCode != EAP_MSCHAP_V2_SUCCESS) { - return new DecodeResult<>( - new EapError( - new EapMsChapV2ParsingException( - "Received type data with invalid opCode: " - + EAP_OP_CODE_STRING.getOrDefault( - opCode, "Unknown (" + opCode + ")")))); - } - - return new DecodeResult<>(new EapMsChapV2SuccessRequest(buffer)); - } catch (BufferUnderflowException - | NumberFormatException - | EapMsChapV2ParsingException ex) { - LOG.e(tag, "Error parsing EAP MSCHAPv2 Success Request type data", ex); - return new DecodeResult<>(new EapError(ex)); - } - } - - /** - * Decodes and returns an EapMsChapV2FailureRequest for the specified eapTypeData. - * - * @param tag String for logging tag - * @param eapTypeData byte[] to be decoded as an EapMsChapV2FailureRequest instance - * @return DecodeResult wrapping an EapMsChapV2FailureRequest instance for the given - * eapTypeData iff the eapTypeData is formatted correctly. Otherwise, the DecodeResult - * wraps the appropriate EapError. - */ - public DecodeResult<EapMsChapV2FailureRequest> decodeFailureRequest( - String tag, byte[] eapTypeData) { - try { - ByteBuffer buffer = ByteBuffer.wrap(eapTypeData); - int opCode = Byte.toUnsignedInt(buffer.get()); - - if (opCode != EAP_MSCHAP_V2_FAILURE) { - return new DecodeResult<>( - new EapError( - new EapMsChapV2ParsingException( - "Received type data with invalid opCode: " - + EAP_OP_CODE_STRING.getOrDefault( - opCode, "Unknown (" + opCode + ")")))); - } - - return new DecodeResult<>(new EapMsChapV2FailureRequest(buffer)); - } catch (BufferUnderflowException - | NumberFormatException - | EapMsChapV2ParsingException ex) { - LOG.e(tag, "Error parsing EAP MSCHAPv2 Failure Request type data", ex); - return new DecodeResult<>(new EapError(ex)); - } - } - - /** - * DecodeResult represents the result from calling a decode method within - * EapMsChapV2TypeDataDecoder. It will contain either an EapMsChapV2TypeData or an EapError. - * - * @param <T> The EapMsChapV2TypeData type that is wrapped in this DecodeResult - */ - public static class DecodeResult<T extends EapMsChapV2TypeData> { - public final T eapTypeData; - public final EapError eapError; - - public DecodeResult(T eapTypeData) { - this.eapTypeData = eapTypeData; - this.eapError = null; - } - - public DecodeResult(EapError eapError) { - this.eapTypeData = null; - this.eapError = eapError; - } - - /** - * Checks whether this instance represents a successful decode operation. - * - * @return true iff this DecodeResult represents a successfully decoded Type Data - */ - public boolean isSuccessfulDecode() { - return eapTypeData != null; - } - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapAkaAttributeFactory.java b/src/java/com/android/internal/net/eap/message/simaka/EapAkaAttributeFactory.java deleted file mode 100644 index 5b687818..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapAkaAttributeFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTS; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_BIDDING; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RES; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.LENGTH_SCALING; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaUnsupportedAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAuts; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtBidding; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRes; - -import java.nio.ByteBuffer; - -/** - * EapAkaAttributeFactory is used for creating EAP-AKA attributes according to their type. - * - * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication - * Protocol for Authentication and Key Agreement (EAP-AKA)</a> - * @see <a href="https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml">EAP SIM/AKA - * Attributes</a> - */ -public class EapAkaAttributeFactory extends EapSimAkaAttributeFactory { - private static EapAkaAttributeFactory sInstance = new EapAkaAttributeFactory(); - - protected EapAkaAttributeFactory() {} - - public static EapAkaAttributeFactory getInstance() { - return sInstance; - } - - /** - * Decodes a single EapSimAkaAttribute object from the given ByteBuffer. - * - * <p>Decoding logic is based on Attribute definitions in RFC 4187#10. - * - * @param byteBuffer The ByteBuffer to parse the current attribute from - * @return The current EapSimAkaAttribute to be parsed, or EapSimAkaUnsupportedAttribute if the - * given attributeType is skippable and unsupported - * @throws EapSimAkaInvalidAttributeException when a malformatted attribute is attempted to be - * decoded - * @throws EapSimAkaUnsupportedAttributeException when an unsupported, unskippable Attribute is - * attempted to be decoded - */ - public EapSimAkaAttribute getAttribute(ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException, EapSimAkaUnsupportedAttributeException { - int attributeType = Byte.toUnsignedInt(byteBuffer.get()); - - // Length is given as a multiple of 4x bytes (RFC 4187#8.1) - int lengthInBytes = Byte.toUnsignedInt(byteBuffer.get()) * LENGTH_SCALING; - - return getAttribute(attributeType, lengthInBytes, byteBuffer); - } - - @Override - protected EapSimAkaAttribute getAttribute( - int attributeType, int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException, EapSimAkaUnsupportedAttributeException { - switch (attributeType) { - case EAP_AT_RAND: - return new AtRandAka(lengthInBytes, byteBuffer); - case EAP_AT_AUTN: - return new AtAutn(lengthInBytes, byteBuffer); - case EAP_AT_RES: - return new AtRes(lengthInBytes, byteBuffer); - case EAP_AT_AUTS: - return new AtAuts(lengthInBytes, byteBuffer); - case EAP_AT_BIDDING: - return new AtBidding(lengthInBytes, byteBuffer); - default: - return super.getAttribute(attributeType, lengthInBytes, byteBuffer); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeAttributeFactory.java b/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeAttributeFactory.java deleted file mode 100644 index 46f97e53..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeAttributeFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF_INPUT; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.LENGTH_SCALING; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaUnsupportedAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdf; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdfInput; - -import java.nio.ByteBuffer; - -/** - * EapAkaPrimeAttributeFactory is used for creating EAP-AKA' attributes according to their type. - * - * @see <a href="https://tools.ietf.org/html/rfc5448">RFC 5448, Improved Extensible Authentication - * Protocol Method for 3rd Generation Authentication and Key Agreement (EAP-AKA')</a> - * @see <a href="https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml">EAP SIM/AKA - * Attributes</a> - */ -public class EapAkaPrimeAttributeFactory extends EapAkaAttributeFactory { - private static EapAkaPrimeAttributeFactory sInstance = new EapAkaPrimeAttributeFactory(); - - private EapAkaPrimeAttributeFactory() {} - - public static EapAkaPrimeAttributeFactory getInstance() { - return sInstance; - } - - /** - * Decodes a single EapSimAkaAttribute object from the given ByteBuffer. - * - * <p>Decoding logic is based on Attribute definitions in RFC 5448#10. - * - * @param byteBuffer The ByteBuffer to parse the current attribute from - * @return The current EapSimAkaAttribute to be parsed, or EapSimAkaUnsupportedAttribute if the - * given attributeType is skippable and unsupported - * @throws EapSimAkaInvalidAttributeException when a malformatted attribute is attempted to be - * decoded - * @throws EapSimAkaUnsupportedAttributeException when an unsupported, unskippable Attribute is - * attempted to be decoded - */ - @Override - public EapSimAkaAttribute getAttribute(ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException, EapSimAkaUnsupportedAttributeException { - int attributeType = Byte.toUnsignedInt(byteBuffer.get()); - - // Length is given as a multiple of 4x bytes (RFC 4187#8.1) - int lengthInBytes = Byte.toUnsignedInt(byteBuffer.get()) * LENGTH_SCALING; - - switch (attributeType) { - case EAP_AT_KDF_INPUT: - return new AtKdfInput(lengthInBytes, byteBuffer); - case EAP_AT_KDF: - return new AtKdf(lengthInBytes, byteBuffer); - default: - return super.getAttribute(attributeType, lengthInBytes, byteBuffer); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeTypeData.java b/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeTypeData.java deleted file mode 100644 index 23dcd024..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeTypeData.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; - -import java.util.LinkedHashMap; -import java.util.List; - -/** - * EapAkaPrimeTypeData represents the Type Data for an {@link EapMessage} during an EAP-AKA' - * session. - */ -public class EapAkaPrimeTypeData extends EapAkaTypeData { - private static final EapAkaPrimeTypeDataDecoder sTypeDataDecoder = - new EapAkaPrimeTypeDataDecoder(); - - @VisibleForTesting - EapAkaPrimeTypeData( - int eapSubType, LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - super(eapSubType, attributeMap); - } - - /** - * Creates and returns an EapAkaPrimeTypeData instance with the given subtype and attributes. - * - * @param eapSubtype the subtype for the EAP-AKA type data - * @param attributes the List of EapSimAkaAttributes to be included in this type data - */ - public EapAkaPrimeTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { - super(eapSubtype, attributes); - } - - public static EapAkaPrimeTypeDataDecoder getEapAkaPrimeTypeDataDecoder() { - return sTypeDataDecoder; - } - - /** - * EapAkaTypeDataDecoder will be used for decoding {@link EapAkaPrimeTypeData} objects. - */ - public static class EapAkaPrimeTypeDataDecoder - extends EapSimAkaTypeDataDecoder<EapAkaTypeData> { - private static final String TAG = EapAkaPrimeTypeDataDecoder.class.getSimpleName(); - private static final String EAP_METHOD = "EAP-AKA'"; - - protected EapAkaPrimeTypeDataDecoder() { - super( - TAG, - EAP_METHOD, - SUPPORTED_SUBTYPES, - EapAkaPrimeAttributeFactory.getInstance(), - EAP_AKA_SUBTYPE_STRING); - } - - /** - * Decodes the given byte-array into a DecodeResult object. - * - * <p>Note that <b>only 1 KDF value</b> is allowed. If multiple AT_KDF attributes are - * supplied, a {@link DecodeResult} wrapping a {@link AtClientErrorCode#UNABLE_TO_PROCESS} - * will be returned. - * - * @param typeData the byte-encoding of the EapAkaPrimeTypeData to be parsed - * @return a DecodeResult object. If the decoding is successful, this will encapsulate an - * EapAkaPrimeTypeData instance representing the data stored in typeData. Otherwise, it - * will contain the relevant AtClientErrorCode for the decoding error. - */ - public DecodeResult<EapAkaTypeData> decode(@NonNull byte[] typeData) { - return super.decode(typeData); - } - - @Override - protected EapAkaPrimeTypeData getInstance( - int eapSubtype, LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - return new EapAkaPrimeTypeData(eapSubtype, attributeMap); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapAkaTypeData.java b/src/java/com/android/internal/net/eap/message/simaka/EapAkaTypeData.java deleted file mode 100644 index e3ce817e..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapAkaTypeData.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.message.EapMessage; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * EapAkaTypeData represents the Type Data for an {@link EapMessage} during an EAP-AKA session. - */ -public class EapAkaTypeData extends EapSimAkaTypeData { - private static final String TAG = EapAkaTypeData.class.getSimpleName(); - - // EAP-AKA Subtype values defined by IANA - // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml - public static final int EAP_AKA_CHALLENGE = 1; - public static final int EAP_AKA_AUTHENTICATION_REJECT = 2; - public static final int EAP_AKA_SYNCHRONIZATION_FAILURE = 4; - public static final int EAP_AKA_IDENTITY = 5; - public static final int EAP_AKA_NOTIFICATION = 12; - public static final int EAP_AKA_REAUTHENTICATION = 13; - public static final int EAP_AKA_CLIENT_ERROR = 14; - - public static final Map<Integer, String> EAP_AKA_SUBTYPE_STRING = new HashMap<>(); - static { - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_CHALLENGE, "Challenge"); - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_AUTHENTICATION_REJECT, "Authentication-Reject"); - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_SYNCHRONIZATION_FAILURE, "Synchronization-Failure"); - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_IDENTITY, "Identity"); - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_NOTIFICATION, "Notification"); - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_REAUTHENTICATION, "Re-authentication"); - EAP_AKA_SUBTYPE_STRING.put(EAP_AKA_CLIENT_ERROR, "Client-Error"); - } - - protected static final Set<Integer> SUPPORTED_SUBTYPES = new HashSet<>(); - static { - SUPPORTED_SUBTYPES.add(EAP_AKA_CHALLENGE); - SUPPORTED_SUBTYPES.add(EAP_AKA_AUTHENTICATION_REJECT); - SUPPORTED_SUBTYPES.add(EAP_AKA_SYNCHRONIZATION_FAILURE); - SUPPORTED_SUBTYPES.add(EAP_AKA_IDENTITY); - SUPPORTED_SUBTYPES.add(EAP_AKA_NOTIFICATION); - SUPPORTED_SUBTYPES.add(EAP_AKA_REAUTHENTICATION); - SUPPORTED_SUBTYPES.add(EAP_AKA_CLIENT_ERROR); - } - - private static final EapAkaTypeDataDecoder sTypeDataDecoder = new EapAkaTypeDataDecoder(); - - @VisibleForTesting - public EapAkaTypeData(int eapSubType, LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - super(eapSubType, attributeMap); - } - - /** - * Creates and returns an EapAkaTypeData instance with the given subtype and attributes. - * - * @param eapSubtype the subtype for the EAP-AKA type data - * @param attributes the List of EapSimAkaAttributes to be included in this type data - */ - public EapAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { - super(eapSubtype, new LinkedHashMap<>()); - - if (!SUPPORTED_SUBTYPES.contains(eapSubtype)) { - throw new IllegalArgumentException("Invalid subtype for EAP-AKA: " + eapSubtype); - } - - for (EapSimAkaAttribute attribute : attributes) { - if (attributeMap.containsKey(attribute.attributeType)) { - throw new IllegalArgumentException( - "Duplicate attribute in attributes: " + attribute.attributeType); - } - attributeMap.put(attribute.attributeType, attribute); - } - } - - public static EapAkaTypeDataDecoder getEapAkaTypeDataDecoder() { - return sTypeDataDecoder; - } - - /** - * EapAkaTypeDataDecoder will be used for decoding {@link EapAkaTypeData} objects. - */ - public static class EapAkaTypeDataDecoder extends EapSimAkaTypeDataDecoder<EapAkaTypeData> { - private static final String TAG = EapAkaTypeDataDecoder.class.getSimpleName(); - private static final String EAP_METHOD = "EAP-AKA"; - - protected EapAkaTypeDataDecoder() { - super( - TAG, - EAP_METHOD, - SUPPORTED_SUBTYPES, - EapAkaAttributeFactory.getInstance(), - EAP_AKA_SUBTYPE_STRING); - } - - /** - * Decodes the given byte-array into a DecodeResult object. - * - * @param typeData the byte-encoding of the EapAkaTypeData to be parsed - * @return a DecodeResult object. If the decoding is successful, this will encapsulate an - * EapAkaTypeData instance representing the data stored in typeData. Otherwise, it - * will contain the relevant AtClientErrorCode for the decoding error. - */ - public DecodeResult<EapAkaTypeData> decode(@NonNull byte[] typeData) { - return super.decode(typeData); - } - - @Override - protected EapAkaTypeData getInstance( - int eapSubtype, - LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - return new EapAkaTypeData(eapSubtype, attributeMap); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttribute.java b/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttribute.java deleted file mode 100644 index 73d6752d..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttribute.java +++ /dev/null @@ -1,1162 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAtPaddingException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimInvalidAtRandException; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * EapSimAkaAttribute represents a single EAP SIM/AKA Attribute. - * - * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication - * Protocol for Subscriber Identity Modules (EAP-SIM)</a> - * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication - * Protocol for Authentication and Key Agreement (EAP-AKA)</a> - * @see <a href="https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml">EAP SIM/AKA - * Attributes</a> - */ -public abstract class EapSimAkaAttribute { - static final int LENGTH_SCALING = 4; - - private static final int MIN_ATTR_LENGTH = 4; - - public static final int SKIPPABLE_ATTRIBUTE_RANGE_START = 128; - - // EAP non-Skippable Attribute values defined by IANA - // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml - public static final int EAP_AT_RAND = 1; - public static final int EAP_AT_AUTN = 2; - public static final int EAP_AT_RES = 3; - public static final int EAP_AT_AUTS = 4; - public static final int EAP_AT_PADDING = 6; - public static final int EAP_AT_NONCE_MT = 7; - public static final int EAP_AT_PERMANENT_ID_REQ = 10; - public static final int EAP_AT_MAC = 11; - public static final int EAP_AT_NOTIFICATION = 12; - public static final int EAP_AT_ANY_ID_REQ = 13; - public static final int EAP_AT_IDENTITY = 14; - public static final int EAP_AT_VERSION_LIST = 15; - public static final int EAP_AT_SELECTED_VERSION = 16; - public static final int EAP_AT_FULLAUTH_ID_REQ = 17; - public static final int EAP_AT_COUNTER = 19; - public static final int EAP_AT_COUNTER_TOO_SMALL = 20; - public static final int EAP_AT_NONCE_S = 21; - public static final int EAP_AT_CLIENT_ERROR_CODE = 22; - public static final int EAP_AT_KDF_INPUT = 23; - public static final int EAP_AT_KDF = 24; - - // EAP Skippable Attribute values defined by IANA - // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml - public static final int EAP_AT_IV = 129; - public static final int EAP_AT_ENCR_DATA = 130; - public static final int EAP_AT_NEXT_PSEUDONYM = 132; - public static final int EAP_AT_NEXT_REAUTH_ID = 133; - public static final int EAP_AT_CHECKCODE = 134; - public static final int EAP_AT_RESULT_IND = 135; - public static final int EAP_AT_BIDDING = 136; - - public static final Map<Integer, String> EAP_ATTRIBUTE_STRING = new HashMap<>(); - static { - EAP_ATTRIBUTE_STRING.put(EAP_AT_RAND, "AT_RAND"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_AUTN, "AT_AUTN"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_RES, "AT_RES"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_AUTS, "AT_AUTS"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_PADDING, "AT_PADDING"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_NONCE_MT, "AT_NONCE_MT"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_PERMANENT_ID_REQ, "AT_PERMANENT_ID_REQ"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_MAC, "AT_MAC"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_NOTIFICATION, "AT_NOTIFICATION"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_ANY_ID_REQ, "AT_ANY_ID_REQ"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_IDENTITY, "AT_IDENTITY"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_VERSION_LIST, "AT_VERSION_LIST"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_SELECTED_VERSION, "AT_SELECTED_VERSION"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_FULLAUTH_ID_REQ, "AT_FULLAUTH_ID_REQ"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_COUNTER, "AT_COUNTER"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_COUNTER_TOO_SMALL, "AT_COUNTER_TOO_SMALL"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_NONCE_S, "AT_NONCE_S"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_CLIENT_ERROR_CODE, "AT_CLIENT_ERROR_CODE"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_KDF_INPUT, "AT_KDF_INPUT"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_KDF, "AT_KDF"); - - EAP_ATTRIBUTE_STRING.put(EAP_AT_IV, "AT_IV"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_ENCR_DATA, "AT_ENCR_DATA"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_NEXT_PSEUDONYM, "AT_NEXT_PSEUDONYM"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_NEXT_REAUTH_ID, "AT_NEXT_REAUTH_ID"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_CHECKCODE, "AT_CHECKCODE"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_RESULT_IND, "AT_RESULT_IND"); - EAP_ATTRIBUTE_STRING.put(EAP_AT_BIDDING, "AT_BIDDING"); - } - - public final int attributeType; - public final int lengthInBytes; - - protected EapSimAkaAttribute(int attributeType, int lengthInBytes) - throws EapSimAkaInvalidAttributeException { - this.attributeType = attributeType; - this.lengthInBytes = lengthInBytes; - - if (lengthInBytes % LENGTH_SCALING != 0) { - throw new EapSimAkaInvalidAttributeException("Attribute length must be multiple of 4"); - } - } - - /** - * Encodes this EapSimAkaAttribute into the given ByteBuffer - * - * @param byteBuffer the ByteBuffer that this instance will be written to - */ - public abstract void encode(ByteBuffer byteBuffer); - - protected void encodeAttributeHeader(ByteBuffer byteBuffer) { - byteBuffer.put((byte) attributeType); - byteBuffer.put((byte) (lengthInBytes / LENGTH_SCALING)); - } - - void consumePadding(int bytesUsed, ByteBuffer byteBuffer) { - int paddingRemaining = lengthInBytes - bytesUsed; - byteBuffer.get(new byte[paddingRemaining]); - } - - void addPadding(int bytesUsed, ByteBuffer byteBuffer) { - int paddingNeeded = lengthInBytes - bytesUsed; - byteBuffer.put(new byte[paddingNeeded]); - } - - /** - * EapSimAkaUnsupportedAttribute represents any unsupported, skippable EAP-SIM attribute. - */ - public static class EapSimAkaUnsupportedAttribute extends EapSimAkaAttribute { - // Attribute Type (1B) + Attribute Length (1B) = 2B Header - private static final int HEADER_BYTES = 2; - - public final byte[] data; - - public EapSimAkaUnsupportedAttribute( - int attributeType, - int lengthInBytes, - ByteBuffer byteBuffer) throws EapSimAkaInvalidAttributeException { - super(attributeType, lengthInBytes); - - // Attribute not supported, but remaining attribute still needs to be saved - int remainingBytes = lengthInBytes - HEADER_BYTES; - data = new byte[remainingBytes]; - byteBuffer.get(data); - } - - @VisibleForTesting - public EapSimAkaUnsupportedAttribute(int attributeType, int lengthInBytes, byte[] data) - throws EapSimAkaInvalidAttributeException { - super(attributeType, lengthInBytes); - this.data = data; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(data); - } - } - - /** - * AtVersionList represents the AT_VERSION_LIST attribute defined in RFC 4186#10.2 - */ - public static class AtVersionList extends EapSimAkaAttribute { - private static final int BYTES_PER_VERSION = 2; - - public final List<Integer> versions = new ArrayList<>(); - - public AtVersionList(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_VERSION_LIST, lengthInBytes); - - // number of bytes used to represent list (RFC 4186 Section 10.2) - int bytesInList = Short.toUnsignedInt(byteBuffer.getShort()); - if (bytesInList % BYTES_PER_VERSION != 0) { - throw new EapSimAkaInvalidAttributeException( - "Actual Version List Length must be multiple of 2"); - } - - int numVersions = bytesInList / BYTES_PER_VERSION; - for (int i = 0; i < numVersions; i++) { - versions.add(Short.toUnsignedInt(byteBuffer.getShort())); - } - - int bytesUsed = MIN_ATTR_LENGTH + (BYTES_PER_VERSION * versions.size()); - consumePadding(bytesUsed, byteBuffer); - } - - @VisibleForTesting - public AtVersionList(int lengthInBytes, int... versions) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_VERSION_LIST, lengthInBytes); - for (int version : versions) { - this.versions.add(version); - } - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - - byteBuffer.putShort((short) (versions.size() * BYTES_PER_VERSION)); - for (int i : versions) { - byteBuffer.putShort((short) i); - } - - int bytesUsed = MIN_ATTR_LENGTH + (BYTES_PER_VERSION * versions.size()); - addPadding(bytesUsed, byteBuffer); - } - } - - /** - * AtSelectedVersion represents the AT_SELECTED_VERSION attribute defined in RFC 4186#10.3 - */ - public static class AtSelectedVersion extends EapSimAkaAttribute { - private static final String TAG = AtSelectedVersion.class.getSimpleName(); - private static final int LENGTH = LENGTH_SCALING; - - public static final int SUPPORTED_VERSION = 1; - - public final int selectedVersion; - - public AtSelectedVersion(int lengthInBytes, int selectedVersion) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_SELECTED_VERSION, LENGTH); - this.selectedVersion = selectedVersion; - - if (lengthInBytes != LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - } - - @VisibleForTesting - public AtSelectedVersion(int selectedVersion) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_SELECTED_VERSION, LENGTH); - this.selectedVersion = selectedVersion; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.putShort((short) selectedVersion); - } - - /** - * Constructs and returns an AtSelectedVersion for the only supported version of EAP-SIM - * - * @return an AtSelectedVersion for the supported version (1) of EAP-SIM - */ - public static AtSelectedVersion getSelectedVersion() { - try { - return new AtSelectedVersion(LENGTH, SUPPORTED_VERSION); - } catch (EapSimAkaInvalidAttributeException ex) { - // this should never happen - LOG.wtf(TAG, - "Error thrown while creating AtSelectedVersion with correct length", ex); - throw new AssertionError("Impossible exception encountered", ex); - } - } - } - - /** - * AtNonceMt represents the AT_NONCE_MT attribute defined in RFC 4186#10.4 - */ - public static class AtNonceMt extends EapSimAkaAttribute { - private static final int LENGTH = 5 * LENGTH_SCALING; - private static final int RESERVED_BYTES = 2; - - public static final int NONCE_MT_LENGTH = 16; - - public final byte[] nonceMt = new byte[NONCE_MT_LENGTH]; - - public AtNonceMt(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_NONCE_MT, LENGTH); - if (lengthInBytes != LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - // next two bytes are reserved (RFC 4186 Section 10.4) - byteBuffer.get(new byte[RESERVED_BYTES]); - byteBuffer.get(nonceMt); - } - - @VisibleForTesting - public AtNonceMt(byte[] nonceMt) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_NONCE_MT, LENGTH); - - if (nonceMt.length != NONCE_MT_LENGTH) { - throw new EapSimAkaInvalidAttributeException("NonceMt length must be 16B"); - } - System.arraycopy(nonceMt, 0, this.nonceMt, 0, NONCE_MT_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - byteBuffer.put(nonceMt); - } - } - - private abstract static class AtIdReq extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = LENGTH_SCALING; - private static final int RESERVED_BYTES = 2; - - protected AtIdReq(int lengthInBytes, int attributeType, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(attributeType, ATTR_LENGTH); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - // next two bytes are reserved (RFC 4186 Section 10.5-10.7) - byteBuffer.get(new byte[RESERVED_BYTES]); - } - - @VisibleForTesting - protected AtIdReq(int attributeType) throws EapSimAkaInvalidAttributeException { - super(attributeType, ATTR_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - } - } - - /** - * AtPermanentIdReq represents the AT_PERMANENT_ID_REQ attribute defined in RFC 4186#10.5 and - * RFC 4187#10.2 - */ - public static class AtPermanentIdReq extends AtIdReq { - public AtPermanentIdReq(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(lengthInBytes, EAP_AT_PERMANENT_ID_REQ, byteBuffer); - } - - @VisibleForTesting - public AtPermanentIdReq() throws EapSimAkaInvalidAttributeException { - super(EAP_AT_PERMANENT_ID_REQ); - } - } - - /** - * AtAnyIdReq represents the AT_ANY_ID_REQ attribute defined in RFC 4186#10.6 and RFC 4187#10.3 - */ - public static class AtAnyIdReq extends AtIdReq { - public AtAnyIdReq(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(lengthInBytes, EAP_AT_ANY_ID_REQ, byteBuffer); - } - - @VisibleForTesting - public AtAnyIdReq() throws EapSimAkaInvalidAttributeException { - super(EAP_AT_ANY_ID_REQ); - } - } - - /** - * AtFullauthIdReq represents the AT_FULLAUTH_ID_REQ attribute defined in RFC 4186#10.7 and RFC - * 4187#10.4 - */ - public static class AtFullauthIdReq extends AtIdReq { - public AtFullauthIdReq(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(lengthInBytes, EAP_AT_FULLAUTH_ID_REQ, byteBuffer); - } - - @VisibleForTesting - public AtFullauthIdReq() throws EapSimAkaInvalidAttributeException { - super(EAP_AT_FULLAUTH_ID_REQ); - } - } - - /** - * AtIdentity represents the AT_IDENTITY attribute defined in RFC 4186#10.8 and RFC 4187#10.5 - */ - public static class AtIdentity extends EapSimAkaAttribute { - public final byte[] identity; - - public AtIdentity(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_IDENTITY, lengthInBytes); - - int identityLength = Short.toUnsignedInt(byteBuffer.getShort()); - identity = new byte[identityLength]; - byteBuffer.get(identity); - - int bytesUsed = MIN_ATTR_LENGTH + identityLength; - consumePadding(bytesUsed, byteBuffer); - } - - @VisibleForTesting - public AtIdentity(int lengthInBytes, byte[] identity) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_IDENTITY, lengthInBytes); - this.identity = identity; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.putShort((short) identity.length); - byteBuffer.put(identity); - - int bytesUsed = MIN_ATTR_LENGTH + identity.length; - addPadding(bytesUsed, byteBuffer); - } - - /** - * Creates and returns an AtIdentity instance for the given identity. - * - * @param identity byte-array representing the identity for the AtIdentity - * @return AtIdentity instance for the given identity byte-array - */ - public static AtIdentity getAtIdentity(byte[] identity) - throws EapSimAkaInvalidAttributeException { - int lengthInBytes = MIN_ATTR_LENGTH + identity.length; - if (lengthInBytes % LENGTH_SCALING != 0) { - lengthInBytes += LENGTH_SCALING - (lengthInBytes % LENGTH_SCALING); - } - - return new AtIdentity(lengthInBytes, identity); - } - } - - /** - * AtRandSim represents the AT_RAND attribute for EAP-SIM defined in RFC 4186#10.9 - */ - public static class AtRandSim extends EapSimAkaAttribute { - private static final int RAND_LENGTH = 16; - private static final int RESERVED_BYTES = 2; - private static final int MIN_RANDS = 2; - private static final int MAX_RANDS = 3; - - public final List<byte[]> rands = new ArrayList<>(MAX_RANDS); - - public AtRandSim(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_RAND, lengthInBytes); - - // next two bytes are reserved (RFC 4186 Section 10.9) - byteBuffer.get(new byte[RESERVED_BYTES]); - - int numRands = (lengthInBytes - MIN_ATTR_LENGTH) / RAND_LENGTH; - if (!isValidNumRands(numRands)) { - throw new EapSimInvalidAtRandException("Unexpected number of rands: " + numRands); - } - - for (int i = 0; i < numRands; i++) { - byte[] rand = new byte[RAND_LENGTH]; - byteBuffer.get(rand); - - // check for rand being unique (RFC 4186 Section 10.9) - for (int j = 0; j < i; j++) { - byte[] otherRand = rands.get(j); - if (Arrays.equals(rand, otherRand)) { - throw new EapSimAkaInvalidAttributeException("Received identical RANDs"); - } - } - rands.add(rand); - } - } - - @VisibleForTesting - public AtRandSim(int lengthInBytes, byte[]... rands) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_RAND, lengthInBytes); - - if (!isValidNumRands(rands.length)) { - throw new EapSimInvalidAtRandException("Unexpected number of rands: " - + rands.length); - } - for (byte[] rand : rands) { - this.rands.add(rand); - } - } - - private boolean isValidNumRands(int numRands) { - // numRands is valid iff 2 <= numRands <= 3 - return MIN_RANDS <= numRands && numRands <= MAX_RANDS; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - - for (byte[] rand : rands) { - byteBuffer.put(rand); - } - } - } - - /** - * AtRandAka represents the AT_RAND attribute for EAP-AKA defined in RFC 4187#10.6 - */ - public static class AtRandAka extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; - private static final int RAND_LENGTH = 16; - private static final int RESERVED_BYTES = 2; - - public final byte[] rand = new byte[RAND_LENGTH]; - - public AtRandAka(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_RAND, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Length must be 20B"); - } - - // next two bytes are reserved (RFC 4187#10.6) - byteBuffer.get(new byte[RESERVED_BYTES]); - - byteBuffer.get(rand); - } - - @VisibleForTesting - public AtRandAka(byte[] rand) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_RAND, ATTR_LENGTH); - - if (rand.length != RAND_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Rand must be 16B"); - } - - System.arraycopy(rand, 0, this.rand, 0, RAND_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - byteBuffer.put(rand); - } - } - - /** - * AtPadding represents the AT_PADDING attribute defined in RFC 4186#10.12 and RFC 4187#10.12 - */ - public static class AtPadding extends EapSimAkaAttribute { - private static final int ATTR_HEADER = 2; - - public AtPadding(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_PADDING, lengthInBytes); - - int remainingBytes = lengthInBytes - ATTR_HEADER; - for (int i = 0; i < remainingBytes; i++) { - // Padding must be checked to all be 0x00 bytes (RFC 4186 Section 10.12) - if (byteBuffer.get() != 0) { - throw new EapSimAkaInvalidAtPaddingException("Padding bytes must all be 0x00"); - } - } - } - - @VisibleForTesting - public AtPadding(int lengthInBytes) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_PADDING, lengthInBytes); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - - addPadding(ATTR_HEADER, byteBuffer); - } - } - - /** - * AtMac represents the AT_MAC attribute defined in RFC 4186#10.14 and RFC 4187#10.15 - */ - public static class AtMac extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; - private static final int RESERVED_BYTES = 2; - - public static final int MAC_LENGTH = 4 * LENGTH_SCALING; - - public final byte[] mac; - - public AtMac(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_MAC, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - // next two bytes are reserved (RFC 4186 Section 10.14) - byteBuffer.get(new byte[RESERVED_BYTES]); - - mac = new byte[MAC_LENGTH]; - byteBuffer.get(mac); - } - - // Used for calculating MACs. Per RFC 4186 Section 10.14, the MAC should be calculated over - // the entire packet, with the value field of the MAC attribute set to zero. - public AtMac() throws EapSimAkaInvalidAttributeException { - super(EAP_AT_MAC, ATTR_LENGTH); - mac = new byte[MAC_LENGTH]; - } - - public AtMac(byte[] mac) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_MAC, ATTR_LENGTH); - this.mac = mac; - - if (mac.length != MAC_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid length for MAC"); - } - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - byteBuffer.put(mac); - } - } - - /** - * AtCounter represents the AT_COUNTER attribute defined in RFC 4186#10.15 and RFC 4187#10.16 - */ - public static class AtCounter extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = LENGTH_SCALING; - - public final int counter; - - public AtCounter(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_COUNTER, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - this.counter = Short.toUnsignedInt(byteBuffer.getShort()); - } - - @VisibleForTesting - public AtCounter(int counter) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_COUNTER, ATTR_LENGTH); - this.counter = counter; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.putShort((short) counter); - } - } - - - /** - * AtCounterTooSmall represents the AT_COUNTER_TOO_SMALL attribute defined in RFC 4186#10.16 and - * RFC 4187#10.17 - */ - public static class AtCounterTooSmall extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = LENGTH_SCALING; - private static final int ATTR_HEADER = 2; - - public AtCounterTooSmall(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_COUNTER_TOO_SMALL, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - consumePadding(ATTR_HEADER, byteBuffer); - } - - public AtCounterTooSmall() throws EapSimAkaInvalidAttributeException { - super(EAP_AT_COUNTER_TOO_SMALL, ATTR_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - addPadding(ATTR_HEADER, byteBuffer); - } - } - - /** - * AtNonceS represents the AT_NONCE_S attribute defined in RFC 4186#10.17 and RFC 4187#10.18 - * - * <p>This Nonce is generated by the server and used for fast re-authentication only. - */ - public static class AtNonceS extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; - private static final int NONCE_S_LENGTH = 4 * LENGTH_SCALING; - private static final int RESERVED_BYTES = 2; - - public final byte[] nonceS = new byte[NONCE_S_LENGTH]; - - public AtNonceS(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_NONCE_S, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - // next two bytes are reserved (RFC 4186 Section 10.17) - byteBuffer.get(new byte[RESERVED_BYTES]); - byteBuffer.get(nonceS); - } - - @VisibleForTesting - public AtNonceS(byte[] nonceS) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_NONCE_S, ATTR_LENGTH); - - if (nonceS.length != NONCE_S_LENGTH) { - throw new EapSimAkaInvalidAttributeException("NonceS length must be 16B"); - } - - System.arraycopy(nonceS, 0, this.nonceS, 0, NONCE_S_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - byteBuffer.put(nonceS); - } - } - - /** - * AtNotification represents the AT_NOTIFICATION attribute defined in RFC 4186#10.18 and RFC - * 4187#10.19 - */ - public static class AtNotification extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = 4; - private static final int SUCCESS_MASK = 0x8000; - private static final int PRE_SUCCESSFUL_CHALLENGE_MASK = 0x4000; - - // Notification codes defined in RFC 4186 Section 10.18 - public static final int GENERAL_FAILURE_POST_CHALLENGE = 0; - public static final int GENERAL_FAILURE_PRE_CHALLENGE = 16384; // 0x4000 - public static final int SUCCESS = 32768; // 0x8000 - public static final int DENIED_ACCESS_POST_CHALLENGE = 1026; - public static final int USER_NOT_SUBSCRIBED_POST_CHALLENGE = 1031; - - private static final Map<Integer, String> CODE_DEFS = loadCodeDefs(); - - public final boolean isSuccessCode; - public final boolean isPreSuccessfulChallenge; - public final int notificationCode; - - public AtNotification(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_NOTIFICATION, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - notificationCode = Short.toUnsignedInt(byteBuffer.getShort()); - - // If Success bit == 0, failure is implied - isSuccessCode = (notificationCode & SUCCESS_MASK) != 0; - - // if Phase bit == 0, notification code can only be used after a successful - isPreSuccessfulChallenge = (notificationCode & PRE_SUCCESSFUL_CHALLENGE_MASK) != 0; - - if (isSuccessCode && isPreSuccessfulChallenge) { - throw new EapSimAkaInvalidAttributeException("Invalid state specified"); - } - } - - @VisibleForTesting - public AtNotification(int notificationCode) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_NOTIFICATION, ATTR_LENGTH); - this.notificationCode = notificationCode; - - // If Success bit == 0, failure is implied - isSuccessCode = (notificationCode & SUCCESS_MASK) != 0; - - // if Phase bit == 0, notification code can only be used after a successful challenge - isPreSuccessfulChallenge = (notificationCode & PRE_SUCCESSFUL_CHALLENGE_MASK) != 0; - - if (isSuccessCode && isPreSuccessfulChallenge) { - throw new EapSimAkaInvalidAttributeException("Invalid state specified"); - } - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.putShort((short) notificationCode); - } - - @Override - public String toString() { - String description = CODE_DEFS.getOrDefault(notificationCode, "Code not recognized"); - return "{Notification Code=" + notificationCode + ", descr=" + description + "}"; - } - - private static Map<Integer, String> loadCodeDefs() { - Map<Integer, String> defs = new HashMap<>(); - defs.put(GENERAL_FAILURE_POST_CHALLENGE, - "General failure after authentication. (Implies failure, used after successful" - + " authentication.)"); - defs.put(GENERAL_FAILURE_PRE_CHALLENGE, - "General failure. (Implies failure, used before authentication.)"); - defs.put(SUCCESS, - "Success. User has been successfully authenticated. (Does not imply failure," - + " used after successful authentication)."); - defs.put(DENIED_ACCESS_POST_CHALLENGE, - "User has been temporarily denied access to the requested service. (Implies" - + " failure, used after successful authentication.)"); - defs.put(USER_NOT_SUBSCRIBED_POST_CHALLENGE, - "User has not subscribed to the requested service. (Implies failure, used" - + " after successful authentication.)"); - return defs; - } - } - - /** - * AtClientErrorCode represents the AT_CLIENT_ERROR_CODE attribute defined in RFC 4186#10.19 and - * RFC 4187#10.20 - */ - public static class AtClientErrorCode extends EapSimAkaAttribute { - private static final String TAG = AtClientErrorCode.class.getSimpleName(); - private static final int ATTR_LENGTH = 4; - - // Error codes defined in RFC 4186 Section 10.19 - public static final AtClientErrorCode UNABLE_TO_PROCESS = getClientErrorCode(0); - public static final AtClientErrorCode UNSUPPORTED_VERSION = getClientErrorCode(1); - public static final AtClientErrorCode INSUFFICIENT_CHALLENGES = getClientErrorCode(2); - public static final AtClientErrorCode STALE_RANDS = getClientErrorCode(3); - - public final int errorCode; - - public AtClientErrorCode(int lengthInBytes, int errorCode) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_CLIENT_ERROR_CODE, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); - } - - this.errorCode = errorCode; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.putShort((short) errorCode); - } - - private static AtClientErrorCode getClientErrorCode(int errorCode) { - try { - return new AtClientErrorCode(ATTR_LENGTH, errorCode); - } catch (EapSimAkaInvalidAttributeException exception) { - LOG.wtf(TAG, "Exception thrown while making AtClientErrorCodeConstants"); - return null; - } - } - } - - /** - * AtAutn represents the AT_AUTN attribute defined in RFC 4187#10.7 - */ - public static class AtAutn extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; - private static final int AUTN_LENGTH = 16; - private static final int RESERVED_BYTES = 2; - - public final byte[] autn = new byte[AUTN_LENGTH]; - - public AtAutn(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_AUTN, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Length must be 20B"); - } - - // next two bytes are reserved (RFC 4187#10.7) - byteBuffer.get(new byte[RESERVED_BYTES]); - - byteBuffer.get(autn); - } - - @VisibleForTesting - public AtAutn(byte[] autn) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_AUTN, ATTR_LENGTH); - - if (autn.length != AUTN_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Autn must be 16B"); - } - - System.arraycopy(autn, 0, this.autn, 0, AUTN_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.put(new byte[RESERVED_BYTES]); - byteBuffer.put(autn); - } - } - - /** - * AtRes respresents the AT_RES attribute defined in RFC 4187#10.8 - */ - public static class AtRes extends EapSimAkaAttribute { - private static final int BITS_PER_BYTE = 8; - private static final int MIN_RES_LEN_BYTES = 4; - private static final int MAX_RES_LEN_BYTES = 16; - - public final byte[] res; - - public AtRes(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_RES, lengthInBytes); - - // RES length is in bits (RFC 4187#10.8). - // RES length should be a multiple of 8 bits (TS 133 105#5.1.7.8) - int resLength = Short.toUnsignedInt(byteBuffer.getShort()); - if (resLength % BITS_PER_BYTE != 0) { - throw new EapSimAkaInvalidAttributeException("RES length must be multiple of 8"); - } - int resLengthBytes = resLength / BITS_PER_BYTE; - if (resLengthBytes < MIN_RES_LEN_BYTES || resLengthBytes > MAX_RES_LEN_BYTES) { - throw new EapSimAkaInvalidAttributeException( - "RES length must be: 4B <= len <= 16B"); - } - - res = new byte[resLengthBytes]; - byteBuffer.get(res); - - int bytesUsed = MIN_ATTR_LENGTH + resLengthBytes; - consumePadding(bytesUsed, byteBuffer); - } - - @VisibleForTesting - public AtRes(int lengthInBytes, byte[] res) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_RES, lengthInBytes); - - if (res.length < MIN_RES_LEN_BYTES || res.length > MAX_RES_LEN_BYTES) { - throw new EapSimAkaInvalidAttributeException( - "RES length must be: 4B <= len <= 16B"); - } - - this.res = res; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - - int resLenBits = res.length * BITS_PER_BYTE; - byteBuffer.putShort((short) resLenBits); - byteBuffer.put(res); - - int bytesUsed = MIN_ATTR_LENGTH + res.length; - addPadding(bytesUsed, byteBuffer); - } - - /** - * Creates and returns an AtRes instance with the given res value. - * - * @param res byte-array RES value to be used for this - * @return AtRes instance for the given RES value - * @throws EapSimAkaInvalidAttributeException if the given res value has an invalid length - */ - public static AtRes getAtRes(byte[] res) throws EapSimAkaInvalidAttributeException { - // Attributes must be 4B-aligned, so there can be 0 to 3 padding bytes added - int resLenBytes = MIN_ATTR_LENGTH + res.length; - if (resLenBytes % LENGTH_SCALING != 0) { - resLenBytes += LENGTH_SCALING - (resLenBytes % LENGTH_SCALING); - } - - return new AtRes(resLenBytes, res); - } - - /** - * Checks whether the given RES length is valid. - * - * @param resLenBytes the RES length to be checked - * @return true iff the given resLen is valid - */ - public static boolean isValidResLen(int resLenBytes) { - return resLenBytes >= MIN_RES_LEN_BYTES && resLenBytes <= MAX_RES_LEN_BYTES; - } - } - - /** - * AtAuts represents the AT_AUTS attribute defined in RFC 4187#10.9 - */ - public static class AtAuts extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = 4 * LENGTH_SCALING; - public static final int AUTS_LENGTH = 14; - - public final byte[] auts = new byte[AUTS_LENGTH]; - - public AtAuts(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_AUTS, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Length must be 16B"); - } - - byteBuffer.get(auts); - } - - public AtAuts(byte[] auts) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_AUTS, ATTR_LENGTH); - - if (auts.length != AUTS_LENGTH) { - throw new EapSimAkaInvalidAttributeException("Auts must be 14B"); - } - - System.arraycopy(auts, 0, this.auts, 0, AUTS_LENGTH); - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - - byteBuffer.put(auts); - } - } - - /** - * AtKdfInput represents the AT_KDF_INPUT attribute defined in RFC 5448#3.1 - */ - public static class AtKdfInput extends EapSimAkaAttribute { - public final byte[] networkName; - - public AtKdfInput(int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_KDF_INPUT, lengthInBytes); - - int networkNameLength = Short.toUnsignedInt(byteBuffer.getShort()); - networkName = new byte[networkNameLength]; - byteBuffer.get(networkName); - - int bytesUsed = MIN_ATTR_LENGTH + networkNameLength; - consumePadding(bytesUsed, byteBuffer); - } - - @VisibleForTesting - public AtKdfInput(int lengthInbytes, byte[] networkName) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_KDF_INPUT, lengthInbytes); - - this.networkName = networkName; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - byteBuffer.putShort((short) networkName.length); - byteBuffer.put(networkName); - - int bytesUsed = MIN_ATTR_LENGTH + networkName.length; - addPadding(bytesUsed, byteBuffer); - } - } - - /** - * AdKdf represents the AT_KDF attribute defined in RFC 5448#3.2 - */ - public static class AtKdf extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = MIN_ATTR_LENGTH; - - public final int kdf; - - public AtKdf(int lengthInBytes, ByteBuffer buffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_KDF, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("AtKdf length must be 4B"); - } - - kdf = Short.toUnsignedInt(buffer.getShort()); - } - - @VisibleForTesting - public AtKdf(int kdf) throws EapSimAkaInvalidAttributeException { - super(EAP_AT_KDF, ATTR_LENGTH); - - this.kdf = kdf; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - - byteBuffer.putShort((short) kdf); - } - } - - /** - * AtBidding represents the AT_BIDDING attribute defined in RFC 5448#4 - */ - public static class AtBidding extends EapSimAkaAttribute { - private static final int ATTR_LENGTH = MIN_ATTR_LENGTH; - private static final int SUPPORTS_EAP_AKA_PRIME_MASK = 0x8000; - - public final boolean doesServerSupportEapAkaPrime; - - public AtBidding(int lengthInBytes, ByteBuffer buffer) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_BIDDING, lengthInBytes); - - if (lengthInBytes != ATTR_LENGTH) { - throw new EapSimAkaInvalidAttributeException("AtBidding length must be 4B"); - } - - int serverFlag = Short.toUnsignedInt(buffer.getShort()); - doesServerSupportEapAkaPrime = (serverFlag & SUPPORTS_EAP_AKA_PRIME_MASK) != 0; - } - - @VisibleForTesting - public AtBidding(boolean doesServerSupportEapAkaPrime) - throws EapSimAkaInvalidAttributeException { - super(EAP_AT_BIDDING, ATTR_LENGTH); - - this.doesServerSupportEapAkaPrime = doesServerSupportEapAkaPrime; - } - - @Override - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - - int flagToWrite = doesServerSupportEapAkaPrime ? SUPPORTS_EAP_AKA_PRIME_MASK : 0; - byteBuffer.putShort((short) flagToWrite); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttributeFactory.java b/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttributeFactory.java deleted file mode 100644 index df79a25e..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttributeFactory.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_CLIENT_ERROR_CODE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_COUNTER; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_COUNTER_TOO_SMALL; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_FULLAUTH_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IDENTITY; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NONCE_S; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PADDING; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.LENGTH_SCALING; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.SKIPPABLE_ATTRIBUTE_RANGE_START; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaUnsupportedAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtCounter; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtCounterTooSmall; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtFullauthIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceS; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPadding; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPermanentIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EapSimAkaUnsupportedAttribute; - -import java.nio.ByteBuffer; - -/** - * EapSimAkaAttributeFactory is used for creating EapSimAkaAttributes according to their type. - * - * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication - * Protocol for Subscriber Identity Modules (EAP-SIM)</a> - * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication - * Protocol for Authentication and Key Agreement (EAP-AKA)</a> - * @see <a href="https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml">EAP SIM/AKA - * Attributes</a> - */ -public abstract class EapSimAkaAttributeFactory { - /** - * Decodes a single EapSimAkaAttribute object from the given ByteBuffer. - * - * <p>Decoding logic is based on Attribute definitions in RFC 4186#10 and RFC 4187#10. - * - * @param attributeType the attribute type to be decoded - * @param lengthInBytes the length in bytes of the attribute to be decoded - * @param byteBuffer The ByteBuffer to parse the current attribute from - * @return The current EapSimAkaAttribute to be parsed, or EapSimAkaUnsupportedAttribute if the - * given attributeType is skippable and unsupported - * @throws EapSimAkaInvalidAttributeException when a malformatted attribute is attempted to be - * decoded - * @throws EapSimAkaUnsupportedAttributeException when an unsupported, unskippable Attribute is - * attempted to be decoded - */ - EapSimAkaAttribute getAttribute(int attributeType, int lengthInBytes, ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException, EapSimAkaUnsupportedAttributeException { - switch (attributeType) { - // TODO(b/139482157): define optional shared attributes - case EAP_AT_PERMANENT_ID_REQ: - return new AtPermanentIdReq(lengthInBytes, byteBuffer); - case EAP_AT_ANY_ID_REQ: - return new AtAnyIdReq(lengthInBytes, byteBuffer); - case EAP_AT_FULLAUTH_ID_REQ: - return new AtFullauthIdReq(lengthInBytes, byteBuffer); - case EAP_AT_IDENTITY: - return new AtIdentity(lengthInBytes, byteBuffer); - case EAP_AT_PADDING: - return new AtPadding(lengthInBytes, byteBuffer); - case EAP_AT_MAC: - return new AtMac(lengthInBytes, byteBuffer); - case EAP_AT_COUNTER: - return new AtCounter(lengthInBytes, byteBuffer); - case EAP_AT_COUNTER_TOO_SMALL: - return new AtCounterTooSmall(lengthInBytes, byteBuffer); - case EAP_AT_NONCE_S: - return new AtNonceS(lengthInBytes, byteBuffer); - case EAP_AT_NOTIFICATION: - return new AtNotification(lengthInBytes, byteBuffer); - case EAP_AT_CLIENT_ERROR_CODE: - int errorCode = Short.toUnsignedInt(byteBuffer.getShort()); - return new AtClientErrorCode(lengthInBytes, errorCode); - default: - if (attributeType >= SKIPPABLE_ATTRIBUTE_RANGE_START) { - return new EapSimAkaUnsupportedAttribute( - attributeType, lengthInBytes, byteBuffer); - } - - throw new EapSimAkaUnsupportedAttributeException( - "Unexpected EAP Attribute=" + attributeType); - } - } - - /** - * This method exists only for testing. - * - * <p>It follows the attributeFactory.getAttribute(ByteBuffer) pattern used by - * EapSimAttributeFactory and EapAkaAttributeFactory. - */ - @VisibleForTesting - public EapSimAkaAttribute getAttribute(ByteBuffer byteBuffer) - throws EapSimAkaInvalidAttributeException, EapSimAkaUnsupportedAttributeException { - int attributeType = Byte.toUnsignedInt(byteBuffer.get()); - - // Length is given as a multiple of 4x bytes (RFC 4186 Section 8.1) - int lengthInBytes = Byte.toUnsignedInt(byteBuffer.get()) * LENGTH_SCALING; - return getAttribute(attributeType, lengthInBytes, byteBuffer); - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaTypeData.java b/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaTypeData.java deleted file mode 100644 index c84fdf4b..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaTypeData.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_ATTRIBUTE_STRING; - -import android.annotation.NonNull; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaUnsupportedAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimInvalidAtRandException; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EapSimAkaUnsupportedAttribute; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * EapSimTypeData represents the Type Data for an {@link EapMessage} during an EAP-SIM session. - */ -public abstract class EapSimAkaTypeData { - private static final int MIN_LEN_BYTES = 3; // subtype (1B) + reserved bytes (2B) - private static final int RESERVED_BYTES = 2; // RFC 4186#8.1, RFC 4187#8.1 - - public final int eapSubtype; - - // LinkedHashMap used to preserve encoded ordering of attributes. This is necessary for checking - // the MAC value for the message - public final LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap; - - public EapSimAkaTypeData( - int eapSubType, - LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - this.eapSubtype = eapSubType; - this.attributeMap = attributeMap; - } - - /** - * Creates and returns the byte-array encoding of this EapSimTypeData instance. - * - * @return byte[] representing the byte-encoding of this EapSimTypeData instance. - */ - public byte[] encode() { - int lengthInBytes = MIN_LEN_BYTES; - for (EapSimAkaAttribute attribute : attributeMap.values()) { - lengthInBytes += attribute.lengthInBytes; - } - - ByteBuffer output = ByteBuffer.allocate(lengthInBytes); - output.put((byte) eapSubtype); - - // two reserved bytes (RFC 4186#8.1, RFC 4187#8.1) - output.put(new byte[RESERVED_BYTES]); - - for (EapSimAkaAttribute attribute : attributeMap.values()) { - attribute.encode(output); - } - - return output.array(); - } - - /** - * Class for decoding EAP-SIM and EAP-AKA type-datas. - * - * @param <T> The EapSimAkaTypeData type that the EapSimAkaTypeDataDecoder will be decoding - */ - public abstract static class EapSimAkaTypeDataDecoder<T extends EapSimAkaTypeData> { - private final String mTAG; - private final String mEapMethod; - private final Set<Integer> mSupportedSubtypes; - private final EapSimAkaAttributeFactory mAttributeFactory; - private final Map<Integer, String> mEapSubtypeStrings; - - EapSimAkaTypeDataDecoder( - String tag, - String eapMethod, - Set<Integer> supportedSubtypes, - EapSimAkaAttributeFactory eapSimAkaAttributeFactory, - Map<Integer, String> eapSubtypeStrings) { - this.mTAG = tag; - this.mEapMethod = eapMethod; - this.mSupportedSubtypes = supportedSubtypes; - this.mAttributeFactory = eapSimAkaAttributeFactory; - this.mEapSubtypeStrings = eapSubtypeStrings; - } - - protected DecodeResult<T> decode(@NonNull byte[] typeData) { - if (typeData == null) { - LOG.d(mTAG, "Invalid EAP Type-Data"); - return new DecodeResult<>(AtClientErrorCode.UNABLE_TO_PROCESS); - } - - ByteBuffer byteBuffer = ByteBuffer.wrap(typeData); - try { - int eapSubType = Byte.toUnsignedInt(byteBuffer.get()); - if (!mSupportedSubtypes.contains(eapSubType)) { - LOG.d(mTAG, "Invalid EAP Type-Data"); - return new DecodeResult<>(AtClientErrorCode.UNABLE_TO_PROCESS); - } - - // next two bytes are reserved (RFC 4186#8.1, RFC 4187#8.1) - byteBuffer.get(new byte[RESERVED_BYTES]); - - // read attributes - LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap = new LinkedHashMap<>(); - while (byteBuffer.hasRemaining()) { - EapSimAkaAttribute attribute = mAttributeFactory.getAttribute(byteBuffer); - - if (attributeMap.containsKey(attribute.attributeType)) { - // Duplicate attributes are not allowed (RFC 4186#6.3.1, RFC 4187#6.3.1) - LOG.e(mTAG, "Duplicate attribute in parsed EAP-Message"); - return new DecodeResult<>(AtClientErrorCode.UNABLE_TO_PROCESS); - } - - if (attribute instanceof EapSimAkaUnsupportedAttribute) { - LOG.d(mTAG, "Unsupported EAP-SIM attribute during decoding: " - + attribute.attributeType); - } - attributeMap.put(attribute.attributeType, attribute); - } - - T eapSimAkaTypeData = getInstance(eapSubType, attributeMap); - - logDecodedEapSimTypeData(eapSimAkaTypeData); - - return new DecodeResult<>(eapSimAkaTypeData); - } catch (EapSimInvalidAtRandException ex) { - LOG.e(mTAG, "Invalid AtRand attribute", ex); - return new DecodeResult<>(AtClientErrorCode.INSUFFICIENT_CHALLENGES); - } catch (EapSimAkaInvalidAttributeException | BufferUnderflowException ex) { - LOG.e(mTAG, "Incorrectly formatted attribute", ex); - return new DecodeResult<>(AtClientErrorCode.UNABLE_TO_PROCESS); - } catch (EapSimAkaUnsupportedAttributeException ex) { - LOG.e(mTAG, "Unrecognized, non-skippable attribute encountered", ex); - return new DecodeResult<>(AtClientErrorCode.UNABLE_TO_PROCESS); - } - } - - protected abstract T getInstance( - int eapSubType, - LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap); - - private void logDecodedEapSimTypeData(EapSimAkaTypeData eapSimAkaTypeData) { - StringBuilder msg = new StringBuilder(); - msg.append("Decoded "); - msg.append(mEapMethod); - msg.append(" type data: "); - - msg.append(mEapSubtypeStrings.getOrDefault(eapSimAkaTypeData.eapSubtype, "Unknown")); - msg.append(" attributes=[ "); - for (int attributeType : eapSimAkaTypeData.attributeMap.keySet()) { - msg.append( - EAP_ATTRIBUTE_STRING.getOrDefault(attributeType, - "Unknown(" + attributeType + ")")); - msg.append(" "); - } - msg.append("]"); - LOG.i(mTAG, msg.toString()); - } - } - - /** - * DecodeResult represents the result from calling EapSimTypeDataDecoder.decode(). It will - * contain either a decoded EapSimTypeData or the relevant AtClientErrorCode. - * - * @param <T> The EapSimAkaTypeData type that is wrapped in this DecodeResult - */ - public static class DecodeResult<T extends EapSimAkaTypeData> { - public final T eapTypeData; - public final EapSimAkaAttribute.AtClientErrorCode atClientErrorCode; - - public DecodeResult(T eapTypeData) { - this.eapTypeData = eapTypeData; - this.atClientErrorCode = null; - } - - public DecodeResult(EapSimAkaAttribute.AtClientErrorCode atClientErrorCode) { - this.atClientErrorCode = atClientErrorCode; - eapTypeData = null; - } - - public boolean isSuccessfulDecode() { - return eapTypeData != null; - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapSimAttributeFactory.java b/src/java/com/android/internal/net/eap/message/simaka/EapSimAttributeFactory.java deleted file mode 100644 index 8be348eb..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapSimAttributeFactory.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NONCE_MT; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_SELECTED_VERSION; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_VERSION_LIST; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.LENGTH_SCALING; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaUnsupportedAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceMt; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandSim; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtSelectedVersion; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; - -import java.nio.ByteBuffer; - -/** - * EapSimAttributeFactory is used for creating EAP-SIM attributes according to their type. - * - * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication - * Protocol for Subscriber Identity Modules (EAP-SIM)</a> - * @see <a href="https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml">EAP SIM/AKA - * Attributes</a> - */ -public class EapSimAttributeFactory extends EapSimAkaAttributeFactory { - private static EapSimAttributeFactory sInstance = new EapSimAttributeFactory(); - - private EapSimAttributeFactory() { - } - - public static EapSimAttributeFactory getInstance() { - return sInstance; - } - - /** - * Decodes a single EapSimAkaAttribute object from the given ByteBuffer. - * - * <p>Decoding logic is based on Attribute definitions in RFC 4186#10. - * - * @param byteBuffer The ByteBuffer to parse the current attribute from - * @return The current EapSimAkaAttribute to be parsed, or EapSimAkaUnsupportedAttribute if the - * given attributeType is skippable and unsupported - * @throws EapSimAkaInvalidAttributeException when a malformatted attribute is attempted to be - * decoded - * @throws EapSimAkaUnsupportedAttributeException when an unsupported, unskippable Attribute is - * attempted to be decoded - */ - public EapSimAkaAttribute getAttribute(ByteBuffer byteBuffer) throws - EapSimAkaInvalidAttributeException, EapSimAkaUnsupportedAttributeException { - int attributeType = Byte.toUnsignedInt(byteBuffer.get()); - - // Length is given as a multiple of 4x bytes (RFC 4186 Section 8.1) - int lengthInBytes = Byte.toUnsignedInt(byteBuffer.get()) * LENGTH_SCALING; - - switch (attributeType) { - // TODO(b/134670528): add case statements for all EAP-SIM attributes - case EAP_AT_VERSION_LIST: - return new AtVersionList(lengthInBytes, byteBuffer); - case EAP_AT_SELECTED_VERSION: - int selectedVersion = Short.toUnsignedInt(byteBuffer.getShort()); - return new AtSelectedVersion(lengthInBytes, selectedVersion); - case EAP_AT_NONCE_MT: - return new AtNonceMt(lengthInBytes, byteBuffer); - case EAP_AT_RAND: - return new AtRandSim(lengthInBytes, byteBuffer); - default: - return super.getAttribute(attributeType, lengthInBytes, byteBuffer); - } - } -} diff --git a/src/java/com/android/internal/net/eap/message/simaka/EapSimTypeData.java b/src/java/com/android/internal/net/eap/message/simaka/EapSimTypeData.java deleted file mode 100644 index ff1be603..00000000 --- a/src/java/com/android/internal/net/eap/message/simaka/EapSimTypeData.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.message.EapMessage; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * EapSimTypeData represents the Type Data for an {@link EapMessage} during an EAP-SIM session. - */ -public class EapSimTypeData extends EapSimAkaTypeData { - private static final String TAG = EapSimTypeData.class.getSimpleName(); - - // EAP-SIM Subtype values defined by IANA - // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml - public static final int EAP_SIM_START = 10; - public static final int EAP_SIM_CHALLENGE = 11; - public static final int EAP_SIM_NOTIFICATION = 12; - public static final int EAP_SIM_REAUTHENTICATION = 13; - public static final int EAP_SIM_CLIENT_ERROR = 14; - - public static final Map<Integer, String> EAP_SIM_SUBTYPE_STRING = new HashMap<>(); - static { - EAP_SIM_SUBTYPE_STRING.put(EAP_SIM_START, "Start"); - EAP_SIM_SUBTYPE_STRING.put(EAP_SIM_CHALLENGE, "Challenge"); - EAP_SIM_SUBTYPE_STRING.put(EAP_SIM_NOTIFICATION, "Notification"); - EAP_SIM_SUBTYPE_STRING.put(EAP_SIM_REAUTHENTICATION, "Re-authentication"); - EAP_SIM_SUBTYPE_STRING.put(EAP_SIM_CLIENT_ERROR, "Client-Error"); - } - - private static final Set<Integer> SUPPORTED_SUBTYPES = new HashSet<>(); - static { - SUPPORTED_SUBTYPES.add(EAP_SIM_START); - SUPPORTED_SUBTYPES.add(EAP_SIM_CHALLENGE); - SUPPORTED_SUBTYPES.add(EAP_SIM_NOTIFICATION); - SUPPORTED_SUBTYPES.add(EAP_SIM_REAUTHENTICATION); - SUPPORTED_SUBTYPES.add(EAP_SIM_CLIENT_ERROR); - } - - private static final EapSimTypeDataDecoder sTypeDataDecoder = new EapSimTypeDataDecoder(); - - @VisibleForTesting - public EapSimTypeData(int eapSubType, LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - super(eapSubType, attributeMap); - } - - public EapSimTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { - super(eapSubtype, new LinkedHashMap<>()); - - if (!SUPPORTED_SUBTYPES.contains(eapSubtype)) { - throw new IllegalArgumentException("Invalid subtype for EAP-SIM: " + eapSubtype); - } - - for (EapSimAkaAttribute attribute : attributes) { - if (attributeMap.containsKey(attribute.attributeType)) { - throw new IllegalArgumentException( - "Duplicate attribute in attributes: " + attribute.attributeType); - } - attributeMap.put(attribute.attributeType, attribute); - } - } - - public static EapSimTypeDataDecoder getEapSimTypeDataDecoder() { - return sTypeDataDecoder; - } - - /** - * EapSimTypeDataDecoder will be used for decoding {@link EapSimTypeData} objects. - */ - public static class EapSimTypeDataDecoder extends EapSimAkaTypeDataDecoder<EapSimTypeData> { - private static final String TAG = EapSimTypeDataDecoder.class.getSimpleName(); - private static final String EAP_METHOD = "EAP-SIM"; - - protected EapSimTypeDataDecoder() { - super( - TAG, - EAP_METHOD, - SUPPORTED_SUBTYPES, - EapSimAttributeFactory.getInstance(), - EAP_SIM_SUBTYPE_STRING); - } - - /** - * Decodes the given byte-array into a DecodeResult object. - * - * @param typeData the byte-encoding of the EapSimTypeData to be parsed - * @return a DecodeResult object. If the decoding is successful, this will encapsulate an - * EapSimTypeData instance representing the data stored in typeData. Otherwise, it - * will contain the relevant AtClientErrorCode for the decoding error. - */ - public DecodeResult<EapSimTypeData> decode(@NonNull byte[] typeData) { - return super.decode(typeData); - } - - @Override - protected EapSimTypeData getInstance( - int eapSubtype, - LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap) { - return new EapSimTypeData(eapSubtype, attributeMap); - } - } -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapAkaMethodStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapAkaMethodStateMachine.java deleted file mode 100644 index b26c8e95..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapAkaMethodStateMachine.java +++ /dev/null @@ -1,612 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_AUTHENTICATION_REJECT; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CLIENT_ERROR; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_SYNCHRONIZATION_FAILURE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_BIDDING; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ENCR_DATA; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_FULLAUTH_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IV; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; - -import android.annotation.Nullable; -import android.content.Context; -import android.net.eap.EapSessionConfig.EapAkaConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.crypto.Fips186_2Prf; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.EapSilentException; -import com.android.internal.net.eap.exceptions.simaka.EapAkaInvalidAuthenticationResponse; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaIdentityUnavailableException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidLengthException; -import com.android.internal.net.eap.message.EapData.EapMethod; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData.EapAkaTypeDataDecoder; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAuts; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtBidding; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRes; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -/** - * EapAkaMethodStateMachine represents the valid paths possible for the EAP-AKA protocol. - * - * <p>EAP-AKA sessions will always follow the path: - * - * Created --+--> Identity --+--> Challenge --> Final - * | | - * +---------------+ - * - * Note: If the EAP-Request/AKA-Challenge message contains an AUTN with an invalid sequence number, - * the peer will indicate a synchronization failure to the server and a new challenge will be - * attempted. - * - * Note: EAP-Request/Notification messages can be received at any point in the above state machine - * At most one EAP-AKA/Notification message is allowed per EAP-AKA session. - * - * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication - * Protocol for Authentication and Key Agreement (EAP-AKA)</a> - */ -class EapAkaMethodStateMachine extends EapSimAkaMethodStateMachine { - private static final String TAG = EapAkaMethodStateMachine.class.getSimpleName(); - - // EAP-AKA identity prefix (RFC 4187#4.1.1.6) - private static final String AKA_IDENTITY_PREFIX = "0"; - - private final EapAkaTypeDataDecoder mEapAkaTypeDataDecoder; - private final boolean mSupportsEapAkaPrime; - - protected EapAkaMethodStateMachine( - Context context, byte[] eapIdentity, EapAkaConfig eapAkaConfig) { - this(context, eapIdentity, eapAkaConfig, false); - } - - EapAkaMethodStateMachine( - Context context, - byte[] eapIdentity, - EapAkaConfig eapAkaConfig, - boolean supportsEapAkaPrime) { - this( - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), - eapIdentity, - eapAkaConfig, - EapAkaTypeData.getEapAkaTypeDataDecoder(), - supportsEapAkaPrime); - } - - @VisibleForTesting - protected EapAkaMethodStateMachine( - TelephonyManager telephonyManager, - byte[] eapIdentity, - EapAkaConfig eapAkaConfig, - EapAkaTypeDataDecoder eapAkaTypeDataDecoder, - boolean supportsEapAkaPrime) { - super( - telephonyManager.createForSubscriptionId(eapAkaConfig.subId), - eapIdentity, - eapAkaConfig); - mEapAkaTypeDataDecoder = eapAkaTypeDataDecoder; - mSupportsEapAkaPrime = supportsEapAkaPrime; - - transitionTo(new CreatedState()); - } - - @Override - @EapMethod - int getEapMethod() { - return EAP_TYPE_AKA; - } - - protected DecodeResult<EapAkaTypeData> decode(byte[] typeData) { - return mEapAkaTypeDataDecoder.decode(typeData); - } - - /** - * This exists so we can override the identity prefix in the EapAkaPrimeMethodStateMachine. - * - * @return the Identity prefix for this EAP method - */ - protected String getIdentityPrefix() { - return AKA_IDENTITY_PREFIX; - } - - protected ChallengeState buildChallengeState() { - return new ChallengeState(); - } - - protected ChallengeState buildChallengeState(byte[] identity) { - return new ChallengeState(identity); - } - - protected class CreatedState extends EapMethodState { - private final String mTAG = CreatedState.class.getSimpleName(); - - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - DecodeResult<? extends EapAkaTypeData> decodeResult = - decode(message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return buildClientErrorResponse( - message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode); - } - - EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData; - switch (eapAkaTypeData.eapSubtype) { - case EAP_AKA_IDENTITY: - return transitionAndProcess(new IdentityState(), message); - case EAP_AKA_CHALLENGE: - return transitionAndProcess(buildChallengeState(), message); - case EAP_AKA_NOTIFICATION: - return handleEapSimAkaNotification( - mTAG, - true, // isPreChallengeState - message.eapIdentifier, - eapAkaTypeData); - default: - return buildClientErrorResponse( - message.eapIdentifier, - getEapMethod(), - AtClientErrorCode.UNABLE_TO_PROCESS); - } - } - } - - protected class IdentityState extends EapMethodState { - private final String mTAG = IdentityState.class.getSimpleName(); - - private byte[] mIdentity; - - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - DecodeResult<? extends EapAkaTypeData> decodeResult = - decode(message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return buildClientErrorResponse( - message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode); - } - - EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData; - switch (eapAkaTypeData.eapSubtype) { - case EAP_AKA_IDENTITY: - break; - case EAP_AKA_CHALLENGE: - return transitionAndProcess(buildChallengeState(mIdentity), message); - case EAP_AKA_NOTIFICATION: - return handleEapSimAkaNotification( - mTAG, - true, // isPreChallengeState - message.eapIdentifier, - eapAkaTypeData); - default: - return buildClientErrorResponse( - message.eapIdentifier, - getEapMethod(), - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - if (!isValidIdentityAttributes(eapAkaTypeData)) { - LOG.e(mTAG, "Invalid attributes: " + eapAkaTypeData.attributeMap.keySet()); - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_AKA, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - String imsi = mTelephonyManager.getSubscriberId(); - if (imsi == null) { - LOG.e(mTAG, "Unable to get IMSI for subId=" + mEapUiccConfig.subId); - return new EapError( - new EapSimAkaIdentityUnavailableException( - "IMSI for subId (" + mEapUiccConfig.subId + ") not available")); - } - String identityString = getIdentityPrefix() + imsi; - mIdentity = identityString.getBytes(StandardCharsets.US_ASCII); - LOG.d(mTAG, "EAP-AKA/Identity=" + LOG.pii(identityString)); - - AtIdentity atIdentity; - try { - atIdentity = AtIdentity.getAtIdentity(mIdentity); - } catch (EapSimAkaInvalidAttributeException ex) { - LOG.wtf(mTAG, "Exception thrown while making AtIdentity attribute", ex); - return new EapError(ex); - } - - return buildResponseMessage( - getEapMethod(), - EAP_AKA_IDENTITY, - message.eapIdentifier, - Arrays.asList(atIdentity)); - } - - private boolean isValidIdentityAttributes(EapAkaTypeData eapAkaTypeData) { - Set<Integer> attrs = eapAkaTypeData.attributeMap.keySet(); - - // exactly one ID request type required - int idRequests = 0; - idRequests += attrs.contains(EAP_AT_PERMANENT_ID_REQ) ? 1 : 0; - idRequests += attrs.contains(EAP_AT_ANY_ID_REQ) ? 1 : 0; - idRequests += attrs.contains(EAP_AT_FULLAUTH_ID_REQ) ? 1 : 0; - - if (idRequests != 1) { - return false; - } - - // can't contain mac, iv, encr data - if (attrs.contains(EAP_AT_MAC) - || attrs.contains(EAP_AT_IV) - || attrs.contains(EAP_AT_ENCR_DATA)) { - return false; - } - return true; - } - } - - protected class ChallengeState extends EapMethodState { - private final String mTAG = ChallengeState.class.getSimpleName(); - - @VisibleForTesting boolean mHadSuccessfulChallenge = false; - @VisibleForTesting protected final byte[] mIdentity; - - // IK and CK lengths defined as 16B (RFC 4187#1) - private final int mIkLenBytes = 16; - private final int mCkLenBytes = 16; - - // Tags for Successful and Synchronization responses - private final byte mSuccess = (byte) 0xDB; - private final byte mSynchronization = (byte) 0xDC; - - ChallengeState() { - // use the EAP-Identity for the default value (RFC 4187#7) - this(mEapIdentity); - } - - ChallengeState(byte[] identity) { - this.mIdentity = identity; - } - - public EapResult process(EapMessage message) { - if (message.eapCode == EAP_CODE_SUCCESS) { - if (!mHadSuccessfulChallenge) { - LOG.e(mTAG, "Received unexpected EAP-Success"); - return new EapError( - new EapInvalidRequestException( - "Received an EAP-Success in the ChallengeState")); - } - transitionTo(new FinalState()); - return new EapSuccess(mMsk, mEmsk); - } else if (message.eapCode == EAP_CODE_FAILURE) { - transitionTo(new FinalState()); - return new EapFailure(); - } else if (message.eapData.eapType == EAP_NOTIFICATION) { - return handleEapNotification(mTAG, message); - } - - if (message.eapData.eapType != getEapMethod()) { - return new EapError(new EapInvalidRequestException( - "Expected EAP Type " + getEapMethod() - + ", received " + message.eapData.eapType)); - } - - DecodeResult<? extends EapAkaTypeData> decodeResult = - decode(message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return buildClientErrorResponse( - message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode); - } - - EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData; - switch (eapAkaTypeData.eapSubtype) { - case EAP_AKA_CHALLENGE: - break; - case EAP_AKA_NOTIFICATION: - return handleEapSimAkaNotification( - mTAG, - false, // isPreChallengeState - message.eapIdentifier, - eapAkaTypeData); - default: - return buildClientErrorResponse( - message.eapIdentifier, - getEapMethod(), - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - if (!isValidChallengeAttributes(eapAkaTypeData)) { - LOG.e(mTAG, "Invalid attributes: " + eapAkaTypeData.attributeMap.keySet()); - return buildClientErrorResponse( - message.eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } - - return handleChallengeAuthentication(message, eapAkaTypeData); - } - - protected EapResult handleChallengeAuthentication( - EapMessage message, EapAkaTypeData eapAkaTypeData) { - RandChallengeResult result; - try { - result = getRandChallengeResult(eapAkaTypeData); - } catch (EapAkaInvalidAuthenticationResponse ex) { - return new EapError(ex); - } catch (EapSimAkaInvalidLengthException | BufferUnderflowException ex) { - LOG.e(mTAG, "Invalid response returned from SIM", ex); - return buildClientErrorResponse( - message.eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } catch (EapSimAkaAuthenticationFailureException ex) { - // Return EAP-Response/AKA-Authentication-Reject when the AUTN is rejected - // (RFC 4187#6.3.1) - return buildAuthenticationRejectMessage(message.eapIdentifier); - } - - if (!result.isSuccessfulResult()) { - try { - return buildResponseMessage( - getEapMethod(), - EAP_AKA_SYNCHRONIZATION_FAILURE, - message.eapIdentifier, - Arrays.asList(new AtAuts(result.auts))); - } catch (EapSimAkaInvalidAttributeException ex) { - LOG.wtf(mTAG, "Error creating an AtAuts attr", ex); - return new EapError(ex); - } - } - - EapResult eapResult = - generateAndPersistEapAkaKeys(result, message.eapIdentifier, eapAkaTypeData); - if (eapResult != null) { - return eapResult; - } - - try { - if (!isValidMac(mTAG, message, eapAkaTypeData, new byte[0])) { - return buildClientErrorResponse( - message.eapIdentifier, - getEapMethod(), - AtClientErrorCode.UNABLE_TO_PROCESS); - } - } catch (GeneralSecurityException - | EapSilentException - | EapSimAkaInvalidAttributeException ex) { - // if the MAC can't be generated, we can't continue - LOG.e(mTAG, "Error computing MAC for EapMessage", ex); - return new EapError(ex); - } - - // before sending a response, check for bidding-down attacks (RFC 5448#4) - if (mSupportsEapAkaPrime) { - AtBidding atBidding = (AtBidding) eapAkaTypeData.attributeMap.get(EAP_AT_BIDDING); - if (atBidding != null && atBidding.doesServerSupportEapAkaPrime) { - LOG.w( - mTAG, - "Potential bidding down attack. AT_BIDDING attr included and EAP-AKA'" - + " is supported"); - return buildAuthenticationRejectMessage(message.eapIdentifier); - } - } - - // server has been authenticated, so we can send a response - try { - mHadSuccessfulChallenge = true; - return buildResponseMessageWithMac( - message.eapIdentifier, - EAP_AKA_CHALLENGE, - new byte[0], - Arrays.asList(AtRes.getAtRes(result.res))); - } catch (EapSimAkaInvalidAttributeException ex) { - LOG.wtf(mTAG, "Error creating AtRes value", ex); - return new EapError(ex); - } - } - - @VisibleForTesting - class RandChallengeResult { - public final byte[] res; - public final byte[] ik; - public final byte[] ck; - public final byte[] auts; - - RandChallengeResult(byte[] res, byte[] ik, byte[] ck) - throws EapSimAkaInvalidLengthException { - if (!AtRes.isValidResLen(res.length)) { - throw new EapSimAkaInvalidLengthException("Invalid RES length"); - } else if (ik.length != mIkLenBytes) { - throw new EapSimAkaInvalidLengthException("Invalid IK length"); - } else if (ck.length != mCkLenBytes) { - throw new EapSimAkaInvalidLengthException("Invalid CK length"); - } - - this.res = res; - this.ik = ik; - this.ck = ck; - this.auts = null; - } - - RandChallengeResult(byte[] auts) throws EapSimAkaInvalidLengthException { - if (auts.length != AtAuts.AUTS_LENGTH) { - throw new EapSimAkaInvalidLengthException("Invalid AUTS length"); - } - - this.res = null; - this.ik = null; - this.ck = null; - this.auts = auts; - } - - private boolean isSuccessfulResult() { - return res != null && ik != null && ck != null; - } - } - - private boolean isValidChallengeAttributes(EapAkaTypeData eapAkaTypeData) { - Set<Integer> attrs = eapAkaTypeData.attributeMap.keySet(); - - // must contain: AT_RAND, AT_AUTN, AT_MAC - return attrs.contains(EAP_AT_RAND) - && attrs.contains(EAP_AT_AUTN) - && attrs.contains(EAP_AT_MAC); - } - - private RandChallengeResult getRandChallengeResult(EapAkaTypeData eapAkaTypeData) - throws EapSimAkaAuthenticationFailureException, EapSimAkaInvalidLengthException { - AtRandAka atRandAka = (AtRandAka) eapAkaTypeData.attributeMap.get(EAP_AT_RAND); - AtAutn atAutn = (AtAutn) eapAkaTypeData.attributeMap.get(EAP_AT_AUTN); - - // pre-Base64 formatting needs to be: [Length][RAND][Length][AUTN] - int randLen = atRandAka.rand.length; - int autnLen = atAutn.autn.length; - ByteBuffer formattedChallenge = ByteBuffer.allocate(1 + randLen + 1 + autnLen); - formattedChallenge.put((byte) randLen); - formattedChallenge.put(atRandAka.rand); - formattedChallenge.put((byte) autnLen); - formattedChallenge.put(atAutn.autn); - - byte[] challengeResponse = - processUiccAuthentication( - mTAG, - TelephonyManager.AUTHTYPE_EAP_AKA, - formattedChallenge.array()); - ByteBuffer buffer = ByteBuffer.wrap(challengeResponse); - byte tag = buffer.get(); - - switch (tag) { - case mSuccess: - // response format: [tag][RES length][RES][IK length][IK][CK length][CK] - break; - case mSynchronization: - // response format: [tag][AUTS length][AUTS] - byte[] auts = new byte[Byte.toUnsignedInt(buffer.get())]; - buffer.get(auts); - - LOG.i(mTAG, "Synchronization Failure"); - LOG.d( - mTAG, - "RAND=" + LOG.pii(atRandAka.rand) - + " AUTN=" + LOG.pii(atAutn.autn) - + " AUTS=" + LOG.pii(auts)); - - return new RandChallengeResult(auts); - default: - throw new EapAkaInvalidAuthenticationResponse( - "Invalid tag for UICC response: " + String.format("%02X", tag)); - } - - byte[] res = new byte[Byte.toUnsignedInt(buffer.get())]; - buffer.get(res); - - byte[] ik = new byte[Byte.toUnsignedInt(buffer.get())]; - buffer.get(ik); - - byte[] ck = new byte[Byte.toUnsignedInt(buffer.get())]; - buffer.get(ck); - - LOG.d( - mTAG, - "RAND=" + LOG.pii(atRandAka.rand) - + " AUTN=" + LOG.pii(atAutn.autn) - + " RES=" + LOG.pii(res) - + " IK=" + LOG.pii(ik) - + " CK=" + LOG.pii(ck)); - - return new RandChallengeResult(res, ik, ck); - } - - protected EapResult buildAuthenticationRejectMessage(int eapIdentifier) { - return buildResponseMessage( - getEapMethod(), - EAP_AKA_AUTHENTICATION_REJECT, - eapIdentifier, - new ArrayList<>()); - } - - @Nullable - protected EapResult generateAndPersistEapAkaKeys( - RandChallengeResult result, int eapIdentifier, EapAkaTypeData eapAkaTypeData) { - try { - MessageDigest sha1 = MessageDigest.getInstance(MASTER_KEY_GENERATION_ALG); - byte[] mkInputData = getMkInputData(result); - generateAndPersistKeys(mTAG, sha1, new Fips186_2Prf(), mkInputData); - return null; - } catch (NoSuchAlgorithmException | BufferUnderflowException ex) { - LOG.e(mTAG, "Error while creating keys", ex); - return buildClientErrorResponse( - eapIdentifier, EAP_TYPE_AKA, AtClientErrorCode.UNABLE_TO_PROCESS); - } - } - - private byte[] getMkInputData(RandChallengeResult result) { - int numInputBytes = mIdentity.length + result.ik.length + result.ck.length; - ByteBuffer buffer = ByteBuffer.allocate(numInputBytes); - buffer.put(mIdentity); - buffer.put(result.ik); - buffer.put(result.ck); - return buffer.array(); - } - } - - EapAkaTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode) { - return new EapAkaTypeData(EAP_AKA_CLIENT_ERROR, Arrays.asList(clientErrorCode)); - } - - EapAkaTypeData getEapSimAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { - return new EapAkaTypeData(eapSubtype, attributes); - } -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeMethodStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeMethodStateMachine.java deleted file mode 100644 index d2e8ba25..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeMethodStateMachine.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CLIENT_ERROR; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF_INPUT; - -import android.annotation.Nullable; -import android.content.Context; -import android.net.eap.EapSessionConfig.EapAkaPrimeConfig; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.crypto.KeyGenerationUtils; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.crypto.HmacSha256ByteSigner; -import com.android.internal.net.eap.message.EapData.EapMethod; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData.EapAkaPrimeTypeDataDecoder; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdf; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdfInput; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * EapAkaPrimeMethodStateMachine represents the valid paths possible for the EAP-AKA' protocol. - * - * <p>EAP-AKA' sessions will always follow the path: - * - * Created --+--> Identity --+--> Challenge --> Final - * | | - * +---------------+ - * - * <p>Note: If the EAP-Request/AKA'-Challenge message contains an AUTN with an invalid sequence - * number, the peer will indicate a synchronization failure to the server and a new challenge will - * be attempted. - * - * <p>Note: EAP-Request/Notification messages can be received at any point in the above state - * machine At most one EAP-AKA'/Notification message is allowed per EAP-AKA' session. - * - * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication Protocol - * for Authentication and Key Agreement (EAP-AKA)</a> - * @see <a href="https://tools.ietf.org/html/rfc5448">RFC 5448, Improved Extensible Authentication - * Protocol Method for 3rd Generation Authentication and Key Agreement (EAP-AKA')</a> - */ -public class EapAkaPrimeMethodStateMachine extends EapAkaMethodStateMachine { - public static final int K_AUT_LEN = 32; - public static final int K_RE_LEN = 32; - - // EAP-AKA' identity prefix (RFC 5448#3) - private static final String AKA_PRIME_IDENTITY_PREFIX = "6"; - private static final int SUPPORTED_KDF = 1; - private static final int FC = 0x20; // Required by TS 133 402 Annex A.2 - private static final int SQN_XOR_AK_LEN = 6; - private static final String MAC_ALGORITHM_STRING = "HmacSHA256"; - private static final String MK_DATA_PREFIX = "EAP-AKA'"; - - // MK_LEN_BYTES = len(K_encr | K_aut | K_re | MSK | EMSK) - private static final int MK_LEN_BYTES = - KEY_LEN + K_AUT_LEN + K_RE_LEN + (2 * SESSION_KEY_LENGTH); - - public final byte[] mKRe = new byte[getKReLen()]; - - private final EapAkaPrimeConfig mEapAkaPrimeConfig; - private final EapAkaPrimeTypeDataDecoder mEapAkaPrimeTypeDataDecoder; - - EapAkaPrimeMethodStateMachine( - Context context, byte[] eapIdentity, EapAkaPrimeConfig eapAkaPrimeConfig) { - this( - context, - eapIdentity, - eapAkaPrimeConfig, - EapAkaPrimeTypeData.getEapAkaPrimeTypeDataDecoder()); - } - - @VisibleForTesting - protected EapAkaPrimeMethodStateMachine( - Context context, - byte[] eapIdentity, - EapAkaPrimeConfig eapAkaPrimeConfig, - EapAkaPrimeTypeDataDecoder eapAkaPrimeTypeDataDecoder) { - super(context, eapIdentity, eapAkaPrimeConfig); - mEapAkaPrimeConfig = eapAkaPrimeConfig; - mEapAkaPrimeTypeDataDecoder = eapAkaPrimeTypeDataDecoder; - - transitionTo(new CreatedState()); - } - - @Override - @EapMethod - int getEapMethod() { - return EAP_TYPE_AKA_PRIME; - } - - @Override - protected int getKAutLength() { - return K_AUT_LEN; - } - - protected int getKReLen() { - return K_RE_LEN; - } - - @Override - protected DecodeResult<EapAkaTypeData> decode(byte[] typeData) { - return mEapAkaPrimeTypeDataDecoder.decode(typeData); - } - - @Override - protected String getIdentityPrefix() { - return AKA_PRIME_IDENTITY_PREFIX; - } - - @Override - protected ChallengeState buildChallengeState() { - return new ChallengeState(); - } - - @Override - protected ChallengeState buildChallengeState(byte[] identity) { - return new ChallengeState(identity); - } - - @Override - protected String getMacAlgorithm() { - return MAC_ALGORITHM_STRING; - } - - protected class ChallengeState extends EapAkaMethodStateMachine.ChallengeState { - private final String mTAG = ChallengeState.class.getSimpleName(); - - ChallengeState() { - super(); - } - - ChallengeState(byte[] identity) { - super(identity); - } - - @Override - protected EapResult handleChallengeAuthentication( - EapMessage message, EapAkaTypeData eapAkaTypeData) { - EapAkaPrimeTypeData eapAkaPrimeTypeData = (EapAkaPrimeTypeData) eapAkaTypeData; - - if (!isValidChallengeAttributes(eapAkaPrimeTypeData)) { - return buildAuthenticationRejectMessage(message.eapIdentifier); - } - return super.handleChallengeAuthentication(message, eapAkaPrimeTypeData); - } - - @VisibleForTesting - boolean isValidChallengeAttributes(EapAkaPrimeTypeData eapAkaPrimeTypeData) { - Map<Integer, EapSimAkaAttribute> attrs = eapAkaPrimeTypeData.attributeMap; - - if (!attrs.containsKey(EAP_AT_KDF) || !attrs.containsKey(EAP_AT_KDF_INPUT)) { - return false; - } - - // TODO(b/143073851): implement KDF resolution specified in RFC 5448#3.2 - // This is safe, as there only exists one defined KDF. - AtKdf atKdf = (AtKdf) attrs.get(EAP_AT_KDF); - if (atKdf.kdf != SUPPORTED_KDF) { - return false; - } - - AtKdfInput atKdfInput = (AtKdfInput) attrs.get(EAP_AT_KDF_INPUT); - if (atKdfInput.networkName.length == 0) { - return false; - } - - boolean hasMatchingNetworkNames = - hasMatchingNetworkNames( - mEapAkaPrimeConfig.networkName, - new String(atKdfInput.networkName, StandardCharsets.UTF_8)); - return mEapAkaPrimeConfig.allowMismatchedNetworkNames || hasMatchingNetworkNames; - } - - /** - * Compares the peer's network name against the server's network name. - * - * <p>RFC 5448#3.1 describes how the network names are to be compared: "each name is broken - * down to the fields separated by colons. If one of the names has more colons and fields - * than the other one, the additional fields are ignored. The remaining sequences of fields - * are compared. This algorithm allows a prefix match". - * - * @return true iff one network name matches the other, as defined by RC 5448#3.1 - */ - @VisibleForTesting - boolean hasMatchingNetworkNames(String peerNetworkName, String serverNetworkName) { - // compare network names according to RFC 5448#3.1 - if (peerNetworkName.isEmpty() || serverNetworkName.isEmpty()) { - return true; - } - - String[] peerNetworkNameFields = peerNetworkName.split(":"); - String[] serverNetworkNameFields = serverNetworkName.split(":"); - int numFieldsToCompare = - Math.min(peerNetworkNameFields.length, serverNetworkNameFields.length); - for (int i = 0; i < numFieldsToCompare; i++) { - if (!peerNetworkNameFields[i].equals(serverNetworkNameFields[i])) { - LOG.i( - mTAG, - "EAP-AKA' network names don't match." - + " Peer: " + LOG.pii(peerNetworkName) - + ", Server: " + LOG.pii(serverNetworkName)); - return false; - } - } - - return true; - } - - @Nullable - @Override - protected EapResult generateAndPersistEapAkaKeys( - RandChallengeResult result, int eapIdentifier, EapAkaTypeData eapAkaTypeData) { - try { - AtKdfInput atKdfInput = - (AtKdfInput) eapAkaTypeData.attributeMap.get(EAP_AT_KDF_INPUT); - AtAutn atAutn = (AtAutn) eapAkaTypeData.attributeMap.get(EAP_AT_AUTN); - byte[] ckIkPrime = deriveCkIkPrime(result, atKdfInput, atAutn); - - int dataToSignLen = MK_DATA_PREFIX.length() + mIdentity.length; - ByteBuffer dataToSign = ByteBuffer.allocate(dataToSignLen); - dataToSign.put(MK_DATA_PREFIX.getBytes(StandardCharsets.US_ASCII)); - dataToSign.put(mIdentity); - - ByteBuffer mk = - ByteBuffer.wrap( - KeyGenerationUtils.prfPlus( - HmacSha256ByteSigner.getInstance(), - ckIkPrime, - dataToSign.array(), - MK_LEN_BYTES)); - - mk.get(mKEncr); - mk.get(mKAut); - mk.get(mKRe); - mk.get(mMsk); - mk.get(mEmsk); - - // Log as hash unless PII debug mode enabled - LOG.d(mTAG, "K_encr=" + LOG.pii(mKEncr)); - LOG.d(mTAG, "K_aut=" + LOG.pii(mKAut)); - LOG.d(mTAG, "K_re=" + LOG.pii(mKRe)); - LOG.d(mTAG, "MSK=" + LOG.pii(mMsk)); - LOG.d(mTAG, "EMSK=" + LOG.pii(mEmsk)); - return null; - } catch (GeneralSecurityException - | BufferOverflowException - | BufferUnderflowException ex) { - LOG.e(mTAG, "Error while generating keys", ex); - return buildClientErrorResponse( - eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } - } - - /** - * Derives CK' and IK' values from CK and IK - * - * <p>CK' and IK' generation is specified in TS 133 402 Annex A.2, which relies on the key - * derivation function KDF specified in TS 133 220 Annex B.2. - */ - @VisibleForTesting - byte[] deriveCkIkPrime( - RandChallengeResult randChallengeResult, AtKdfInput atKdfInput, AtAutn atAutn) - throws GeneralSecurityException { - final int fcLen = 1; - int lengthFieldLen = 2; - - // SQN ^ AK is the first 6B of the AUTN value - byte[] sqnXorAk = Arrays.copyOf(atAutn.autn, SQN_XOR_AK_LEN); - int sLength = - fcLen - + atKdfInput.networkName.length + lengthFieldLen - + SQN_XOR_AK_LEN + lengthFieldLen; - - ByteBuffer dataToSign = ByteBuffer.allocate(sLength); - dataToSign.put((byte) FC); - dataToSign.put(atKdfInput.networkName); - dataToSign.putShort((short) atKdfInput.networkName.length); - dataToSign.put(sqnXorAk); - dataToSign.putShort((short) SQN_XOR_AK_LEN); - - int keyLen = randChallengeResult.ck.length + randChallengeResult.ik.length; - ByteBuffer key = ByteBuffer.allocate(keyLen); - key.put(randChallengeResult.ck); - key.put(randChallengeResult.ik); - - Mac mac = Mac.getInstance(MAC_ALGORITHM_STRING); - mac.init(new SecretKeySpec(key.array(), MAC_ALGORITHM_STRING)); - return mac.doFinal(dataToSign.array()); - } - } - - EapAkaPrimeTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode) { - return new EapAkaPrimeTypeData(EAP_AKA_CLIENT_ERROR, Arrays.asList(clientErrorCode)); - } - - EapAkaPrimeTypeData getEapSimAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { - return new EapAkaPrimeTypeData(eapSubtype, attributes); - } -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapMethodStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapMethodStateMachine.java deleted file mode 100644 index bab182b9..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapMethodStateMachine.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; - -import android.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData.EapMethod; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.utils.SimpleStateMachine; - -/** - * EapMethodStateMachine is an abstract class representing a state machine for EAP Method - * implementations. - */ -public abstract class EapMethodStateMachine extends SimpleStateMachine<EapMessage, EapResult> { - /** - * Returns the EAP Method type for this EapMethodStateMachine implementation. - * - * @return the IANA value for the EAP Method represented by this EapMethodStateMachine - */ - @EapMethod - abstract int getEapMethod(); - - @VisibleForTesting - protected SimpleState getState() { - return mState; - } - - @VisibleForTesting - protected void transitionTo(EapMethodState newState) { - LOG.d( - this.getClass().getSimpleName(), - "Transitioning from " + mState.getClass().getSimpleName() - + " to " + newState.getClass().getSimpleName()); - super.transitionTo(newState); - } - - abstract EapResult handleEapNotification(String tag, EapMessage message); - - protected abstract class EapMethodState extends SimpleState { - /** - * Handles premature EAP-Success and EAP-Failure messages, as well as EAP-Notification - * messages. - * - * @param tag the String logging tag to be used while handing message - * @param message the EapMessage to be checked for early Success/Failure/Notification - * messages - * @return the EapResult generated from handling the give EapMessage, or null if the message - * Type matches that of the current EAP method - */ - @Nullable - EapResult handleEapSuccessFailureNotification(String tag, EapMessage message) { - if (message.eapCode == EAP_CODE_SUCCESS) { - // EAP-SUCCESS is required to be the last EAP message sent during the EAP protocol, - // so receiving a premature SUCCESS message is an unrecoverable error. - return new EapError( - new EapInvalidRequestException( - "Received an EAP-Success in the " + tag)); - } else if (message.eapCode == EAP_CODE_FAILURE) { - transitionTo(new EapAkaMethodStateMachine.FinalState()); - return new EapFailure(); - } else if (message.eapData.eapType == EAP_NOTIFICATION) { - return handleEapNotification(tag, message); - } else if (message.eapData.eapType != getEapMethod()) { - return new EapError(new EapInvalidRequestException( - "Expected EAP Type " + getEapMethod() - + ", received " + message.eapData.eapType)); - } - - return null; - } - } - - protected class FinalState extends EapMethodState { - @Override - public EapResult process(EapMessage msg) { - return new EapError( - new IllegalStateException("Attempting to process from a FinalState")); - } - } -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2MethodStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2MethodStateMachine.java deleted file mode 100644 index 490b8ba1..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2MethodStateMachine.java +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_STRING; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_RESPONSE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_FAILURE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_SUCCESS; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_OP_CODE_STRING; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2FailureRequest.EAP_ERROR_CODE_STRING; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2FailureResponse.getEapMsChapV2FailureResponse; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2SuccessResponse.getEapMsChapV2SuccessResponse; - -import android.net.eap.EapSessionConfig.EapMsChapV2Config; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.crypto.ParityBitUtil; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.EapSilentException; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapData.EapMethod; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeResponse; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2FailureRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2SuccessRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; -import com.android.internal.net.utils.Log; -import com.android.org.bouncycastle.crypto.digests.MD4Digest; - -import java.io.UnsupportedEncodingException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.SecureRandom; -import java.util.Arrays; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESKeySpec; - -/** - * EapMsChapV2MethodStateMachine represents the valid paths possible for the EAP MSCHAPv2 protocol. - * - * <p>EAP MSCHAPv2 sessions will always follow the path: - * - * <p>CreatedState - * | - * +--> ChallengeState - * | - * +--> ValidateAuthenticatorState --+--> AwaitingEapSuccessState --> FinalState - * | - * +--> AwaitingEapFailureState --> FinalState - * - * <p>Note: All Failure-Request messages received in the PostChallenge state will be responded to - * with Failure-Response messages. That is, retryable failures <i>will not</i> be retried. - * - * <p>Note: The EAP standard states that EAP methods may disallow EAP Notification messages for the - * duration of the method (RFC 3748#5.2). EAP MSCHAPv2 does not explicitly ban these packets, so - * they are allowed at any time (except once a terminal state is reached). - * - * @see <a href="https://tools.ietf.org/html/draft-kamath-pppext-eap-mschapv2-02">Microsoft EAP CHAP - * Extensions Draft (EAP MSCHAPv2)</a> - * @see <a href="https://tools.ietf.org/html/rfc2759">RFC 2759, Microsoft PPP CHAP Extensions, - * Version 2 (MSCHAPv2)</a> - * @see <a href="https://tools.ietf.org/html/rfc3079">RFC 3079, Deriving Keys for use with Microsoft - * Point-to-Point Encryption (MPPE)</a> - */ -public class EapMsChapV2MethodStateMachine extends EapMethodStateMachine { - private static final String SHA_ALG = "SHA-1"; - private static final String DES_ALG = "DES/ECB/NoPadding"; - private static final String DES_KEY_FACTORY = "DES"; - private static final int PEER_CHALLENGE_SIZE = 16; - private static final int CHALLENGE_HASH_LEN = 8; - private static final int PASSWORD_HASH_LEN = 16; - private static final int PASSWORD_HASH_HASH_LEN = 16; - private static final int RESPONSE_LEN = 24; - private static final int Z_PASSWORD_HASH_LEN = 21; - private static final int Z_PASSWORD_SECTION_LEN = 7; - private static final int RESPONSE_SECTION_LEN = 8; - private static final int SHS_PAD_LEN = 40; - private static final int MASTER_KEY_LEN = 16; - private static final int SESSION_KEY_LEN = 16; - private static final int MASTER_SESSION_KEY_LEN = 2 * SESSION_KEY_LEN; - - // Reserved for future use and must be 0 (EAP MSCHAPv2#2.2) - private static final int FLAGS = 0; - - // we all need a little magic in our lives - // Defined in RFC 2759#8.7. Constants used for Success response generation. - private static final byte[] CHALLENGE_MAGIC_1 = { - (byte) 0x4D, (byte) 0x61, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x20, (byte) 0x73, - (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x74, - (byte) 0x6F, (byte) 0x20, (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E, - (byte) 0x74, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x67, (byte) 0x6E, (byte) 0x69, - (byte) 0x6E, (byte) 0x67, (byte) 0x20, (byte) 0x63, (byte) 0x6F, (byte) 0x6E, (byte) 0x73, - (byte) 0x74, (byte) 0x61, (byte) 0x6E, (byte) 0x74 - }; - private static final byte[] CHALLENGE_MAGIC_2 = { - (byte) 0x50, (byte) 0x61, (byte) 0x64, (byte) 0x20, (byte) 0x74, (byte) 0x6F, (byte) 0x20, - (byte) 0x6D, (byte) 0x61, (byte) 0x6B, (byte) 0x65, (byte) 0x20, (byte) 0x69, (byte) 0x74, - (byte) 0x20, (byte) 0x64, (byte) 0x6F, (byte) 0x20, (byte) 0x6D, (byte) 0x6F, (byte) 0x72, - (byte) 0x65, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x61, (byte) 0x6E, (byte) 0x20, - (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x20, (byte) 0x69, (byte) 0x74, (byte) 0x65, - (byte) 0x72, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6F, (byte) 0x6E - }; - - // Defined in RFC 3079#3.4. Constants used for Master Session Key (MSK) generation - private static final byte[] SHS_PAD_1 = new byte[SHS_PAD_LEN]; - private static final byte[] SHS_PAD_2 = new byte[SHS_PAD_LEN]; - - static { - Arrays.fill(SHS_PAD_2, (byte) 0xF2); - } - - private static final byte[] MSK_MAGIC_1 = { - (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69, - (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x20, - (byte) 0x4D, (byte) 0x50, (byte) 0x50, (byte) 0x45, (byte) 0x20, (byte) 0x4D, - (byte) 0x61, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x20, - (byte) 0x4B, (byte) 0x65, (byte) 0x79 - }; - private static final byte[] MSK_MAGIC_2 = { - (byte) 0x4F, (byte) 0x6E, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, - (byte) 0x20, (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E, - (byte) 0x74, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x64, (byte) 0x65, - (byte) 0x2C, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x69, (byte) 0x73, - (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68, - (byte) 0x65, (byte) 0x20, (byte) 0x73, (byte) 0x65, (byte) 0x6E, (byte) 0x64, - (byte) 0x20, (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x3B, (byte) 0x20, - (byte) 0x6F, (byte) 0x6E, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, - (byte) 0x20, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65, - (byte) 0x72, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x64, (byte) 0x65, - (byte) 0x2C, (byte) 0x20, (byte) 0x69, (byte) 0x74, (byte) 0x20, (byte) 0x69, - (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x20, - (byte) 0x72, (byte) 0x65, (byte) 0x63, (byte) 0x65, (byte) 0x69, (byte) 0x76, - (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x2E - }; - private static final byte[] MSK_MAGIC_3 = { - (byte) 0x4F, (byte) 0x6E, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, - (byte) 0x20, (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E, - (byte) 0x74, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x64, (byte) 0x65, - (byte) 0x2C, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x69, (byte) 0x73, - (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68, - (byte) 0x65, (byte) 0x20, (byte) 0x72, (byte) 0x65, (byte) 0x63, (byte) 0x65, - (byte) 0x69, (byte) 0x76, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65, - (byte) 0x79, (byte) 0x3B, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x20, - (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x20, (byte) 0x73, (byte) 0x65, - (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x73, - (byte) 0x69, (byte) 0x64, (byte) 0x65, (byte) 0x2C, (byte) 0x20, (byte) 0x69, - (byte) 0x74, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x74, - (byte) 0x68, (byte) 0x65, (byte) 0x20, (byte) 0x73, (byte) 0x65, (byte) 0x6E, - (byte) 0x64, (byte) 0x20, (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x2E - }; - - private final EapMsChapV2Config mEapMsChapV2Config; - private final SecureRandom mSecureRandom; - private final EapMsChapV2TypeDataDecoder mTypeDataDecoder; - - public EapMsChapV2MethodStateMachine( - EapMsChapV2Config eapMsChapV2Config, SecureRandom secureRandom) { - this(eapMsChapV2Config, secureRandom, new EapMsChapV2TypeDataDecoder()); - } - - @VisibleForTesting - EapMsChapV2MethodStateMachine( - EapMsChapV2Config eapMsChapV2Config, - SecureRandom secureRandom, - EapMsChapV2TypeDataDecoder eapMsChapV2TypeDataDecoder) { - this.mEapMsChapV2Config = eapMsChapV2Config; - this.mSecureRandom = secureRandom; - this.mTypeDataDecoder = eapMsChapV2TypeDataDecoder; - - transitionTo(new CreatedState()); - } - - @Override - @EapMethod - int getEapMethod() { - return EAP_TYPE_MSCHAP_V2; - } - - @Override - EapResult handleEapNotification(String tag, EapMessage message) { - return EapStateMachine.handleNotification(tag, message); - } - - protected class CreatedState extends EapMethodState { - private final String mTAG = this.getClass().getSimpleName(); - - @Override - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - DecodeResult<EapMsChapV2ChallengeRequest> decodeResult = - mTypeDataDecoder.decodeChallengeRequest(mTAG, message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return decodeResult.eapError; - } - - return transitionAndProcess(new ChallengeState(), message); - } - } - - protected class ChallengeState extends EapMethodState { - private final String mTAG = this.getClass().getSimpleName(); - - @Override - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - DecodeResult<EapMsChapV2ChallengeRequest> decodeResult = - mTypeDataDecoder.decodeChallengeRequest(mTAG, message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return decodeResult.eapError; - } - - EapMsChapV2ChallengeRequest challengeRequest = decodeResult.eapTypeData; - LOG.d( - mTAG, - "Received Challenge Request:" - + " Challenge=" + LOG.pii(challengeRequest.challenge) - + " Server-Name=" + Log.byteArrayToHexString(challengeRequest.name)); - - byte[] peerChallenge = new byte[PEER_CHALLENGE_SIZE]; - mSecureRandom.nextBytes(peerChallenge); - - byte[] ntResponse; - try { - ntResponse = - generateNtResponse( - challengeRequest.challenge, - peerChallenge, - mEapMsChapV2Config.username, - mEapMsChapV2Config.password); - } catch (GeneralSecurityException ex) { - LOG.e(mTAG, "Error generating EAP MSCHAPv2 Challenge response", ex); - return new EapError(ex); - } - - LOG.d( - mTAG, - "Generating Challenge Response:" - + " Username=" + LOG.pii(mEapMsChapV2Config.username) - + " Peer-Challenge=" + LOG.pii(peerChallenge) - + " NT-Response=" + LOG.pii(ntResponse)); - - try { - EapMsChapV2ChallengeResponse challengeResponse = - new EapMsChapV2ChallengeResponse( - challengeRequest.msChapV2Id, - peerChallenge, - ntResponse, - FLAGS, - usernameToBytes(mEapMsChapV2Config.username)); - transitionTo( - new ValidateAuthenticatorState( - challengeRequest.challenge, peerChallenge, ntResponse)); - - return buildEapMessageResponse(mTAG, message.eapIdentifier, challengeResponse); - } catch (EapMsChapV2ParsingException ex) { - LOG.e(mTAG, "Error building response type data", ex); - return new EapError(ex); - } - } - } - - protected class ValidateAuthenticatorState extends EapMethodState { - private final String mTAG = this.getClass().getSimpleName(); - - private final byte[] mAuthenticatorChallenge; - private final byte[] mPeerChallenge; - private final byte[] mNtResponse; - - @VisibleForTesting - ValidateAuthenticatorState( - byte[] authenticatorChallenge, byte[] peerChallenge, byte[] ntResponse) { - this.mAuthenticatorChallenge = authenticatorChallenge; - this.mPeerChallenge = peerChallenge; - this.mNtResponse = ntResponse; - } - - @Override - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - int opCode; - try { - opCode = mTypeDataDecoder.getOpCode(message.eapData.eapTypeData); - } catch (BufferUnderflowException ex) { - LOG.e(mTAG, "Empty type data received in ValidateAuthenticatorState", ex); - return new EapError(ex); - } - - LOG.d( - mTAG, - "Received Op Code: " - + EAP_OP_CODE_STRING.getOrDefault(opCode, "Unknown") - + " (" + opCode + ")"); - - switch (opCode) { - case EAP_MSCHAP_V2_SUCCESS: - DecodeResult<EapMsChapV2SuccessRequest> successDecodeResult = - mTypeDataDecoder.decodeSuccessRequest( - mTAG, message.eapData.eapTypeData); - if (!successDecodeResult.isSuccessfulDecode()) { - return successDecodeResult.eapError; - } - - EapMsChapV2SuccessRequest successRequest = successDecodeResult.eapTypeData; - LOG.d( - mTAG, - "Received SuccessRequest:" - + " Auth-String=" + LOG.pii(successRequest.authBytes) - + " Message=" + successRequest.message); - - boolean isSuccessfulAuth; - try { - isSuccessfulAuth = - checkAuthenticatorResponse( - mEapMsChapV2Config.password, - mNtResponse, - mPeerChallenge, - mAuthenticatorChallenge, - mEapMsChapV2Config.username, - successRequest.authBytes); - } catch (GeneralSecurityException | UnsupportedEncodingException ex) { - LOG.e(mTAG, "Error validating MSCHAPv2 Authenticator Response", ex); - return new EapError(ex); - } - - if (!isSuccessfulAuth) { - LOG.e( - mTAG, - "Authenticator Response does not match expected response value"); - transitionTo(new FinalState()); - return new EapFailure(); - } - - transitionTo(new AwaitingEapSuccessState(mNtResponse)); - return buildEapMessageResponse( - mTAG, message.eapIdentifier, getEapMsChapV2SuccessResponse()); - - case EAP_MSCHAP_V2_FAILURE: - DecodeResult<EapMsChapV2FailureRequest> failureDecodeResult = - mTypeDataDecoder.decodeFailureRequest( - mTAG, message.eapData.eapTypeData); - if (!failureDecodeResult.isSuccessfulDecode()) { - return failureDecodeResult.eapError; - } - - EapMsChapV2FailureRequest failureRequest = failureDecodeResult.eapTypeData; - int errorCode = failureRequest.errorCode; - LOG.e( - mTAG, - String.format( - "Received MSCHAPv2 Failure-Request: E=%s (%d) R=%b V=%d M=%s", - EAP_ERROR_CODE_STRING.getOrDefault(errorCode, "UNKNOWN"), - errorCode, - failureRequest.isRetryable, - failureRequest.passwordChangeProtocol, - failureRequest.message)); - transitionTo(new AwaitingEapFailureState()); - return buildEapMessageResponse( - mTAG, message.eapIdentifier, getEapMsChapV2FailureResponse()); - - default: - LOG.e(mTAG, "Invalid OpCode: " + opCode); - return new EapError( - new EapInvalidRequestException( - "Unexpected request received in EAP MSCHAPv2")); - } - } - } - - protected class AwaitingEapSuccessState extends EapMethodState { - private final String mTAG = this.getClass().getSimpleName(); - - private final byte[] mNtResponse; - - AwaitingEapSuccessState(byte[] ntResponse) { - this.mNtResponse = ntResponse; - } - - @Override - public EapResult process(EapMessage message) { - if (message.eapCode == EAP_CODE_FAILURE) { - LOG.e(mTAG, "Received EAP-Failure in PreSuccessState"); - transitionTo(new FinalState()); - return new EapFailure(); - } else if (message.eapCode != EAP_CODE_SUCCESS) { - int eapType = message.eapData.eapType; - if (eapType == EAP_NOTIFICATION) { - return handleEapNotification(mTAG, message); - } else { - LOG.e( - mTAG, - "Received unexpected EAP message. Type=" - + EAP_TYPE_STRING.getOrDefault( - eapType, "UNKNOWN (" + eapType + ")")); - return new EapError( - new EapInvalidRequestException( - "Expected EAP Type " - + getEapMethod() - + ", received " - + eapType)); - } - } - - try { - byte[] msk = generateMsk(mEapMsChapV2Config.password, mNtResponse); - transitionTo(new FinalState()); - return new EapSuccess(msk, new byte[0]); - } catch (GeneralSecurityException | UnsupportedEncodingException ex) { - LOG.e(mTAG, "Error generating MSK for EAP MSCHAPv2", ex); - return new EapError(ex); - } - } - } - - protected class AwaitingEapFailureState extends EapMethodState { - private final String mTAG = this.getClass().getSimpleName(); - - @Override - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - int eapType = message.eapData.eapType; - LOG.e( - mTAG, - "Received unexpected EAP message. Type=" - + EAP_TYPE_STRING.getOrDefault(eapType, "UNKNOWN (" + eapType + ")")); - return new EapError( - new EapInvalidRequestException( - "Expected EAP Type " + getEapMethod() + ", received " + eapType)); - } - } - - private EapResult buildEapMessageResponse( - String tag, int eapIdentifier, EapMsChapV2TypeData typeData) { - try { - EapData eapData = new EapData(getEapMethod(), typeData.encode()); - EapMessage eapMessage = new EapMessage(EAP_CODE_RESPONSE, eapIdentifier, eapData); - return EapResponse.getEapResponse(eapMessage); - } catch (EapSilentException ex) { - LOG.e(tag, "Error building response EapMessage", ex); - return new EapError(ex); - } - } - - /** Util for converting String username to "0-to-256 char username", as used in RFC 2759#8. */ - @VisibleForTesting - static byte[] usernameToBytes(String username) { - return username.getBytes(StandardCharsets.US_ASCII); - } - - /** - * Util for converting String password to "0-to-256-unicode-char password", as used in - * RFC 2759#8. - */ - @VisibleForTesting - static byte[] passwordToBytes(String password) { - return password.getBytes(StandardCharsets.UTF_16LE); - } - - /* Implementation of RFC 2759#8.1: GenerateNTResponse() */ - @VisibleForTesting - static byte[] generateNtResponse( - byte[] authenticatorChallenge, byte[] peerChallenge, String username, String password) - throws GeneralSecurityException { - byte[] challenge = challengeHash(peerChallenge, authenticatorChallenge, username); - byte[] passwordHash = ntPasswordHash(password); - return challengeResponse(challenge, passwordHash); - } - - /* Implementation of RFC 2759#8.2: ChallengeHash() */ - @VisibleForTesting - static byte[] challengeHash( - byte[] peerChallenge, byte[] authenticatorChallenge, String username) - throws GeneralSecurityException { - MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG); - sha1.update(peerChallenge); - sha1.update(authenticatorChallenge); - sha1.update(usernameToBytes(username)); - return Arrays.copyOf(sha1.digest(), CHALLENGE_HASH_LEN); - } - - /* Implementation of RFC 2759#8.3: NtPasswordHash() */ - @VisibleForTesting - static byte[] ntPasswordHash(String password) { - MD4Digest md4 = new MD4Digest(); - byte[] passwordBytes = passwordToBytes(password); - md4.update(passwordBytes, 0, passwordBytes.length); - - byte[] passwordHash = new byte[PASSWORD_HASH_LEN]; - md4.doFinal(passwordHash, 0); - return passwordHash; - } - - /* Implementation of RFC 2759#8.4: HashNtPasswordHash() */ - @VisibleForTesting - static byte[] hashNtPasswordHash(byte[] passwordHash) { - MD4Digest md4 = new MD4Digest(); - md4.update(passwordHash, 0, passwordHash.length); - - byte[] passwordHashHash = new byte[PASSWORD_HASH_HASH_LEN]; - md4.doFinal(passwordHashHash, 0); - return passwordHashHash; - } - - /* Implementation of RFC 2759#8.5: ChallengeResponse() */ - @VisibleForTesting - static byte[] challengeResponse(byte[] challenge, byte[] passwordHash) - throws GeneralSecurityException { - byte[] zPasswordHash = Arrays.copyOf(passwordHash, Z_PASSWORD_HASH_LEN); - - ByteBuffer response = ByteBuffer.allocate(RESPONSE_LEN); - for (int i = 0; i < 3; i++) { - int from = i * Z_PASSWORD_SECTION_LEN; - int to = from + Z_PASSWORD_SECTION_LEN; - byte[] zPasswordSection = Arrays.copyOfRange(zPasswordHash, from, to); - response.put(desEncrypt(challenge, zPasswordSection)); - } - return response.array(); - } - - /* Implementation of RFC 2759#8.6: DesEncrypt() */ - @VisibleForTesting - static byte[] desEncrypt(byte[] clear, byte[] key) throws GeneralSecurityException { - if (key.length != Z_PASSWORD_SECTION_LEN) { - throw new IllegalArgumentException("DES Key must be 7B before parity-bits are added"); - } - - key = ParityBitUtil.addParityBits(key); - SecretKey secretKey = - SecretKeyFactory.getInstance(DES_KEY_FACTORY).generateSecret(new DESKeySpec(key)); - - Cipher des = Cipher.getInstance(DES_ALG); - des.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] output = des.doFinal(clear); - - // RFC 2759#8.6 specifies 8B outputs for DesEncrypt() - return Arrays.copyOf(output, RESPONSE_SECTION_LEN); - } - - /** - * Implementation of RFC 2759#8.7: GenerateAuthenticatorResponse() - * - * <p>Keep response as byte[] so checkAuthenticatorResponse() can easily compare byte[]'s - */ - @VisibleForTesting - static byte[] generateAuthenticatorResponse( - String password, - byte[] ntResponse, - byte[] peerChallenge, - byte[] authenticatorChallenge, - String username) - throws GeneralSecurityException, UnsupportedEncodingException { - byte[] passwordHash = ntPasswordHash(password); - byte[] passwordHashHash = hashNtPasswordHash(passwordHash); - - MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG); - sha1.update(passwordHashHash); - sha1.update(ntResponse); - sha1.update(CHALLENGE_MAGIC_1); // add just a dash of magic - byte[] digest = sha1.digest(); - - byte[] challenge = challengeHash(peerChallenge, authenticatorChallenge, username); - - sha1.update(digest); - sha1.update(challenge); - sha1.update(CHALLENGE_MAGIC_2); - - return sha1.digest(); - } - - /* Implementation of RFC 2759#8.8: CheckAuthenticatorResponse() */ - @VisibleForTesting - static boolean checkAuthenticatorResponse( - String password, - byte[] ntResponse, - byte[] peerChallenge, - byte[] authenticatorChallenge, - String userName, - byte[] receivedResponse) - throws GeneralSecurityException, UnsupportedEncodingException { - byte[] myResponse = - generateAuthenticatorResponse( - password, ntResponse, peerChallenge, authenticatorChallenge, userName); - return Arrays.equals(myResponse, receivedResponse); - } - - /* Implementation of RFC 3079#3.4: GetMasterKey() */ - @VisibleForTesting - static byte[] getMasterKey(byte[] passwordHashHash, byte[] ntResponse) - throws GeneralSecurityException { - MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG); - sha1.update(passwordHashHash); - sha1.update(ntResponse); - sha1.update(MSK_MAGIC_1); - return Arrays.copyOf(sha1.digest(), MASTER_KEY_LEN); - } - - /* Implementation of RFC 3079#3.4: GetAsymmetricStartKey() */ - @VisibleForTesting - static byte[] getAsymmetricStartKey(byte[] masterKey, boolean isSend) - throws GeneralSecurityException { - // salt: referred to as 's' in RFC 3079#3.4 GetAsymmetricStartKey() - byte[] salt = isSend ? MSK_MAGIC_2 : MSK_MAGIC_3; - MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG); - sha1.update(masterKey); - sha1.update(SHS_PAD_1); - sha1.update(salt); - sha1.update(SHS_PAD_2); - return Arrays.copyOf(sha1.digest(), SESSION_KEY_LEN); - } - - @VisibleForTesting - static byte[] generateMsk(String password, byte[] ntResponse) - throws GeneralSecurityException, UnsupportedEncodingException { - byte[] passwordHash = ntPasswordHash(password); - byte[] passwordHashHash = hashNtPasswordHash(passwordHash); - byte[] masterKey = getMasterKey(passwordHashHash, ntResponse); - - // MSK: SendKey + ReceiveKey - ByteBuffer msk = ByteBuffer.allocate(MASTER_SESSION_KEY_LEN); - msk.put(getAsymmetricStartKey(masterKey, true /* isSend */)); - msk.put(getAsymmetricStartKey(masterKey, false /* isSend */)); - - return msk.array(); - } -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapSimAkaMethodStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapSimAkaMethodStateMachine.java deleted file mode 100644 index dfd16276..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapSimAkaMethodStateMachine.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_RESPONSE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NOTIFICATION; - -import android.net.eap.EapSessionConfig.EapUiccConfig; -import android.telephony.TelephonyManager; -import android.util.Base64; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.crypto.Fips186_2Prf; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.EapSilentException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData; -import com.android.internal.net.utils.Log; - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * EapSimAkaMethodStateMachine represents an abstract state machine for managing EAP-SIM and EAP-AKA - * sessions. - * - * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication - * Protocol for Subscriber Identity Modules (EAP-SIM)</a> - * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication - * Protocol for Authentication and Key Agreement (EAP-AKA)</a> - */ -public abstract class EapSimAkaMethodStateMachine extends EapMethodStateMachine { - public static final String MASTER_KEY_GENERATION_ALG = "SHA-1"; - public static final String MAC_ALGORITHM_STRING = "HmacSHA1"; - - // K_encr and K_aut lengths are 16 bytes (RFC 4186#7, RFC 4187#7) - public static final int KEY_LEN = 16; - - // Session Key lengths are 64 bytes (RFC 4186#7, RFC 4187#7) - public static final int SESSION_KEY_LENGTH = 64; - - public final byte[] mKEncr = new byte[getKEncrLength()]; - public final byte[] mKAut = new byte[getKAutLength()]; - public final byte[] mMsk = new byte[getMskLength()]; - public final byte[] mEmsk = new byte[getEmskLength()]; - - @VisibleForTesting boolean mHasReceivedSimAkaNotification = false; - - final TelephonyManager mTelephonyManager; - final byte[] mEapIdentity; - final EapUiccConfig mEapUiccConfig; - - @VisibleForTesting Mac mMacAlgorithm; - - EapSimAkaMethodStateMachine( - TelephonyManager telephonyManager, byte[] eapIdentity, EapUiccConfig eapUiccConfig) { - if (telephonyManager == null) { - throw new IllegalArgumentException("TelephonyManager must be non-null"); - } else if (eapIdentity == null) { - throw new IllegalArgumentException("EapIdentity must be non-null"); - } else if (eapUiccConfig == null) { - throw new IllegalArgumentException("EapUiccConfig must be non-null"); - } - this.mTelephonyManager = telephonyManager; - this.mEapIdentity = eapIdentity; - this.mEapUiccConfig = eapUiccConfig; - - LOG.d( - this.getClass().getSimpleName(), - mEapUiccConfig.getClass().getSimpleName() + ":" - + " subId=" + mEapUiccConfig.subId - + " apptype=" + mEapUiccConfig.apptype); - } - - protected int getKEncrLength() { - return KEY_LEN; - } - - protected int getKAutLength() { - return KEY_LEN; - } - - protected int getMskLength() { - return SESSION_KEY_LENGTH; - } - - protected int getEmskLength() { - return SESSION_KEY_LENGTH; - } - - @Override - EapResult handleEapNotification(String tag, EapMessage message) { - return EapStateMachine.handleNotification(tag, message); - } - - protected String getMacAlgorithm() { - return MAC_ALGORITHM_STRING; - } - - @VisibleForTesting - EapResult buildClientErrorResponse( - int eapIdentifier, - int eapMethodType, - AtClientErrorCode clientErrorCode) { - EapSimAkaTypeData eapSimAkaTypeData = getEapSimAkaTypeData(clientErrorCode); - byte[] encodedTypeData = eapSimAkaTypeData.encode(); - - EapData eapData = new EapData(eapMethodType, encodedTypeData); - try { - EapMessage response = new EapMessage(EAP_CODE_RESPONSE, eapIdentifier, eapData); - return EapResult.EapResponse.getEapResponse(response); - } catch (EapSilentException ex) { - return new EapResult.EapError(ex); - } - } - - @VisibleForTesting - EapResult buildResponseMessage( - int eapType, - int eapSubtype, - int identifier, - List<EapSimAkaAttribute> attributes) { - EapSimAkaTypeData eapSimTypeData = getEapSimAkaTypeData(eapSubtype, attributes); - EapData eapData = new EapData(eapType, eapSimTypeData.encode()); - - try { - EapMessage eapMessage = new EapMessage(EAP_CODE_RESPONSE, identifier, eapData); - return EapResult.EapResponse.getEapResponse(eapMessage); - } catch (EapSilentException ex) { - return new EapResult.EapError(ex); - } - } - - @VisibleForTesting - protected void generateAndPersistKeys( - String tag, MessageDigest sha1, Fips186_2Prf prf, byte[] mkInput) { - byte[] mk = sha1.digest(mkInput); - - // run mk through FIPS 186-2 - int outputBytes = mKEncr.length + mKAut.length + mMsk.length + mEmsk.length; - byte[] prfResult = prf.getRandom(mk, outputBytes); - - ByteBuffer prfResultBuffer = ByteBuffer.wrap(prfResult); - prfResultBuffer.get(mKEncr); - prfResultBuffer.get(mKAut); - prfResultBuffer.get(mMsk); - prfResultBuffer.get(mEmsk); - - // Log as hash unless PII debug mode enabled - LOG.d(tag, "K_encr=" + LOG.pii(mKEncr)); - LOG.d(tag, "K_aut=" + LOG.pii(mKAut)); - LOG.d(tag, "MSK=" + LOG.pii(mMsk)); - LOG.d(tag, "EMSK=" + LOG.pii(mEmsk)); - } - - @VisibleForTesting - byte[] processUiccAuthentication(String tag, int authType, byte[] formattedChallenge) throws - EapSimAkaAuthenticationFailureException { - String base64Challenge = Base64.encodeToString(formattedChallenge, Base64.NO_WRAP); - String base64Response = - mTelephonyManager.getIccAuthentication( - mEapUiccConfig.apptype, - authType, - base64Challenge); - - if (base64Response == null) { - String msg = "UICC authentication failed. Input: " + LOG.pii(formattedChallenge); - LOG.e(tag, msg); - throw new EapSimAkaAuthenticationFailureException(msg); - } - - return Base64.decode(base64Response, Base64.DEFAULT); - } - - @VisibleForTesting - boolean isValidMac(String tag, EapMessage message, EapSimAkaTypeData typeData, byte[] extraData) - throws GeneralSecurityException, EapSimAkaInvalidAttributeException, - EapSilentException { - mMacAlgorithm = Mac.getInstance(getMacAlgorithm()); - mMacAlgorithm.init(new SecretKeySpec(mKAut, getMacAlgorithm())); - - byte[] mac = getMac(message.eapCode, message.eapIdentifier, typeData, extraData); - // attributes are 'valid', so must have AtMac - AtMac atMac = (AtMac) typeData.attributeMap.get(EAP_AT_MAC); - - boolean isValidMac = Arrays.equals(mac, atMac.mac); - if (!isValidMac) { - // MAC in message != calculated mac - LOG.e( - tag, - "Received message with invalid Mac." - + " received=" + Log.byteArrayToHexString(atMac.mac) - + ", computed=" + Log.byteArrayToHexString(mac)); - } - - return isValidMac; - } - - @VisibleForTesting - byte[] getMac(int eapCode, int eapIdentifier, EapSimAkaTypeData typeData, byte[] extraData) - throws EapSimAkaInvalidAttributeException, EapSilentException { - if (mMacAlgorithm == null) { - throw new IllegalStateException( - "Can't calculate MAC before mMacAlgorithm is set in ChallengeState"); - } - - // cache original Mac so it can be restored after calculating the Mac - AtMac originalMac = (AtMac) typeData.attributeMap.get(EAP_AT_MAC); - typeData.attributeMap.put(EAP_AT_MAC, new AtMac()); - - byte[] typeDataWithEmptyMac = typeData.encode(); - EapData eapData = new EapData(getEapMethod(), typeDataWithEmptyMac); - EapMessage messageForMac = new EapMessage(eapCode, eapIdentifier, eapData); - - ByteBuffer buffer = ByteBuffer.allocate(messageForMac.eapLength + extraData.length); - buffer.put(messageForMac.encode()); - buffer.put(extraData); - byte[] mac = mMacAlgorithm.doFinal(buffer.array()); - - typeData.attributeMap.put(EAP_AT_MAC, originalMac); - - // need HMAC-SHA1-128 - first 16 bytes of SHA1 (RFC 4186#10.14, RFC 4187#10.15) - return Arrays.copyOfRange(mac, 0, AtMac.MAC_LENGTH); - } - - @VisibleForTesting - EapResult buildResponseMessageWithMac(int identifier, int eapSubtype, byte[] extraData) { - // capacity of 1 for AtMac to be added - return buildResponseMessageWithMac(identifier, eapSubtype, extraData, new ArrayList<>(1)); - } - - @VisibleForTesting - EapResult buildResponseMessageWithMac( - int identifier, int eapSubtype, byte[] extraData, List<EapSimAkaAttribute> attributes) { - try { - attributes = new ArrayList<>(attributes); - attributes.add(new AtMac()); - EapSimAkaTypeData eapSimAkaTypeData = getEapSimAkaTypeData(eapSubtype, attributes); - - byte[] mac = getMac(EAP_CODE_RESPONSE, identifier, eapSimAkaTypeData, extraData); - - eapSimAkaTypeData.attributeMap.put(EAP_AT_MAC, new AtMac(mac)); - EapData eapData = new EapData(getEapMethod(), eapSimAkaTypeData.encode()); - EapMessage eapMessage = new EapMessage(EAP_CODE_RESPONSE, identifier, eapData); - return EapResponse.getEapResponse(eapMessage); - } catch (EapSimAkaInvalidAttributeException | EapSilentException ex) { - // this should never happen - return new EapError(ex); - } - } - - @VisibleForTesting - EapResult handleEapSimAkaNotification( - String tag, - boolean isPreChallengeState, - int identifier, - EapSimAkaTypeData eapSimAkaTypeData) { - // EAP-SIM exchanges must not include more than one EAP-SIM notification round - // (RFC 4186#6.1, RFC 4187#6.1) - if (mHasReceivedSimAkaNotification) { - return new EapError( - new EapInvalidRequestException("Received multiple EAP-SIM notifications")); - } - - mHasReceivedSimAkaNotification = true; - AtNotification atNotification = - (AtNotification) eapSimAkaTypeData.attributeMap.get(EAP_AT_NOTIFICATION); - - LOG.d( - tag, - "Received AtNotification:" - + " S=" + (atNotification.isSuccessCode ? "1" : "0") - + " P=" + (atNotification.isPreSuccessfulChallenge ? "1" : "0") - + " Code=" + atNotification.notificationCode); - - // P bit of notification code is only allowed after a successful challenge round. This is - // only possible in the ChallengeState (RFC 4186#6.1, RFC 4187#6.1) - if (isPreChallengeState && !atNotification.isPreSuccessfulChallenge) { - return buildClientErrorResponse( - identifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } - - if (atNotification.isPreSuccessfulChallenge) { - // AT_MAC attribute must not be included when the P bit is set (RFC 4186#9.8, - // RFC 4187#9.10) - if (eapSimAkaTypeData.attributeMap.containsKey(EAP_AT_MAC)) { - return buildClientErrorResponse( - identifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } - - return buildResponseMessage( - getEapMethod(), eapSimAkaTypeData.eapSubtype, identifier, Arrays.asList()); - } else if (!eapSimAkaTypeData.attributeMap.containsKey(EAP_AT_MAC)) { - // MAC must be included for messages with their P bit not set (RFC 4186#9.8, - // RFC 4187#9.10) - return buildClientErrorResponse( - identifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } - - try { - byte[] mac = getMac(EAP_CODE_REQUEST, identifier, eapSimAkaTypeData, new byte[0]); - - AtMac atMac = (AtMac) eapSimAkaTypeData.attributeMap.get(EAP_AT_MAC); - if (!Arrays.equals(mac, atMac.mac)) { - // MAC in message != calculated mac - return buildClientErrorResponse( - identifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); - } - } catch (EapSilentException | EapSimAkaInvalidAttributeException ex) { - // We can't continue if the MAC can't be generated - return new EapError(ex); - } - - // server has been authenticated, so we can send a response - return buildResponseMessageWithMac(identifier, eapSimAkaTypeData.eapSubtype, new byte[0]); - } - - abstract EapSimAkaTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode); - abstract EapSimAkaTypeData getEapSimAkaTypeData( - int eapSubtype, List<EapSimAkaAttribute> attributes); -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapSimMethodStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapSimMethodStateMachine.java deleted file mode 100644 index 005cfb96..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapSimMethodStateMachine.java +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ENCR_DATA; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_FULLAUTH_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IV; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_VERSION_LIST; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_CLIENT_ERROR; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_START; - -import android.annotation.Nullable; -import android.content.Context; -import android.net.eap.EapSessionConfig.EapSimConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.crypto.Fips186_2Prf; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.EapSilentException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaIdentityUnavailableException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidLengthException; -import com.android.internal.net.eap.message.EapData.EapMethod; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceMt; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandSim; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtSelectedVersion; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.message.simaka.EapSimTypeData.EapSimTypeDataDecoder; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -/** - * EapSimMethodStateMachine represents the valid paths possible for the EAP-SIM protocol. - * - * <p>EAP-SIM procedures will always follow the path: - * - * Created ---> Start --+--> Challenge --+--> null - * | | - * +--> failed >--+ - * - * Note that EAP-SIM/Notification messages can be received at any point in the above state machine. - * At most one EAP-SIM/Notification message is allowed per EAP-SIM session. - * - * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication Protocol - * Method for Subscriber Identity Modules (EAP-SIM)</a> - */ -class EapSimMethodStateMachine extends EapSimAkaMethodStateMachine { - private final SecureRandom mSecureRandom; - private final EapSimTypeDataDecoder mEapSimTypeDataDecoder; - - EapSimMethodStateMachine( - Context context, - byte[] eapIdentity, - EapSimConfig eapSimConfig, - SecureRandom secureRandom) { - this( - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), - eapIdentity, - eapSimConfig, - secureRandom, - EapSimTypeData.getEapSimTypeDataDecoder()); - } - - @VisibleForTesting - EapSimMethodStateMachine( - TelephonyManager telephonyManager, - byte[] eapIdentity, - EapSimConfig eapSimConfig, - SecureRandom secureRandom, - EapSimTypeDataDecoder eapSimTypeDataDecoder) { - super( - telephonyManager.createForSubscriptionId(eapSimConfig.subId), - eapIdentity, - eapSimConfig); - - if (eapSimTypeDataDecoder == null) { - throw new IllegalArgumentException("EapSimTypeDataDecoder must be non-null"); - } - - this.mSecureRandom = secureRandom; - this.mEapSimTypeDataDecoder = eapSimTypeDataDecoder; - transitionTo(new CreatedState()); - } - - @Override - @EapMethod - int getEapMethod() { - return EAP_TYPE_SIM; - } - - protected class CreatedState extends EapMethodState { - private final String mTAG = CreatedState.class.getSimpleName(); - - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - DecodeResult<EapSimTypeData> decodeResult = - mEapSimTypeDataDecoder.decode(message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - decodeResult.atClientErrorCode); - } - - EapSimTypeData eapSimTypeData = decodeResult.eapTypeData; - switch (eapSimTypeData.eapSubtype) { - case EAP_SIM_START: - break; - case EAP_SIM_NOTIFICATION: - return handleEapSimAkaNotification( - mTAG, - true, // isPreChallengeState - message.eapIdentifier, - eapSimTypeData); - default: - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - byte[] nonce = new byte[AtNonceMt.NONCE_MT_LENGTH]; - mSecureRandom.nextBytes(nonce); - AtNonceMt atNonceMt; - try { - atNonceMt = new AtNonceMt(nonce); - } catch (EapSimAkaInvalidAttributeException ex) { - LOG.wtf(mTAG, "Exception thrown while creating AtNonceMt", ex); - return new EapError(ex); - } - return transitionAndProcess(new StartState(atNonceMt), message); - } - } - - protected class StartState extends EapMethodState { - private final String mTAG = StartState.class.getSimpleName(); - private final AtNonceMt mAtNonceMt; - - private List<Integer> mVersions; - - // use the EAP-Identity for the default value (RFC 4186#7) - @VisibleForTesting byte[] mIdentity = mEapIdentity; - - protected StartState(AtNonceMt atNonceMt) { - this.mAtNonceMt = atNonceMt; - } - - public EapResult process(EapMessage message) { - EapResult result = handleEapSuccessFailureNotification(mTAG, message); - if (result != null) { - return result; - } - - DecodeResult<EapSimTypeData> decodeResult = - mEapSimTypeDataDecoder.decode(message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - decodeResult.atClientErrorCode); - } - - EapSimTypeData eapSimTypeData = decodeResult.eapTypeData; - switch (eapSimTypeData.eapSubtype) { - case EAP_SIM_START: - break; - case EAP_SIM_NOTIFICATION: - return handleEapSimAkaNotification( - mTAG, - true, // isPreChallengeState - message.eapIdentifier, - eapSimTypeData); - case EAP_SIM_CHALLENGE: - // By virtue of being in the StartState, we have received (and processed) the - // EAP-SIM/Start request. Receipt of an EAP-SIM/Challenge request indicates that - // the server has accepted our EAP-SIM/Start response, including our identity - // (if any). - return transitionAndProcess( - new ChallengeState(mVersions, mAtNonceMt, mIdentity), message); - default: - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - if (!isValidStartAttributes(eapSimTypeData)) { - LOG.e(mTAG, "Invalid attributes: " + eapSimTypeData.attributeMap.keySet()); - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - List<EapSimAkaAttribute> responseAttributes = new ArrayList<>(); - responseAttributes.add(mAtNonceMt); - - // choose EAP-SIM version - AtVersionList atVersionList = (AtVersionList) - eapSimTypeData.attributeMap.get(EAP_AT_VERSION_LIST); - mVersions = atVersionList.versions; - if (!mVersions.contains(AtSelectedVersion.SUPPORTED_VERSION)) { - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNSUPPORTED_VERSION); - } - responseAttributes.add(AtSelectedVersion.getSelectedVersion()); - - try { - AtIdentity atIdentity = getIdentityResponse(eapSimTypeData); - if (atIdentity != null) { - responseAttributes.add(atIdentity); - } - } catch (EapSimAkaInvalidAttributeException ex) { - LOG.wtf(mTAG, "Exception thrown while making AtIdentity attribute", ex); - return new EapError(ex); - } catch (EapSimAkaIdentityUnavailableException ex) { - LOG.e(mTAG, "Unable to get IMSI for subId=" + mEapUiccConfig.subId); - return new EapError(ex); - } - - return buildResponseMessage( - EAP_TYPE_SIM, - EAP_SIM_START, - message.eapIdentifier, - responseAttributes); - } - - /** - * Returns true iff the given EapSimTypeData meets the following conditions: - * - contains an AT_VERSION_LIST attribute - * - contains at most one of AT_PERMANENT_ID_REQ, AT_ANY_ID_REQ, or AT_FULLAUTH_D_REQ - * attributes - * - does not contain AT_MAC, AT_IV, or AT_ENCR_DATA attributes - */ - @VisibleForTesting - boolean isValidStartAttributes(EapSimTypeData eapSimTypeData) { - // must contain: version list - Set<Integer> attrs = eapSimTypeData.attributeMap.keySet(); - if (!attrs.contains(EAP_AT_VERSION_LIST)) { - return false; - } - - // may contain: ID request (but only 1) - int idRequests = 0; - if (attrs.contains(EAP_AT_PERMANENT_ID_REQ)) { - idRequests++; - } - if (attrs.contains(EAP_AT_ANY_ID_REQ)) { - idRequests++; - } - if (attrs.contains(EAP_AT_FULLAUTH_ID_REQ)) { - idRequests++; - } - if (idRequests > 1) { - return false; - } - - // can't contain mac, iv, encr data - if (attrs.contains(EAP_AT_MAC) - || attrs.contains(EAP_AT_IV) - || attrs.contains(EAP_AT_ENCR_DATA)) { - return false; - } - return true; - } - - @VisibleForTesting - @Nullable - AtIdentity getIdentityResponse(EapSimTypeData eapSimTypeData) - throws EapSimAkaInvalidAttributeException, EapSimAkaIdentityUnavailableException { - Set<Integer> attributes = eapSimTypeData.attributeMap.keySet(); - - // TODO(b/136180022): process separate ID requests differently (pseudonym vs permanent) - if (attributes.contains(EAP_AT_PERMANENT_ID_REQ) - || attributes.contains(EAP_AT_FULLAUTH_ID_REQ) - || attributes.contains(EAP_AT_ANY_ID_REQ)) { - String imsi = mTelephonyManager.getSubscriberId(); - if (imsi == null) { - throw new EapSimAkaIdentityUnavailableException( - "IMSI for subId (" + mEapUiccConfig.subId + ") not available"); - } - - // Permanent Identity is "1" + IMSI (RFC 4186 Section 4.1.2.6) - String identity = "1" + imsi; - mIdentity = identity.getBytes(StandardCharsets.US_ASCII); - LOG.d(mTAG, "EAP-SIM/Identity=" + LOG.pii(identity)); - - return AtIdentity.getAtIdentity(mIdentity); - } - - return null; - } - } - - protected class ChallengeState extends EapMethodState { - private final String mTAG = ChallengeState.class.getSimpleName(); - private final int mBytesPerShort = 2; - private final int mVersionLenBytes = 2; - - // Lengths defined by TS 31.102 Section 7.1.2.1 (case 3) - // SRES stands for "SIM response" - // Kc stands for "cipher key" - private final int mSresLenBytes = 4; - private final int mKcLenBytes = 8; - - private final List<Integer> mVersions; - private final byte[] mNonce; - @VisibleForTesting final byte[] mIdentity; - - protected ChallengeState(List<Integer> versions, AtNonceMt atNonceMt, byte[] identity) { - mVersions = versions; - mNonce = atNonceMt.nonceMt; - mIdentity = identity; - } - - public EapResult process(EapMessage message) { - if (message.eapCode == EAP_CODE_SUCCESS) { - transitionTo(new FinalState()); - return new EapSuccess(mMsk, mEmsk); - } else if (message.eapCode == EAP_CODE_FAILURE) { - transitionTo(new FinalState()); - return new EapFailure(); - } else if (message.eapData.eapType == EAP_NOTIFICATION) { - return handleEapNotification(mTAG, message); - } - - if (message.eapData.eapType != getEapMethod()) { - return new EapError(new EapInvalidRequestException( - "Expected EAP Type " + getEapMethod() - + ", received " + message.eapData.eapType)); - } - - DecodeResult<EapSimTypeData> decodeResult = - mEapSimTypeDataDecoder.decode(message.eapData.eapTypeData); - if (!decodeResult.isSuccessfulDecode()) { - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - decodeResult.atClientErrorCode); - } - - EapSimTypeData eapSimTypeData = decodeResult.eapTypeData; - switch (eapSimTypeData.eapSubtype) { - case EAP_SIM_NOTIFICATION: - return handleEapSimAkaNotification( - mTAG, - false, // isPreChallengeState - message.eapIdentifier, - eapSimTypeData); - case EAP_SIM_CHALLENGE: - break; - default: - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - if (!isValidChallengeAttributes(eapSimTypeData)) { - LOG.e(mTAG, "Invalid attributes: " + eapSimTypeData.attributeMap.keySet()); - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - List<RandChallengeResult> randChallengeResults; - try { - randChallengeResults = getRandChallengeResults(eapSimTypeData); - } catch (EapSimAkaInvalidLengthException | BufferUnderflowException ex) { - LOG.e(mTAG, "Invalid SRES/Kc tuple returned from SIM", ex); - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } catch (EapSimAkaAuthenticationFailureException ex) { - return new EapError(ex); - } - - try { - MessageDigest sha1 = MessageDigest.getInstance(MASTER_KEY_GENERATION_ALG); - byte[] mkInputData = getMkInputData(randChallengeResults); - generateAndPersistKeys(mTAG, sha1, new Fips186_2Prf(), mkInputData); - } catch (NoSuchAlgorithmException | BufferUnderflowException ex) { - LOG.e(mTAG, "Error while creating keys", ex); - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - - try { - if (!isValidMac(mTAG, message, eapSimTypeData, mNonce)) { - return buildClientErrorResponse( - message.eapIdentifier, - EAP_TYPE_SIM, - AtClientErrorCode.UNABLE_TO_PROCESS); - } - } catch (GeneralSecurityException | EapSilentException - | EapSimAkaInvalidAttributeException ex) { - // if the MAC can't be generated, we can't continue - LOG.e(mTAG, "Error computing MAC for EapMessage", ex); - return new EapError(ex); - } - - ByteBuffer sresValues = - ByteBuffer.allocate(randChallengeResults.size() * mSresLenBytes); - for (RandChallengeResult result : randChallengeResults) { - sresValues.put(result.sres); - } - - // server has been authenticated, so we can send a response - return buildResponseMessageWithMac( - message.eapIdentifier, - EAP_SIM_CHALLENGE, - sresValues.array()); - } - - /** - * Returns true iff the given EapSimTypeData contains both AT_RAND and AT_MAC attributes. - */ - @VisibleForTesting - boolean isValidChallengeAttributes(EapSimTypeData eapSimTypeData) { - Set<Integer> attrs = eapSimTypeData.attributeMap.keySet(); - return attrs.contains(EAP_AT_RAND) && attrs.contains(EAP_AT_MAC); - } - - @VisibleForTesting - List<RandChallengeResult> getRandChallengeResults(EapSimTypeData eapSimTypeData) - throws EapSimAkaInvalidLengthException, EapSimAkaAuthenticationFailureException { - AtRandSim atRand = (AtRandSim) eapSimTypeData.attributeMap.get(EAP_AT_RAND); - List<byte[]> randList = atRand.rands; - List<RandChallengeResult> challengeResults = new ArrayList<>(); - - for (byte[] rand : randList) { - // Rand (pre-Base64 formatting) needs to be formatted as [length][rand data] - ByteBuffer formattedRand = ByteBuffer.allocate(rand.length + 1); - formattedRand.put((byte) rand.length); - formattedRand.put(rand); - - byte[] challengeResponseBytes = - processUiccAuthentication( - mTAG, - TelephonyManager.AUTHTYPE_EAP_SIM, - formattedRand.array()); - - RandChallengeResult randChallengeResult = - getRandChallengeResultFromResponse(challengeResponseBytes); - challengeResults.add(randChallengeResult); - - // Log rand/challenge as PII - LOG.d( - mTAG, - "RAND=" + LOG.pii(rand) - + " SRES=" + LOG.pii(randChallengeResult.sres) - + " Kc=" + LOG.pii(randChallengeResult.kc)); - } - - return challengeResults; - } - - /** - * Parses the SRES and Kc values from the given challengeResponse. The values are returned - * in a Pair<byte[], byte[]>, where SRES and Kc are the first and second values, - * respectively. - */ - @VisibleForTesting - RandChallengeResult getRandChallengeResultFromResponse(byte[] challengeResponse) - throws EapSimAkaInvalidLengthException { - ByteBuffer buffer = ByteBuffer.wrap(challengeResponse); - int lenSres = Byte.toUnsignedInt(buffer.get()); - if (lenSres != mSresLenBytes) { - throw new EapSimAkaInvalidLengthException("Invalid SRES length specified"); - } - byte[] sres = new byte[mSresLenBytes]; - buffer.get(sres); - - int lenKc = Byte.toUnsignedInt(buffer.get()); - if (lenKc != mKcLenBytes) { - throw new EapSimAkaInvalidLengthException("Invalid Kc length specified"); - } - byte[] kc = new byte[mKcLenBytes]; - buffer.get(kc); - - return new RandChallengeResult(sres, kc); - } - - @VisibleForTesting - class RandChallengeResult { - public final byte[] sres; - public final byte[] kc; - - RandChallengeResult(byte[] sres, byte[] kc) throws EapSimAkaInvalidLengthException { - this.sres = sres; - this.kc = kc; - - if (sres.length != mSresLenBytes) { - throw new EapSimAkaInvalidLengthException("Invalid SRES length"); - } - if (kc.length != mKcLenBytes) { - throw new EapSimAkaInvalidLengthException("Invalid Kc length"); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof RandChallengeResult)) return false; - RandChallengeResult that = (RandChallengeResult) o; - return Arrays.equals(sres, that.sres) - && Arrays.equals(kc, that.kc); - } - - @Override - public int hashCode() { - int result = Arrays.hashCode(sres); - result = 31 * result + Arrays.hashCode(kc); - return result; - } - } - - private byte[] getMkInputData(List<RandChallengeResult> randChallengeResults) { - int numInputBytes = - mIdentity.length - + (randChallengeResults.size() * mKcLenBytes) - + mNonce.length - + (mVersions.size() * mBytesPerShort) // 2B per version - + mVersionLenBytes; - - ByteBuffer mkInputBuffer = ByteBuffer.allocate(numInputBytes); - mkInputBuffer.put(mIdentity); - for (RandChallengeResult randChallengeResult : randChallengeResults) { - mkInputBuffer.put(randChallengeResult.kc); - } - mkInputBuffer.put(mNonce); - for (int i : mVersions) { - mkInputBuffer.putShort((short) i); - } - mkInputBuffer.putShort((short) AtSelectedVersion.SUPPORTED_VERSION); - return mkInputBuffer.array(); - } - } - - EapSimTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode) { - return new EapSimTypeData(EAP_SIM_CLIENT_ERROR, Arrays.asList(clientErrorCode)); - } - - EapSimTypeData getEapSimAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { - return new EapSimTypeData(eapSubtype, attributes); - } -} diff --git a/src/java/com/android/internal/net/eap/statemachine/EapStateMachine.java b/src/java/com/android/internal/net/eap/statemachine/EapStateMachine.java deleted file mode 100644 index f3e933e2..00000000 --- a/src/java/com/android/internal/net/eap/statemachine/EapStateMachine.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapAuthenticator.LOG; -import static com.android.internal.net.eap.message.EapData.EAP_IDENTITY; -import static com.android.internal.net.eap.message.EapData.EAP_NAK; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_STRING; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_RESPONSE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_STRING; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -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 com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.EapSilentException; -import com.android.internal.net.eap.exceptions.UnsupportedEapTypeException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapData.EapMethod; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.utils.SimpleStateMachine; - -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; - -/** - * EapStateMachine represents the valid paths for a single EAP Authentication procedure. - * - * <p>EAP Authentication procedures will always follow the path: - * - * CreatedState --> IdentityState --> Method State --+--> SuccessState - * | ^ | - * +---------------------------------+ +--> FailureState - * - */ -public class EapStateMachine extends SimpleStateMachine<byte[], EapResult> { - private static final String TAG = EapStateMachine.class.getSimpleName(); - - private final Context mContext; - private final EapSessionConfig mEapSessionConfig; - private final SecureRandom mSecureRandom; - - public EapStateMachine( - @NonNull Context context, - @NonNull EapSessionConfig eapSessionConfig, - @NonNull SecureRandom secureRandom) { - this.mContext = context; - this.mEapSessionConfig = eapSessionConfig; - this.mSecureRandom = secureRandom; - - LOG.d( - TAG, - "Starting EapStateMachine with EAP-Identity=" - + LOG.pii(eapSessionConfig.eapIdentity) - + " and configs=" + eapSessionConfig.eapConfigs.keySet()); - - transitionTo(new CreatedState()); - } - - @VisibleForTesting - protected SimpleStateMachine.SimpleState getState() { - return mState; - } - - @VisibleForTesting - protected void transitionTo(EapState newState) { - LOG.d( - TAG, - "Transitioning from " + mState.getClass().getSimpleName() - + " to " + newState.getClass().getSimpleName()); - super.transitionTo(newState); - } - - @VisibleForTesting - protected EapResult transitionAndProcess(EapState newState, byte[] packet) { - return super.transitionAndProcess(newState, packet); - } - - protected abstract class EapState extends SimpleState { - protected DecodeResult decode(@NonNull byte[] packet) { - LOG.d(getClass().getSimpleName(), - "Received packet=[" + LOG.pii(packet) + "]"); - - if (packet == null) { - return new DecodeResult(new EapError( - new IllegalArgumentException("Attempting to decode null packet"))); - } - - try { - EapMessage eapMessage = EapMessage.decode(packet); - - // Log inbound message in the format "EAP-<Code>/<Type>" - String eapDataString = - (eapMessage.eapData == null) - ? "" - : "/" + EAP_TYPE_STRING.getOrDefault( - eapMessage.eapData.eapType, - "UNKNOWN (" + eapMessage.eapData.eapType + ")"); - String msg = "Decoded message: EAP-" - + EAP_CODE_STRING.getOrDefault(eapMessage.eapCode, "UNKNOWN") - + eapDataString; - LOG.i(getClass().getSimpleName(), msg); - - if (eapMessage.eapCode == EAP_CODE_RESPONSE) { - EapInvalidRequestException cause = - new EapInvalidRequestException("Received an EAP-Response message"); - return new DecodeResult(new EapError(cause)); - } else if (eapMessage.eapCode == EAP_CODE_REQUEST - && eapMessage.eapData.eapType == EAP_NAK) { - // RFC 3748 Section 5.3.1 states that Nak type is only valid in responses - EapInvalidRequestException cause = - new EapInvalidRequestException("Received an EAP-Request of type Nak"); - return new DecodeResult(new EapError(cause)); - } - - return new DecodeResult(eapMessage); - } catch (UnsupportedEapTypeException ex) { - return new DecodeResult( - EapMessage.getNakResponse( - ex.eapIdentifier, - mEapSessionConfig.eapConfigs.keySet())); - } catch (EapSilentException ex) { - return new DecodeResult(new EapError(ex)); - } - } - - protected final class DecodeResult { - public final EapMessage eapMessage; - public final EapResult eapResult; - - public DecodeResult(EapMessage eapMessage) { - this.eapMessage = eapMessage; - this.eapResult = null; - } - - public DecodeResult(EapResult eapResult) { - this.eapMessage = null; - this.eapResult = eapResult; - } - - public boolean isValidEapMessage() { - return eapMessage != null; - } - } - } - - protected class CreatedState extends EapState { - private final String mTAG = CreatedState.class.getSimpleName(); - - public EapResult process(@NonNull byte[] packet) { - DecodeResult decodeResult = decode(packet); - if (!decodeResult.isValidEapMessage()) { - return decodeResult.eapResult; - } - EapMessage message = decodeResult.eapMessage; - - if (message.eapCode != EAP_CODE_REQUEST) { - return new EapError( - new EapInvalidRequestException("Received non EAP-Request in CreatedState")); - } - - // EapMessage#validate verifies that all EapMessage objects representing - // EAP-Request packets have a Type value - switch (message.eapData.eapType) { - case EAP_NOTIFICATION: - return handleNotification(mTAG, message); - - case EAP_IDENTITY: - return transitionAndProcess(new IdentityState(), packet); - - // all EAP methods should be handled by MethodState - default: - return transitionAndProcess(new MethodState(), packet); - } - } - } - - protected class IdentityState extends EapState { - private final String mTAG = IdentityState.class.getSimpleName(); - - public EapResult process(@NonNull byte[] packet) { - DecodeResult decodeResult = decode(packet); - if (!decodeResult.isValidEapMessage()) { - return decodeResult.eapResult; - } - EapMessage message = decodeResult.eapMessage; - - if (message.eapCode != EAP_CODE_REQUEST) { - return new EapError(new EapInvalidRequestException( - "Received non EAP-Request in IdentityState")); - } - - // EapMessage#validate verifies that all EapMessage objects representing - // EAP-Request packets have a Type value - switch (message.eapData.eapType) { - case EAP_NOTIFICATION: - return handleNotification(mTAG, message); - - case EAP_IDENTITY: - return getIdentityResponse(message.eapIdentifier); - - // all EAP methods should be handled by MethodState - default: - return transitionAndProcess(new MethodState(), packet); - } - } - - @VisibleForTesting - EapResult getIdentityResponse(int eapIdentifier) { - try { - LOG.d(mTAG, "Returning EAP-Identity: " + LOG.pii(mEapSessionConfig.eapIdentity)); - EapData identityData = new EapData(EAP_IDENTITY, mEapSessionConfig.eapIdentity); - return EapResponse.getEapResponse( - new EapMessage(EAP_CODE_RESPONSE, eapIdentifier, identityData)); - } catch (EapSilentException ex) { - // this should never happen - only identifier and identity bytes are variable - LOG.wtf(mTAG, "Failed to create Identity response for message with identifier=" - + LOG.pii(eapIdentifier)); - return new EapError(ex); - } - } - } - - protected class MethodState extends EapState { - private final String mTAG = MethodState.class.getSimpleName(); - - @VisibleForTesting - EapMethodStateMachine mEapMethodStateMachine; - - // Not all EAP Method implementations may support EAP-Notifications, so allow the EAP-Method - // to handle any EAP-REQUEST/Notification messages (RFC 3748 Section 5.2) - public EapResult process(@NonNull byte[] packet) { - DecodeResult decodeResult = decode(packet); - if (!decodeResult.isValidEapMessage()) { - return decodeResult.eapResult; - } - EapMessage eapMessage = decodeResult.eapMessage; - - if (mEapMethodStateMachine == null) { - if (eapMessage.eapCode == EAP_CODE_SUCCESS) { - // EAP-SUCCESS is required to be the last EAP message sent during the EAP - // protocol, so receiving a premature SUCCESS message is an unrecoverable error - return new EapError( - new EapInvalidRequestException( - "Received an EAP-Success in the MethodState")); - } else if (eapMessage.eapCode == EAP_CODE_FAILURE) { - transitionTo(new FailureState()); - return new EapFailure(); - } else if (eapMessage.eapData.eapType == EAP_NOTIFICATION) { - // if no EapMethodStateMachine has been assigned and we receive an - // EAP-Notification, we should log it and respond - return handleNotification(mTAG, eapMessage); - } - - int eapType = eapMessage.eapData.eapType; - mEapMethodStateMachine = buildEapMethodStateMachine(eapType); - - if (mEapMethodStateMachine == null) { - return EapMessage.getNakResponse( - eapMessage.eapIdentifier, - mEapSessionConfig.eapConfigs.keySet()); - } - } - - EapResult result = mEapMethodStateMachine.process(decodeResult.eapMessage); - if (result instanceof EapSuccess) { - transitionTo(new SuccessState()); - } else if (result instanceof EapFailure) { - transitionTo(new FailureState()); - } - return result; - } - - @Nullable - private EapMethodStateMachine buildEapMethodStateMachine(@EapMethod int eapType) { - EapMethodConfig eapMethodConfig = mEapSessionConfig.eapConfigs.get(eapType); - if (eapMethodConfig == null) { - LOG.e( - mTAG, - "No configs provided for method: " - + EAP_TYPE_STRING.getOrDefault( - eapType, "Unknown (" + eapType + ")")); - return null; - } - - switch (eapType) { - case EAP_TYPE_SIM: - EapSimConfig eapSimConfig = (EapSimConfig) eapMethodConfig; - return new EapSimMethodStateMachine( - mContext, mEapSessionConfig.eapIdentity, eapSimConfig, mSecureRandom); - case EAP_TYPE_AKA: - EapAkaConfig eapAkaConfig = (EapAkaConfig) eapMethodConfig; - boolean supportsEapAkaPrime = - mEapSessionConfig.eapConfigs.containsKey(EAP_TYPE_AKA_PRIME); - return new EapAkaMethodStateMachine( - mContext, - mEapSessionConfig.eapIdentity, - eapAkaConfig, - supportsEapAkaPrime); - case EAP_TYPE_AKA_PRIME: - EapAkaPrimeConfig eapAkaPrimeConfig = (EapAkaPrimeConfig) eapMethodConfig; - return new EapAkaPrimeMethodStateMachine( - mContext, mEapSessionConfig.eapIdentity, eapAkaPrimeConfig); - case EAP_TYPE_MSCHAP_V2: - EapMsChapV2Config eapMsChapV2Config = (EapMsChapV2Config) eapMethodConfig; - return new EapMsChapV2MethodStateMachine(eapMsChapV2Config, mSecureRandom); - default: - // received unsupported EAP Type. This should never happen. - LOG.e(mTAG, "Received unsupported EAP Type=" + eapType); - throw new IllegalArgumentException( - "Received unsupported EAP Type in MethodState constructor"); - } - } - } - - protected class SuccessState extends EapState { - public EapResult process(byte[] packet) { - return new EapError(new EapInvalidRequestException( - "Not possible to process messages in Success State")); - } - } - - protected class FailureState extends EapState { - public EapResult process(byte[] message) { - return new EapError(new EapInvalidRequestException( - "Not possible to process messages in Failure State")); - } - } - - protected static EapResult handleNotification(String tag, EapMessage message) { - // Type-Data will be UTF-8 encoded ISO 10646 characters (RFC 3748 Section 5.2) - String content = new String(message.eapData.eapTypeData, StandardCharsets.UTF_8); - LOG.i(tag, "Received EAP-Request/Notification: [" + content + "]"); - return EapMessage.getNotificationResponse(message.eapIdentifier); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java deleted file mode 100644 index 82122847..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static android.net.ipsec.ike.IkeManager.getIkeLog; - -import android.os.Looper; -import android.os.Message; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import java.util.concurrent.TimeUnit; - -/** - * This class represents the common information of both IkeSessionStateMachine and - * ChildSessionStateMachine - */ -abstract class AbstractSessionStateMachine extends StateMachine { - private static final int CMD_SHARED_BASE = 0; - protected static final int CMD_CATEGORY_SIZE = 100; - - /** - * Commands of Child local request that will be used in both IkeSessionStateMachine and - * ChildSessionStateMachine. - */ - protected static final int CMD_CHILD_LOCAL_REQUEST_BASE = CMD_SHARED_BASE; - - @VisibleForTesting - static final int CMD_LOCAL_REQUEST_CREATE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 1; - - @VisibleForTesting - static final int CMD_LOCAL_REQUEST_DELETE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 2; - - @VisibleForTesting - static final int CMD_LOCAL_REQUEST_REKEY_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 3; - - /** Timeout commands. */ - protected static final int CMD_TIMEOUT_BASE = CMD_SHARED_BASE + CMD_CATEGORY_SIZE; - /** Timeout when the remote side fails to send a Rekey-Delete request. */ - @VisibleForTesting static final int TIMEOUT_REKEY_REMOTE_DELETE = CMD_TIMEOUT_BASE + 1; - - /** Commands for testing only */ - protected static final int CMD_TEST_BASE = CMD_SHARED_BASE + 2 * CMD_CATEGORY_SIZE; - /** Force state machine to a target state for testing purposes. */ - @VisibleForTesting static final int CMD_FORCE_TRANSITION = CMD_TEST_BASE + 1; - - /** Private commands for subclasses */ - protected static final int CMD_PRIVATE_BASE = CMD_SHARED_BASE + 3 * CMD_CATEGORY_SIZE; - - protected static final SparseArray<String> SHARED_CMD_TO_STR; - - static { - SHARED_CMD_TO_STR = new SparseArray<>(); - SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_CREATE_CHILD, "Create Child"); - SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_DELETE_CHILD, "Delete Child"); - SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD, "Rekey Child"); - SHARED_CMD_TO_STR.put(TIMEOUT_REKEY_REMOTE_DELETE, "Timout rekey remote delete"); - SHARED_CMD_TO_STR.put(CMD_FORCE_TRANSITION, "Force transition"); - } - - // Use a value greater than the retransmit-failure timeout. - static final long REKEY_DELETE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(180L); - - private final String mLogTag; - - protected AbstractSessionStateMachine(String name, Looper looper) { - super(name, looper); - mLogTag = name; - } - - /** - * Top level state for handling uncaught exceptions for all subclasses. - * - * <p>All other state in SessionStateMachine MUST extend this state. - * - * <p>Only errors this state should catch are unexpected internal failures. Since this may be - * run in critical processes, it must never take down the process if it fails - */ - protected abstract class ExceptionHandlerBase extends State { - @Override - public final void enter() { - try { - enterState(); - } catch (RuntimeException e) { - cleanUpAndQuit(e); - } - } - - @Override - public final boolean processMessage(Message message) { - try { - String cmdName = SHARED_CMD_TO_STR.get(message.what); - if (cmdName == null) { - cmdName = getCmdString(message.what); - } - - // Unrecognized message will be logged by super class(Android StateMachine) - if (cmdName != null) logd("processStateMessage: " + cmdName); - - return processStateMessage(message); - } catch (RuntimeException e) { - cleanUpAndQuit(e); - return HANDLED; - } - } - - @Override - public final void exit() { - try { - exitState(); - } catch (RuntimeException e) { - cleanUpAndQuit(e); - } - } - - protected void enterState() { - // Do nothing. Subclasses MUST override it if they care. - } - - protected boolean processStateMessage(Message message) { - return NOT_HANDLED; - } - - protected void exitState() { - // Do nothing. Subclasses MUST override it if they care. - } - - protected abstract void cleanUpAndQuit(RuntimeException e); - - protected abstract String getCmdString(int cmd); - } - - @Override - protected void log(String s) { - getIkeLog().d(mLogTag, s); - } - - @Override - protected void logd(String s) { - getIkeLog().d(mLogTag, s); - } - - protected void logd(String s, Throwable e) { - getIkeLog().d(mLogTag, s, e); - } - - @Override - protected void logv(String s) { - getIkeLog().v(mLogTag, s); - } - - @Override - protected void logi(String s) { - getIkeLog().i(mLogTag, s); - } - - protected void logi(String s, Throwable cause) { - getIkeLog().i(mLogTag, s, cause); - } - - @Override - protected void logw(String s) { - getIkeLog().w(mLogTag, s); - } - - @Override - protected void loge(String s) { - getIkeLog().e(mLogTag, s); - } - - @Override - protected void loge(String s, Throwable e) { - getIkeLog().e(mLogTag, s, e); - } - - protected void logWtf(String s) { - getIkeLog().wtf(mLogTag, s); - } - - protected void logWtf(String s, Throwable e) { - getIkeLog().wtf(mLogTag, s, e); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java deleted file mode 100644 index 889d8c12..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java +++ /dev/null @@ -1,2268 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static android.net.ipsec.ike.IkeManager.getIkeLog; -import static android.net.ipsec.ike.SaProposal.DH_GROUP_NONE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; - -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_IKE_AUTH; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.ExchangeType; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_KE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NONCE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PROTOCOL_ID_ESP; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.ResourceUnavailableException; -import android.net.IpSecManager.SecurityParameterIndex; -import android.net.IpSecManager.SpiUnavailableException; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.ipsec.ike.ChildSaProposal; -import android.net.ipsec.ike.ChildSessionCallback; -import android.net.ipsec.ike.ChildSessionConfiguration; -import android.net.ipsec.ike.ChildSessionOptions; -import android.net.ipsec.ike.IkeTrafficSelector; -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.TunnelModeChildSessionOptions; -import android.net.ipsec.ike.exceptions.IkeException; -import android.net.ipsec.ike.exceptions.IkeInternalException; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.os.Looper; -import android.os.Message; -import android.util.Pair; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IkeExchangeSubType; -import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecord; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.exceptions.TemporaryFailureException; -import com.android.internal.net.ipsec.ike.exceptions.TsUnacceptableException; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; -import com.android.internal.net.ipsec.ike.message.IkeDeletePayload; -import com.android.internal.net.ipsec.ike.message.IkeKePayload; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeNoncePayload; -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NotifyType; -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.ChildProposal; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeTsPayload; -import com.android.internal.util.State; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.InetAddress; -import java.security.GeneralSecurityException; -import java.security.Provider; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -/** - * ChildSessionStateMachine tracks states and manages exchanges of this Child Session. - * - * <p>ChildSessionStateMachine has two types of states. One type are states where there is no - * ongoing procedure affecting Child Session (non-procedure state), including Initial, Idle and - * Receiving. All other states are "procedure" states which are named as follows: - * - * <pre> - * State Name = [Procedure Type] + [Exchange Initiator] + [Exchange Type]. - * - An IKE procedure consists of one or two IKE exchanges: - * Procedure Type = {CreateChild | DeleteChild | Info | RekeyChild | SimulRekeyChild}. - * - Exchange Initiator indicates whether local or remote peer is the exchange initiator: - * Exchange Initiator = {Local | Remote} - * - Exchange type defines the function of this exchange. - * Exchange Type = {Create | Delete} - * </pre> - */ -public class ChildSessionStateMachine extends AbstractSessionStateMachine { - private static final String TAG = "ChildSessionStateMachine"; - - private static final int SPI_NOT_REGISTERED = 0; - - // Time after which Child SA needs to be rekeyed - @VisibleForTesting static final long SA_SOFT_LIFETIME_MS = TimeUnit.HOURS.toMillis(2L); - - private static final int CMD_GENERAL_BASE = CMD_PRIVATE_BASE; - - /** Receive request for negotiating first Child SA. */ - private static final int CMD_HANDLE_FIRST_CHILD_EXCHANGE = CMD_GENERAL_BASE + 1; - /** Receive a request from the remote. */ - private static final int CMD_HANDLE_RECEIVED_REQUEST = CMD_GENERAL_BASE + 2; - /** Receive a reponse from the remote. */ - private static final int CMD_HANDLE_RECEIVED_RESPONSE = CMD_GENERAL_BASE + 3; - /** Kill Session and close all alive Child SAs immediately. */ - private static final int CMD_KILL_SESSION = CMD_GENERAL_BASE + 4; - - private static final SparseArray<String> CMD_TO_STR; - - static { - CMD_TO_STR = new SparseArray<>(); - CMD_TO_STR.put(CMD_HANDLE_FIRST_CHILD_EXCHANGE, "Handle First Child"); - CMD_TO_STR.put(CMD_HANDLE_RECEIVED_REQUEST, "Rcv request"); - CMD_TO_STR.put(CMD_HANDLE_RECEIVED_RESPONSE, "Rcv response"); - CMD_TO_STR.put(CMD_KILL_SESSION, "Kill session"); - } - - private final Context mContext; - private final IpSecManager mIpSecManager; - - /** User provided configurations. */ - private final ChildSessionOptions mChildSessionOptions; - - private final Executor mUserCbExecutor; - private final ChildSessionCallback mUserCallback; - - /** Callback to notify IKE Session the state changes. */ - private final IChildSessionSmCallback mChildSmCallback; - - // TODO: Also store ChildSessionCallback for notifying users. - - /** Local address assigned on device. */ - @VisibleForTesting InetAddress mLocalAddress; - /** Remote address configured by users. */ - @VisibleForTesting InetAddress mRemoteAddress; - - /** - * UDP-Encapsulated socket that allows IPsec traffic to pass through a NAT. Null if UDP - * encapsulation is not needed. - */ - @VisibleForTesting @Nullable UdpEncapsulationSocket mUdpEncapSocket; - - /** Crypto parameters. Updated upon initial negotiation or IKE SA rekey. */ - @VisibleForTesting IkeMacPrf mIkePrf; - - @VisibleForTesting byte[] mSkD; - - /** Package private ChildSaProposal that represents the negotiated Child SA proposal. */ - @VisibleForTesting ChildSaProposal mSaProposal; - - /** Negotiated local Traffic Selector. */ - @VisibleForTesting IkeTrafficSelector[] mLocalTs; - /** Negotiated remote Traffic Selector. */ - @VisibleForTesting IkeTrafficSelector[] mRemoteTs; - - @VisibleForTesting IkeCipher mChildCipher; - @VisibleForTesting IkeMacIntegrity mChildIntegrity; - - /** Package private */ - @VisibleForTesting ChildSaRecord mCurrentChildSaRecord; - /** Package private */ - @VisibleForTesting ChildSaRecord mLocalInitNewChildSaRecord; - /** Package private */ - @VisibleForTesting ChildSaRecord mRemoteInitNewChildSaRecord; - - /** Package private */ - @VisibleForTesting ChildSaRecord mChildSaRecordSurviving; - - @VisibleForTesting final State mKillChildSessionParent = new KillChildSessionParent(); - - @VisibleForTesting final State mInitial = new Initial(); - @VisibleForTesting final State mCreateChildLocalCreate = new CreateChildLocalCreate(); - @VisibleForTesting final State mIdle = new Idle(); - @VisibleForTesting final State mDeleteChildLocalDelete = new DeleteChildLocalDelete(); - @VisibleForTesting final State mDeleteChildRemoteDelete = new DeleteChildRemoteDelete(); - @VisibleForTesting final State mRekeyChildLocalCreate = new RekeyChildLocalCreate(); - @VisibleForTesting final State mRekeyChildRemoteCreate = new RekeyChildRemoteCreate(); - @VisibleForTesting final State mRekeyChildLocalDelete = new RekeyChildLocalDelete(); - @VisibleForTesting final State mRekeyChildRemoteDelete = new RekeyChildRemoteDelete(); - - /** - * Builds a new uninitialized ChildSessionStateMachine - * - * <p>Upon creation, this state machine will await either the handleFirstChildExchange - * (IKE_AUTH), or the createChildSession (Additional child creation beyond the first child) to - * be called, both of which must pass keying and SA information. - * - * <p>This two-stage initialization is required to allow race-free user interaction with the IKE - * Session keyed on the child state machine callbacks. - * - * <p>Package private - */ - ChildSessionStateMachine( - Looper looper, - Context context, - IpSecManager ipSecManager, - ChildSessionOptions sessionOptions, - Executor userCbExecutor, - ChildSessionCallback userCallback, - IChildSessionSmCallback childSmCallback) { - super(TAG, looper); - - mContext = context; - mIpSecManager = ipSecManager; - mChildSessionOptions = sessionOptions; - - mUserCbExecutor = userCbExecutor; - mUserCallback = userCallback; - mChildSmCallback = childSmCallback; - - addState(mKillChildSessionParent); - - addState(mInitial, mKillChildSessionParent); - addState(mCreateChildLocalCreate, mKillChildSessionParent); - addState(mIdle, mKillChildSessionParent); - addState(mDeleteChildLocalDelete, mKillChildSessionParent); - addState(mDeleteChildRemoteDelete, mKillChildSessionParent); - addState(mRekeyChildLocalCreate, mKillChildSessionParent); - addState(mRekeyChildRemoteCreate, mKillChildSessionParent); - addState(mRekeyChildLocalDelete, mKillChildSessionParent); - addState(mRekeyChildRemoteDelete, mKillChildSessionParent); - - setInitialState(mInitial); - } - - /** - * Interface for ChildSessionStateMachine to notify IkeSessionStateMachine of state changes. - * - * <p>Child Session may encounter an IKE Session fatal error in three cases with different - * handling rules: - * - * <pre> - * - When there is a fatal error in an inbound request, onOutboundPayloadsReady will be - * called first to send out an error notification and then onFatalIkeSessionError(false) - * will be called to locally close the IKE Session. - * - When there is a fatal error in an inbound response, only onFatalIkeSessionError(true) - * will be called to notify the remote with a Delete request and then close the IKE Session. - * - When there is an fatal error notification in an inbound response, only - * onFatalIkeSessionError(false) is called to close the IKE Session locally. - * </pre> - * - * <p>Package private. - */ - interface IChildSessionSmCallback { - /** Notify that new Child SA is created. */ - void onChildSaCreated(int remoteSpi, ChildSessionStateMachine childSession); - - /** Notify that a Child SA is deleted. */ - void onChildSaDeleted(int remoteSpi); - - /** Schedule a future Child Rekey Request on the LocalRequestScheduler. */ - void scheduleLocalRequest(ChildLocalRequest futureRequest, long delayedTime); - - /** Schedule retry for a Child Rekey Request on the LocalRequestScheduler. */ - void scheduleRetryLocalRequest(ChildLocalRequest futureRequest); - - /** Notify the IKE Session to send out IKE message for this Child Session. */ - void onOutboundPayloadsReady( - @ExchangeType int exchangeType, - boolean isResp, - List<IkePayload> payloadList, - ChildSessionStateMachine childSession); - - /** Notify that a Child procedure has been finished. */ - void onProcedureFinished(ChildSessionStateMachine childSession); - - /** - * Notify the IKE Session State Machine that this Child has been fully shut down. - * - * <p>This method MUST be called after the user callbacks have been fired, and MUST always - * be called before the state machine can shut down. - */ - void onChildSessionClosed(ChildSessionCallback userCallbacks); - - /** - * Notify that a Child procedure has been finished and the IKE Session should close itself - * because of a fatal error. - * - * <p>The IKE Session should send a Delete IKE request before closing when needsNotifyRemote - * is true. - */ - void onFatalIkeSessionError(boolean needsNotifyRemote); - } - - /** - * Receive requesting and responding payloads for negotiating first Child SA. - * - * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call - * as an asynchronous job to the ChildStateMachine handler. - * - * @param reqPayloads SA negotiation related payloads in IKE_AUTH request. - * @param respPayloads SA negotiation related payloads in IKE_AUTH response. - * @param localAddress The local (outer) address of the Child Session. - * @param remoteAddress The remote (outer) address of the Child Session. - * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed. - * @param ikePrf The pseudo-random function to use for key derivation - * @param skD The key for which to derive new keying information from. - */ - public void handleFirstChildExchange( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - InetAddress localAddress, - InetAddress remoteAddress, - UdpEncapsulationSocket udpEncapSocket, - IkeMacPrf ikePrf, - byte[] skD) { - - this.mLocalAddress = localAddress; - this.mRemoteAddress = remoteAddress; - this.mUdpEncapSocket = udpEncapSocket; - this.mIkePrf = ikePrf; - this.mSkD = skD; - - int spi = registerProvisionalChildAndGetSpi(respPayloads); - sendMessage( - CMD_HANDLE_FIRST_CHILD_EXCHANGE, - new FirstChildNegotiationData(reqPayloads, respPayloads, spi)); - } - - /** - * Initiate Create Child procedure. - * - * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call - * as an asynchronous job to the ChildStateMachine handler. - * - * @param localAddress The local (outer) address from which traffic will originate. - * @param remoteAddress The remote (outer) address to which traffic will be sent. - * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed. - * @param ikePrf The pseudo-random function to use for key derivation - * @param skD The key for which to derive new keying information from. - */ - public void createChildSession( - InetAddress localAddress, - InetAddress remoteAddress, - UdpEncapsulationSocket udpEncapSocket, - IkeMacPrf ikePrf, - byte[] skD) { - this.mLocalAddress = localAddress; - this.mRemoteAddress = remoteAddress; - this.mUdpEncapSocket = udpEncapSocket; - this.mIkePrf = ikePrf; - this.mSkD = skD; - - sendMessage(CMD_LOCAL_REQUEST_CREATE_CHILD); - } - - /** - * Initiate Delete Child procedure. - * - * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call - * as an asynchronous job to the ChildStateMachine handler. - */ - public void deleteChildSession() { - sendMessage(CMD_LOCAL_REQUEST_DELETE_CHILD); - } - - /** - * Initiate Rekey Child procedure. - * - * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call - * as an asynchronous job to the ChildStateMachine handler. - */ - public void rekeyChildSession() { - sendMessage(CMD_LOCAL_REQUEST_REKEY_CHILD); - } - - /** - * Kill Child Session and all alive Child SAs without doing IKE exchange. - * - * <p>It is usually called when IKE Session is being closed. - */ - public void killSession() { - sendMessage(CMD_KILL_SESSION); - } - - private ChildLocalRequest makeRekeyLocalRequest() { - return new ChildLocalRequest( - CMD_LOCAL_REQUEST_REKEY_CHILD, mUserCallback, null /*childOptions*/); - } - - private long getRekeyTimeout() { - // TODO: Make rekey timout fuzzy - return SA_SOFT_LIFETIME_MS; - } - - /** - * Receive a request - * - * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call - * as an asynchronous job to the ChildStateMachine handler. - * - * @param exchangeSubtype the exchange subtype of this inbound request. - * @param exchangeType the exchange type in the request message. - * @param payloadList the Child-procedure-related payload list in the request message that needs - * validation. - */ - public void receiveRequest( - @IkeExchangeSubType int exchangeSubtype, - @ExchangeType int exchangeType, - List<IkePayload> payloadList) { - sendMessage( - CMD_HANDLE_RECEIVED_REQUEST, - new ReceivedRequest(exchangeSubtype, exchangeType, payloadList)); - } - - /** - * Receive a response. - * - * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call - * as an asynchronous job to the ChildStateMachine handler. - * - * @param exchangeType the exchange type in the response message that needs validation. - * @param payloadList the Child-procedure-related payload list in the response message that - * needs validation. - */ - public void receiveResponse(@ExchangeType int exchangeType, List<IkePayload> payloadList) { - if (!isAwaitingCreateResp()) { - sendMessage( - CMD_HANDLE_RECEIVED_RESPONSE, new ReceivedResponse(exchangeType, payloadList)); - } - - // If we are waiting for a Create/RekeyCreate response and the received message contains SA - // payload we need to register for this provisional Child. - int spi = registerProvisionalChildAndGetSpi(payloadList); - sendMessage( - CMD_HANDLE_RECEIVED_RESPONSE, - new ReceivedCreateResponse(exchangeType, payloadList, spi)); - } - - private boolean isAwaitingCreateResp() { - return (getCurrentState() == mCreateChildLocalCreate - || getCurrentState() == mRekeyChildLocalCreate); - } - - /** - * Update SK_d with provided value when IKE SA is rekeyed. - * - * <p>It MUST be only called at the end of Rekey IKE procedure, which guarantees this Child - * Session is not in Create Child or Rekey Child procedure. - * - * @param skD the new skD in byte array. - */ - public void setSkD(byte[] skD) { - mSkD = skD; - } - - /** - * Register provisioning ChildSessionStateMachine in IChildSessionSmCallback - * - * <p>This method is for avoiding CHILD_SA_NOT_FOUND error in IkeSessionStateMachine when remote - * peer sends request for delete/rekey this Child SA before ChildSessionStateMachine sends - * FirstChildNegotiationData or Create response to itself. - */ - private int registerProvisionalChildAndGetSpi(List<IkePayload> respPayloads) { - IkeSaPayload saPayload = - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_SA, IkeSaPayload.class, respPayloads); - - if (saPayload == null) return SPI_NOT_REGISTERED; - - // IkeSaPayload.Proposal stores SPI in long type so as to be applied to both 8-byte IKE SPI - // and 4-byte Child SPI. Here we cast the stored SPI to int to represent a Child SPI. - int remoteGenSpi = (int) (saPayload.proposalList.get(0).spi); - mChildSmCallback.onChildSaCreated(remoteGenSpi, this); - return remoteGenSpi; - } - - private void replyErrorNotification(@NotifyType int notifyType) { - replyErrorNotification(notifyType, new byte[0]); - } - - private void replyErrorNotification(@NotifyType int notifyType, byte[] notifyData) { - List<IkePayload> outPayloads = new ArrayList<>(1); - IkeNotifyPayload notifyPayload = new IkeNotifyPayload(notifyType, notifyData); - outPayloads.add(notifyPayload); - - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_INFORMATIONAL, true /*isResp*/, outPayloads, this); - } - - /** Notify users the deletion of a Child SA. MUST be called through mUserCbExecutor */ - private void onIpSecTransformPairDeleted(ChildSaRecord childSaRecord) { - mUserCallback.onIpSecTransformDeleted( - childSaRecord.getOutboundIpSecTransform(), IpSecManager.DIRECTION_OUT); - mUserCallback.onIpSecTransformDeleted( - childSaRecord.getInboundIpSecTransform(), IpSecManager.DIRECTION_IN); - } - - /** - * ReceivedRequest contains exchange subtype and payloads that are extracted from a request - * message to the current Child procedure. - */ - private static class ReceivedRequest { - @IkeExchangeSubType public final int exchangeSubtype; - @ExchangeType public final int exchangeType; - public final List<IkePayload> requestPayloads; - - ReceivedRequest( - @IkeExchangeSubType int eSubtype, - @ExchangeType int eType, - List<IkePayload> reqPayloads) { - exchangeSubtype = eSubtype; - exchangeType = eType; - requestPayloads = reqPayloads; - } - } - - /** - * ReceivedResponse contains exchange type and payloads that are extracted from a response - * message to the current Child procedure. - */ - private static class ReceivedResponse { - @ExchangeType public final int exchangeType; - public final List<IkePayload> responsePayloads; - - ReceivedResponse(@ExchangeType int eType, List<IkePayload> respPayloads) { - exchangeType = eType; - responsePayloads = respPayloads; - } - } - - private static class ReceivedCreateResponse extends ReceivedResponse { - public final int registeredSpi; - - ReceivedCreateResponse(@ExchangeType int eType, List<IkePayload> respPayloads, int spi) { - super(eType, respPayloads); - registeredSpi = spi; - } - } - - /** - * FirstChildNegotiationData contains payloads for negotiating first Child SA in IKE_AUTH - * request and IKE_AUTH response and callback to notify IkeSessionStateMachine the SA - * negotiation result. - */ - private static class FirstChildNegotiationData extends ReceivedCreateResponse { - public final List<IkePayload> requestPayloads; - - FirstChildNegotiationData( - List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int spi) { - super(EXCHANGE_TYPE_IKE_AUTH, respPayloads, spi); - requestPayloads = reqPayloads; - } - } - - /** Top level state for handling uncaught exceptions for all subclasses. */ - abstract class ExceptionHandler extends ExceptionHandlerBase { - @Override - protected void cleanUpAndQuit(RuntimeException e) { - // TODO: b/140123526 Send a response if exception was caught when processing a request. - - // Clean up all SaRecords. - closeAllSaRecords(false /*expectSaClosed*/); - - mUserCbExecutor.execute( - () -> { - mUserCallback.onClosedExceptionally(new IkeInternalException(e)); - }); - logWtf("Unexpected exception in " + getCurrentState().getName(), e); - quitNow(); - } - - @Override - protected String getCmdString(int cmd) { - return CMD_TO_STR.get(cmd); - } - } - - /** Called when this StateMachine quits. */ - @Override - protected void onQuitting() { - // Clean up all SaRecords. - closeAllSaRecords(true /*expectSaClosed*/); - - mChildSmCallback.onProcedureFinished(this); - mChildSmCallback.onChildSessionClosed(mUserCallback); - } - - private void closeAllSaRecords(boolean expectSaClosed) { - closeChildSaRecord(mCurrentChildSaRecord, expectSaClosed); - closeChildSaRecord(mLocalInitNewChildSaRecord, expectSaClosed); - closeChildSaRecord(mRemoteInitNewChildSaRecord, expectSaClosed); - - mCurrentChildSaRecord = null; - mLocalInitNewChildSaRecord = null; - mRemoteInitNewChildSaRecord = null; - } - - private void closeChildSaRecord(ChildSaRecord childSaRecord, boolean expectSaClosed) { - if (childSaRecord == null) return; - - mUserCbExecutor.execute( - () -> { - onIpSecTransformPairDeleted(childSaRecord); - }); - - mChildSmCallback.onChildSaDeleted(childSaRecord.getRemoteSpi()); - childSaRecord.close(); - - if (!expectSaClosed) return; - - logWtf( - "ChildSaRecord with local SPI: " - + childSaRecord.getLocalSpi() - + " is not correctly closed."); - } - - private void handleChildFatalError(Exception error) { - IkeException ikeException = - error instanceof IkeException - ? (IkeException) error - : new IkeInternalException(error); - - mUserCbExecutor.execute( - () -> { - mUserCallback.onClosedExceptionally(ikeException); - }); - loge("Child Session fatal error", ikeException); - - // Clean up all SaRecords and quit - closeAllSaRecords(false /*expectSaClosed*/); - quitNow(); - } - - /** - * This state handles the request to close Child Session immediately without initiating any - * exchange. - * - * <p>Request for closing Child Session immediately is usually caused by the closing of IKE - * Session. All states MUST be a child state of KillChildSessionParent to handle the closing - * request. - */ - private class KillChildSessionParent extends ExceptionHandler { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_KILL_SESSION: - mUserCbExecutor.execute( - () -> { - mUserCallback.onClosed(); - }); - - closeAllSaRecords(false /*expectSaClosed*/); - - quitNow(); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - /** - * CreateChildLocalCreateBase represents the common information for a locally-initiated initial - * Child SA negotiation for setting up this Child Session. - */ - private abstract class CreateChildLocalCreateBase extends ExceptionHandler { - protected void validateAndBuildChild( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - @ExchangeType int exchangeType, - @ExchangeType int expectedExchangeType, - int registeredSpi) { - CreateChildResult createChildResult = - CreateChildSaHelper.validateAndNegotiateInitChild( - reqPayloads, - respPayloads, - exchangeType, - expectedExchangeType, - mChildSessionOptions.isTransportMode(), - mIpSecManager, - mRemoteAddress); - switch (createChildResult.status) { - case CREATE_STATUS_OK: - try { - setUpNegotiatedResult(createChildResult); - - ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest(); - - mCurrentChildSaRecord = - ChildSaRecord.makeChildSaRecord( - mContext, - reqPayloads, - respPayloads, - createChildResult.initSpi, - createChildResult.respSpi, - mLocalAddress, - mRemoteAddress, - mUdpEncapSocket, - mIkePrf, - mChildIntegrity, - mChildCipher, - mSkD, - mChildSessionOptions.isTransportMode(), - true /*isLocalInit*/, - rekeyLocalRequest); - - mChildSmCallback.scheduleLocalRequest(rekeyLocalRequest, getRekeyTimeout()); - - ChildSessionConfiguration sessionConfig = - buildChildSessionConfigFromResp(createChildResult, respPayloads); - mUserCbExecutor.execute( - () -> { - mUserCallback.onIpSecTransformCreated( - mCurrentChildSaRecord.getInboundIpSecTransform(), - IpSecManager.DIRECTION_IN); - mUserCallback.onIpSecTransformCreated( - mCurrentChildSaRecord.getOutboundIpSecTransform(), - IpSecManager.DIRECTION_OUT); - mUserCallback.onOpened(sessionConfig); - }); - - transitionTo(mIdle); - } catch (GeneralSecurityException - | ResourceUnavailableException - | SpiUnavailableException - | IOException e) { - // #makeChildSaRecord failed. - - // TODO: Initiate deletion - mChildSmCallback.onChildSaDeleted(createChildResult.respSpi.getSpi()); - createChildResult.initSpi.close(); - createChildResult.respSpi.close(); - handleChildFatalError(e); - } - break; - case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: - // TODO: Initiate deletion - handleCreationFailAndQuit(registeredSpi, createChildResult.exception); - break; - case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY: - handleCreationFailAndQuit(registeredSpi, createChildResult.exception); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Unrecognized status: " + createChildResult.status)); - } - } - - private void setUpNegotiatedResult(CreateChildResult createChildResult) { - // Build crypto tools using negotiated ChildSaProposal. It is ensured by {@link - // IkeSaPayload#getVerifiedNegotiatedChildProposalPair} that the negotiated - // ChildSaProposal is valid. The negotiated ChildSaProposal has exactly one encryption - // algorithm. When it has a combined-mode encryption algorithm, it either does not have - // integrity algorithm or only has one NONE value integrity algorithm. When the - // negotiated ChildSaProposal has a normal encryption algorithm, it either does not have - // integrity algorithm or has one integrity algorithm with any supported value. - - mSaProposal = createChildResult.negotiatedProposal; - Provider provider = IkeMessage.getSecurityProvider(); - mChildCipher = IkeCipher.create(mSaProposal.getEncryptionTransforms()[0], provider); - if (mSaProposal.getIntegrityTransforms().length != 0 - && mSaProposal.getIntegrityTransforms()[0].id - != SaProposal.INTEGRITY_ALGORITHM_NONE) { - mChildIntegrity = - IkeMacIntegrity.create(mSaProposal.getIntegrityTransforms()[0], provider); - } - - mLocalTs = createChildResult.initTs; - mRemoteTs = createChildResult.respTs; - } - - private ChildSessionConfiguration buildChildSessionConfigFromResp( - CreateChildResult createChildResult, List<IkePayload> respPayloads) { - IkeConfigPayload configPayload = - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_CP, IkeConfigPayload.class, respPayloads); - - if (mChildSessionOptions.isTransportMode() - || configPayload == null - || configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) { - if (configPayload != null) { - logw("Unexpected config payload. Config Type: " + configPayload.configType); - } - - return new ChildSessionConfiguration( - Arrays.asList(createChildResult.initTs), - Arrays.asList(createChildResult.respTs)); - } else { - return new ChildSessionConfiguration( - Arrays.asList(createChildResult.initTs), - Arrays.asList(createChildResult.respTs), - configPayload); - } - } - - private void handleCreationFailAndQuit(int registeredSpi, IkeException exception) { - if (registeredSpi != SPI_NOT_REGISTERED) { - mChildSmCallback.onChildSaDeleted(registeredSpi); - } - handleChildFatalError(exception); - } - } - - /** Initial state of ChildSessionStateMachine. */ - class Initial extends CreateChildLocalCreateBase { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_FIRST_CHILD_EXCHANGE: - FirstChildNegotiationData childNegotiationData = - (FirstChildNegotiationData) message.obj; - List<IkePayload> reqPayloads = childNegotiationData.requestPayloads; - List<IkePayload> respPayloads = childNegotiationData.responsePayloads; - - // Negotiate Child SA. The exchangeType has been validated in - // IkeSessionStateMachine. Won't validate it again here. - validateAndBuildChild( - reqPayloads, - respPayloads, - EXCHANGE_TYPE_IKE_AUTH, - EXCHANGE_TYPE_IKE_AUTH, - childNegotiationData.registeredSpi); - - return HANDLED; - case CMD_LOCAL_REQUEST_CREATE_CHILD: - transitionTo(mCreateChildLocalCreate); - return HANDLED; - case CMD_LOCAL_REQUEST_DELETE_CHILD: - // This may happen when creation has been rescheduled to be after deletion. - mUserCbExecutor.execute( - () -> { - mUserCallback.onClosed(); - }); - quitNow(); - return HANDLED; - case CMD_FORCE_TRANSITION: - transitionTo((State) message.obj); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - /** - * CreateChildLocalCreate represents the state where Child Session initiates the Create Child - * exchange. - */ - class CreateChildLocalCreate extends CreateChildLocalCreateBase { - private List<IkePayload> mRequestPayloads; - - @Override - public void enterState() { - try { - mRequestPayloads = - CreateChildSaHelper.getInitChildCreateReqPayloads( - mIpSecManager, - mLocalAddress, - mChildSessionOptions, - false /*isFirstChild*/); - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_CREATE_CHILD_SA, - false /*isResp*/, - mRequestPayloads, - ChildSessionStateMachine.this); - } catch (ResourceUnavailableException e) { - // Fail to assign SPI - handleChildFatalError(e); - } - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_RECEIVED_RESPONSE: - ReceivedCreateResponse rcvResp = (ReceivedCreateResponse) message.obj; - validateAndBuildChild( - mRequestPayloads, - rcvResp.responsePayloads, - rcvResp.exchangeType, - EXCHANGE_TYPE_CREATE_CHILD_SA, - rcvResp.registeredSpi); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - /** - * Idle represents a state when there is no ongoing IKE exchange affecting established Child SA. - */ - class Idle extends ExceptionHandler { - @Override - public void enterState() { - mChildSmCallback.onProcedureFinished(ChildSessionStateMachine.this); - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_LOCAL_REQUEST_DELETE_CHILD: - transitionTo(mDeleteChildLocalDelete); - return HANDLED; - case CMD_LOCAL_REQUEST_REKEY_CHILD: - transitionTo(mRekeyChildLocalCreate); - return HANDLED; - case CMD_HANDLE_RECEIVED_REQUEST: - ReceivedRequest req = (ReceivedRequest) message.obj; - switch (req.exchangeSubtype) { - case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: - deferMessage(message); - transitionTo(mDeleteChildRemoteDelete); - return HANDLED; - case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: - deferMessage(message); - transitionTo(mRekeyChildRemoteCreate); - return HANDLED; - default: - return NOT_HANDLED; - } - case CMD_FORCE_TRANSITION: // Testing command - transitionTo((State) message.obj); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - /** - * DeleteResponderBase represents all states after Child Session is established - * - * <p>All post-init states share common functionality of being able to respond to Delete Child - * requests. - */ - private abstract class DeleteResponderBase extends ExceptionHandler { - /** - * Check if the payload list has a Delete Payload that includes the remote SPI of the input - * ChildSaRecord. - */ - protected boolean hasRemoteChildSpiForDelete( - List<IkePayload> payloads, ChildSaRecord expectedRecord) { - List<IkeDeletePayload> delPayloads = - IkePayload.getPayloadListForTypeInProvidedList( - PAYLOAD_TYPE_DELETE, IkeDeletePayload.class, payloads); - - for (IkeDeletePayload delPayload : delPayloads) { - for (int spi : delPayload.spisToDelete) { - if (spi == expectedRecord.getRemoteSpi()) return true; - } - } - return false; - } - - /** - * Build and send payload list that has a Delete Payload that includes the local SPI of the - * input ChildSaRecord. - */ - protected void sendDeleteChild(ChildSaRecord childSaRecord, boolean isResp) { - List<IkePayload> outIkePayloads = new ArrayList<>(1); - outIkePayloads.add(new IkeDeletePayload(new int[] {childSaRecord.getLocalSpi()})); - - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_INFORMATIONAL, - isResp, - outIkePayloads, - ChildSessionStateMachine.this); - } - - /** - * Helper method for responding to a session deletion request - * - * <p>Note that this method expects that the session is keyed on the mCurrentChildSaRecord - * and closing this Child SA indicates that the remote wishes to end the session as a whole. - * As such, this should not be used in rekey cases where there is any ambiguity as to which - * Child SA the session is reliant upon. - * - * <p>Note that this method will also quit the state machine - */ - protected void handleDeleteSessionRequest(List<IkePayload> payloads) { - if (!hasRemoteChildSpiForDelete(payloads, mCurrentChildSaRecord)) { - cleanUpAndQuit( - new IllegalStateException( - "Found no remote SPI for mCurrentChildSaRecord in a Delete Child" - + " request.")); - } else { - - mUserCbExecutor.execute( - () -> { - mUserCallback.onClosed(); - onIpSecTransformPairDeleted(mCurrentChildSaRecord); - }); - - sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/); - - mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi()); - mCurrentChildSaRecord.close(); - mCurrentChildSaRecord = null; - - quitNow(); - } - } - } - - /** - * DeleteBase abstracts deletion handling for all states initiating and responding to a Delete - * Child exchange - * - * <p>All subclasses of this state share common functionality that a deletion request is sent, - * and the response is received. - */ - private abstract class DeleteBase extends DeleteResponderBase { - /** Validate payload types in Delete Child response. */ - protected void validateDeleteRespPayloadAndExchangeType( - List<IkePayload> respPayloads, @ExchangeType int exchangeType) - throws IkeProtocolException { - - if (exchangeType != EXCHANGE_TYPE_INFORMATIONAL) { - throw new InvalidSyntaxException( - "Unexpected exchange type in Delete Child response: " + exchangeType); - } - - for (IkePayload payload : respPayloads) { - handlePayload: - switch (payload.payloadType) { - case PAYLOAD_TYPE_DELETE: - // A Delete Payload is only required when it is not simultaneous deletion. - // Included Child SPIs are verified in the subclass to make sure the remote - // side is deleting the right SAs. - break handlePayload; - case PAYLOAD_TYPE_NOTIFY: - IkeNotifyPayload notify = (IkeNotifyPayload) payload; - if (!notify.isErrorNotify()) { - logw( - "Unexpected or unknown status notification in Delete Child" - + " response: " - + notify.notifyType); - break handlePayload; - } - - throw notify.validateAndBuildIkeException(); - default: - logw( - "Unexpected payload type in Delete Child response: " - + payload.payloadType); - } - } - } - } - - /** - * DeleteChildLocalDelete represents the state where Child Session initiates the Delete Child - * exchange. - */ - class DeleteChildLocalDelete extends DeleteBase { - private boolean mSimulDeleteDetected = false; - - @Override - public void enterState() { - mSimulDeleteDetected = false; - sendDeleteChild(mCurrentChildSaRecord, false /*isResp*/); - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_RECEIVED_RESPONSE: - try { - ReceivedResponse resp = (ReceivedResponse) message.obj; - validateDeleteRespPayloadAndExchangeType( - resp.responsePayloads, resp.exchangeType); - - boolean currentSaSpiFound = - hasRemoteChildSpiForDelete( - resp.responsePayloads, mCurrentChildSaRecord); - if (!currentSaSpiFound && !mSimulDeleteDetected) { - throw new InvalidSyntaxException( - "Found no remote SPI in received Delete response."); - } else if (currentSaSpiFound && mSimulDeleteDetected) { - // As required by the RFC 7296, in simultaneous delete case, the remote - // side MUST NOT include SPI of mCurrentChildSaRecord. However, to - // provide better interoperatibility, IKE library will keep IKE Session - // alive and continue the deleting process. - logw( - "Found remote SPI in the Delete response in a simultaneous" - + " deletion case"); - } - - mUserCbExecutor.execute( - () -> { - mUserCallback.onClosed(); - onIpSecTransformPairDeleted(mCurrentChildSaRecord); - }); - - mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi()); - mCurrentChildSaRecord.close(); - mCurrentChildSaRecord = null; - - quitNow(); - } catch (IkeProtocolException e) { - // Shut down Child Session and notify users the error. - handleChildFatalError(e); - } - return HANDLED; - case CMD_HANDLE_RECEIVED_REQUEST: - ReceivedRequest req = (ReceivedRequest) message.obj; - switch (req.exchangeSubtype) { - case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: - // It has been verified in IkeSessionStateMachine that the incoming - // request can be ONLY for mCurrentChildSaRecord at this point. - if (!hasRemoteChildSpiForDelete( - req.requestPayloads, mCurrentChildSaRecord)) { - // Program error - cleanUpAndQuit( - new IllegalStateException( - "Found no remote SPI for mCurrentChildSaRecord in" - + " a Delete request")); - - } else { - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_INFORMATIONAL, - true /*isResp*/, - new LinkedList<>(), - ChildSessionStateMachine.this); - mSimulDeleteDetected = true; - } - return HANDLED; - case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: - replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE); - return HANDLED; - default: - cleanUpAndQuit( - new IllegalStateException( - "Invalid exchange subtype for Child Session: " - + req.exchangeSubtype)); - return HANDLED; - } - default: - return NOT_HANDLED; - } - } - } - - /** - * DeleteChildRemoteDelete represents the state where Child Session receives the Delete Child - * request. - */ - class DeleteChildRemoteDelete extends DeleteResponderBase { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_RECEIVED_REQUEST: - ReceivedRequest req = (ReceivedRequest) message.obj; - if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) { - handleDeleteSessionRequest(req.requestPayloads); - return HANDLED; - } - return NOT_HANDLED; - default: - return NOT_HANDLED; - } - } - } - - /** - * RekeyChildLocalCreate represents the state where Child Session initiates the Rekey Child - * exchange. - * - * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have - * different Traffic Selectors and algorithms than the old one." - */ - class RekeyChildLocalCreate extends DeleteResponderBase { - private List<IkePayload> mRequestPayloads; - - @Override - public void enterState() { - try { - // Build request with negotiated proposal and TS. - mRequestPayloads = - CreateChildSaHelper.getRekeyChildCreateReqPayloads( - mIpSecManager, - mLocalAddress, - mSaProposal, - mLocalTs, - mRemoteTs, - mCurrentChildSaRecord.getLocalSpi(), - mChildSessionOptions.isTransportMode()); - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_CREATE_CHILD_SA, - false /*isResp*/, - mRequestPayloads, - ChildSessionStateMachine.this); - } catch (ResourceUnavailableException e) { - loge("Fail to assign Child SPI. Schedule a retry for rekey Child"); - mChildSmCallback.scheduleRetryLocalRequest( - (ChildLocalRequest) mCurrentChildSaRecord.getFutureRekeyEvent()); - transitionTo(mIdle); - } - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_RECEIVED_RESPONSE: - ReceivedCreateResponse resp = (ReceivedCreateResponse) message.obj; - CreateChildResult createChildResult = - CreateChildSaHelper.validateAndNegotiateRekeyChildResp( - mRequestPayloads, - resp.responsePayloads, - resp.exchangeType, - EXCHANGE_TYPE_CREATE_CHILD_SA, - mChildSessionOptions.isTransportMode(), - mCurrentChildSaRecord, - mIpSecManager, - mRemoteAddress); - - switch (createChildResult.status) { - case CREATE_STATUS_OK: - try { - // Do not need to update the negotiated proposal and TS because they - // are not changed. - - ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest(); - - mLocalInitNewChildSaRecord = - ChildSaRecord.makeChildSaRecord( - mContext, - mRequestPayloads, - resp.responsePayloads, - createChildResult.initSpi, - createChildResult.respSpi, - mLocalAddress, - mRemoteAddress, - mUdpEncapSocket, - mIkePrf, - mChildIntegrity, - mChildCipher, - mSkD, - mChildSessionOptions.isTransportMode(), - true /*isLocalInit*/, - rekeyLocalRequest); - - mChildSmCallback.scheduleLocalRequest( - rekeyLocalRequest, getRekeyTimeout()); - - mUserCbExecutor.execute( - () -> { - mUserCallback.onIpSecTransformCreated( - mLocalInitNewChildSaRecord - .getInboundIpSecTransform(), - IpSecManager.DIRECTION_IN); - mUserCallback.onIpSecTransformCreated( - mLocalInitNewChildSaRecord - .getOutboundIpSecTransform(), - IpSecManager.DIRECTION_OUT); - }); - - transitionTo(mRekeyChildLocalDelete); - } catch (GeneralSecurityException - | ResourceUnavailableException - | SpiUnavailableException - | IOException e) { - // #makeChildSaRecord failed - handleProcessRespOrSaCreationFailAndQuit(resp.registeredSpi, e); - createChildResult.initSpi.close(); - createChildResult.respSpi.close(); - } - break; - case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: - handleProcessRespOrSaCreationFailAndQuit( - resp.registeredSpi, createChildResult.exception); - break; - case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY: - if (createChildResult.exception instanceof TemporaryFailureException) { - loge( - "Received TEMPORARY_FAILURE for rekey Child. Retry has" - + "already been scheduled by IKE Session."); - } else { - loge( - "Received error notification for rekey Child. Schedule a" - + " retry"); - mChildSmCallback.scheduleRetryLocalRequest( - (ChildLocalRequest) - mCurrentChildSaRecord.getFutureRekeyEvent()); - } - - transitionTo(mIdle); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Unrecognized status: " + createChildResult.status)); - } - return HANDLED; - default: - // TODO: Handle rekey and delete request - return NOT_HANDLED; - } - } - - private void handleProcessRespOrSaCreationFailAndQuit( - int registeredSpi, Exception exception) { - // We don't retry rekey if failure was caused by invalid response or SA creation error. - // Reason is there is no way to notify the remote side the old SA is still alive but the - // new one has failed. Sending delete request for new SA indicates the rekey has - // finished and the new SA has died. - - // TODO: Initiate deletion on newly created SA - if (registeredSpi != SPI_NOT_REGISTERED) { - mChildSmCallback.onChildSaDeleted(registeredSpi); - } - handleChildFatalError(exception); - } - } - - /** - * RekeyChildRemoteCreate represents the state where Child Session receives a Rekey Child - * request. - * - * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have - * different Traffic Selectors and algorithms than the old one." - * - * <p>Errors in this exchange with no specific protocol error code will all be classified to use - * NO_PROPOSAL_CHOSEN. The reason that we don't use NO_ADDITIONAL_SAS is because it indicates - * "responder is unwilling to accept any more Child SAs on this IKE SA.", according to RFC 7296. - * Sending this error may mislead the remote peer. - */ - class RekeyChildRemoteCreate extends ExceptionHandler { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_RECEIVED_REQUEST: - ReceivedRequest req = (ReceivedRequest) message.obj; - - if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_REKEY_CHILD) { - handleCreateChildRequest(req); - return HANDLED; - } - - return NOT_HANDLED; - default: - return NOT_HANDLED; - } - } - - private void handleCreateChildRequest(ReceivedRequest req) { - List<IkePayload> reqPayloads = null; - List<IkePayload> respPayloads = null; - try { - reqPayloads = req.requestPayloads; - - // Build a rekey response payload list with our previously selected proposal, - // against which we will validate the received request. It is guaranteed in - // IkeSessionStateMachine#getIkeExchangeSubType that a SA Payload is included in the - // inbound request payload list. - IkeSaPayload reqSaPayload = - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); - byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(mSaProposal); - - respPayloads = - CreateChildSaHelper.getRekeyChildCreateRespPayloads( - mIpSecManager, - mLocalAddress, - respProposalNumber, - mSaProposal, - mLocalTs, - mRemoteTs, - mCurrentChildSaRecord.getLocalSpi(), - mChildSessionOptions.isTransportMode()); - } catch (NoValidProposalChosenException e) { - handleCreationFailureAndBackToIdle(e); - return; - } catch (ResourceUnavailableException e) { - handleCreationFailureAndBackToIdle( - new NoValidProposalChosenException("Fail to assign inbound SPI", e)); - return; - } - - CreateChildResult createChildResult = - CreateChildSaHelper.validateAndNegotiateRekeyChildRequest( - reqPayloads, - respPayloads, - req.exchangeType /*exchangeType*/, - EXCHANGE_TYPE_CREATE_CHILD_SA /*expectedExchangeType*/, - mChildSessionOptions.isTransportMode(), - mIpSecManager, - mRemoteAddress); - - switch (createChildResult.status) { - case CREATE_STATUS_OK: - try { - // Do not need to update the negotiated proposal and TS - // because they are not changed. - - ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest(); - - mRemoteInitNewChildSaRecord = - ChildSaRecord.makeChildSaRecord( - mContext, - reqPayloads, - respPayloads, - createChildResult.initSpi, - createChildResult.respSpi, - mLocalAddress, - mRemoteAddress, - mUdpEncapSocket, - mIkePrf, - mChildIntegrity, - mChildCipher, - mSkD, - mChildSessionOptions.isTransportMode(), - false /*isLocalInit*/, - rekeyLocalRequest); - - mChildSmCallback.scheduleLocalRequest(rekeyLocalRequest, getRekeyTimeout()); - - mChildSmCallback.onChildSaCreated( - mRemoteInitNewChildSaRecord.getRemoteSpi(), - ChildSessionStateMachine.this); - - // To avoid traffic loss, outbound transform should only be applied once - // the remote has (implicitly) acknowledged our response via the - // delete-old-SA request. This will be performed in the finishRekey() - // method. - mUserCbExecutor.execute( - () -> { - mUserCallback.onIpSecTransformCreated( - mRemoteInitNewChildSaRecord.getInboundIpSecTransform(), - IpSecManager.DIRECTION_IN); - }); - - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_CREATE_CHILD_SA, - true /*isResp*/, - respPayloads, - ChildSessionStateMachine.this); - - transitionTo(mRekeyChildRemoteDelete); - } catch (GeneralSecurityException - | ResourceUnavailableException - | SpiUnavailableException - | IOException e) { - // #makeChildSaRecord failed. - createChildResult.initSpi.close(); - createChildResult.respSpi.close(); - - handleCreationFailureAndBackToIdle( - new NoValidProposalChosenException( - "Error in Child SA creation", e)); - } - break; - case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: - IkeException error = createChildResult.exception; - if (error instanceof IkeProtocolException) { - handleCreationFailureAndBackToIdle((IkeProtocolException) error); - } else { - handleCreationFailureAndBackToIdle( - new NoValidProposalChosenException( - "Error in validating Create Child request", error)); - } - break; - case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY: - cleanUpAndQuit( - new IllegalStateException( - "Unexpected processing status in Create Child request: " - + createChildResult.status)); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Unrecognized status: " + createChildResult.status)); - } - } - - private void handleCreationFailureAndBackToIdle(IkeProtocolException e) { - loge("Received invalid Rekey Child request. Reject with error notification", e); - - ArrayList<IkePayload> payloads = new ArrayList<>(1); - payloads.add(e.buildNotifyPayload()); - mChildSmCallback.onOutboundPayloadsReady( - EXCHANGE_TYPE_CREATE_CHILD_SA, - true /*isResp*/, - payloads, - ChildSessionStateMachine.this); - - transitionTo(mIdle); - } - } - - /** - * RekeyChildDeleteBase represents common behaviours of deleting stage during rekeying Child SA. - */ - abstract class RekeyChildDeleteBase extends DeleteBase { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_HANDLE_RECEIVED_REQUEST: - try { - if (isOnNewSa((ReceivedRequest) message.obj)) { - finishRekey(); - deferMessage(message); - transitionTo(mIdle); - return HANDLED; - } - return NOT_HANDLED; - } catch (IllegalStateException e) { - cleanUpAndQuit(e); - return HANDLED; - } - default: - return NOT_HANDLED; - } - } - - private boolean isOnNewSa(ReceivedRequest req) { - switch (req.exchangeSubtype) { - case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: - return hasRemoteChildSpiForDelete(req.requestPayloads, mChildSaRecordSurviving); - case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: - return CreateChildSaHelper.hasRemoteChildSpiForRekey( - req.requestPayloads, mChildSaRecordSurviving); - default: - throw new IllegalStateException( - "Invalid exchange subtype for Child Session: " + req.exchangeSubtype); - } - } - - // Rekey timer for old SA will be cancelled as part of the closing of the SA. - protected void finishRekey() { - mUserCbExecutor.execute( - () -> { - onIpSecTransformPairDeleted(mCurrentChildSaRecord); - }); - - mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi()); - mCurrentChildSaRecord.close(); - - mCurrentChildSaRecord = mChildSaRecordSurviving; - - mLocalInitNewChildSaRecord = null; - mRemoteInitNewChildSaRecord = null; - mChildSaRecordSurviving = null; - } - } - - /** - * RekeyChildLocalDelete represents the deleting stage of a locally-initiated Rekey Child - * procedure. - */ - class RekeyChildLocalDelete extends RekeyChildDeleteBase { - @Override - public void enterState() { - mChildSaRecordSurviving = mLocalInitNewChildSaRecord; - sendDeleteChild(mCurrentChildSaRecord, false /*isResp*/); - } - - @Override - public boolean processStateMessage(Message message) { - if (super.processStateMessage(message) == HANDLED) { - return HANDLED; - } - - switch (message.what) { - case CMD_HANDLE_RECEIVED_RESPONSE: - try { - ReceivedResponse resp = (ReceivedResponse) message.obj; - validateDeleteRespPayloadAndExchangeType( - resp.responsePayloads, resp.exchangeType); - - boolean currentSaSpiFound = - hasRemoteChildSpiForDelete( - resp.responsePayloads, mCurrentChildSaRecord); - if (!currentSaSpiFound) { - loge( - "Found no remote SPI for current SA in received Delete" - + " response. Shutting down old SA and finishing rekey."); - } - } catch (IkeProtocolException e) { - loge( - "Received Delete response with invalid syntax or error" - + " notifications. Shutting down old SA and finishing rekey.", - e); - } - finishRekey(); - transitionTo(mIdle); - return HANDLED; - default: - // TODO: Handle requests on mCurrentChildSaRecord: Reply TEMPORARY_FAILURE to - // a rekey request and reply empty INFORMATIONAL message to a delete request. - return NOT_HANDLED; - } - } - } - - /** - * RekeyChildRemoteDelete represents the deleting stage of a remotely-initiated Rekey Child - * procedure. - */ - class RekeyChildRemoteDelete extends RekeyChildDeleteBase { - @Override - public void enterState() { - mChildSaRecordSurviving = mRemoteInitNewChildSaRecord; - sendMessageDelayed(TIMEOUT_REKEY_REMOTE_DELETE, REKEY_DELETE_TIMEOUT_MS); - } - - @Override - public boolean processStateMessage(Message message) { - if (super.processStateMessage(message) == HANDLED) { - return HANDLED; - } - - switch (message.what) { - case CMD_HANDLE_RECEIVED_REQUEST: - ReceivedRequest req = (ReceivedRequest) message.obj; - - if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) { - handleDeleteRequest(req.requestPayloads); - - } else { - replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE); - } - return HANDLED; - case TIMEOUT_REKEY_REMOTE_DELETE: - // Receiving this signal means the remote side has received the outbound - // Rekey-Create response since no retransmissions were received during the - // waiting time. IKE library will assume the remote side has set up the new - // Child SA and finish the rekey procedure. Users should be warned there is - // a risk that the remote side failed to set up the new Child SA and all - // outbound IPsec traffic protected by new Child SA will be dropped. - - // TODO:Consider finishing rekey procedure if the IKE Session receives a new - // request. Since window size is one, receiving a new request indicates the - // remote side has received the outbound Rekey-Create response - - finishRekey(); - transitionTo(mIdle); - return HANDLED; - default: - return NOT_HANDLED; - } - } - - private void handleDeleteRequest(List<IkePayload> payloads) { - if (!hasRemoteChildSpiForDelete(payloads, mCurrentChildSaRecord)) { - // Request received on incorrect SA - cleanUpAndQuit( - new IllegalStateException( - "Found no remote SPI for current SA in received Delete" - + " response.")); - } else { - sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/); - finishRekey(); - transitionTo(mIdle); - } - } - - @Override - protected void finishRekey() { - mUserCbExecutor.execute( - () -> { - mUserCallback.onIpSecTransformCreated( - mRemoteInitNewChildSaRecord.getOutboundIpSecTransform(), - IpSecManager.DIRECTION_OUT); - }); - - super.finishRekey(); - } - - @Override - public void exitState() { - removeMessages(TIMEOUT_REKEY_REMOTE_DELETE); - } - } - - /** - * Package private helper class to generate IKE SA creation payloads, in both request and - * response directions. - */ - static class CreateChildSaHelper { - /** Create payload list for creating the initial Child SA for this Child Session. */ - public static List<IkePayload> getInitChildCreateReqPayloads( - IpSecManager ipSecManager, - InetAddress localAddress, - ChildSessionOptions childSessionOptions, - boolean isFirstChild) - throws ResourceUnavailableException { - - ChildSaProposal[] saProposals = childSessionOptions.getSaProposals(); - - if (isFirstChild) { - for (int i = 0; i < saProposals.length; i++) { - saProposals[i] = - childSessionOptions.getSaProposals()[i].getCopyWithoutDhTransform(); - } - } - - List<IkePayload> payloadList = - getChildCreatePayloads( - IkeSaPayload.createChildSaRequestPayload( - saProposals, ipSecManager, localAddress), - childSessionOptions.getLocalTrafficSelectors(), - childSessionOptions.getRemoteTrafficSelectors(), - childSessionOptions.isTransportMode()); - - if (!childSessionOptions.isTransportMode()) { - ConfigAttribute[] attributes = - ((TunnelModeChildSessionOptions) childSessionOptions) - .getConfigurationRequests(); - IkeConfigPayload configPayload = - new IkeConfigPayload(false /*isReply*/, Arrays.asList(attributes)); - payloadList.add(configPayload); - } - - return payloadList; - } - - /** Create payload list as a rekey Child Session request. */ - public static List<IkePayload> getRekeyChildCreateReqPayloads( - IpSecManager ipSecManager, - InetAddress localAddress, - ChildSaProposal currentProposal, - IkeTrafficSelector[] currentLocalTs, - IkeTrafficSelector[] currentRemoteTs, - int localSpi, - boolean isTransport) - throws ResourceUnavailableException { - List<IkePayload> payloads = - getChildCreatePayloads( - IkeSaPayload.createChildSaRequestPayload( - new ChildSaProposal[] {currentProposal}, - ipSecManager, - localAddress), - currentLocalTs, - currentRemoteTs, - isTransport); - - payloads.add( - new IkeNotifyPayload( - PROTOCOL_ID_ESP, localSpi, NOTIFY_TYPE_REKEY_SA, new byte[0])); - return payloads; - } - - /** Create payload list as a rekey Child Session response. */ - public static List<IkePayload> getRekeyChildCreateRespPayloads( - IpSecManager ipSecManager, - InetAddress localAddress, - byte proposalNumber, - ChildSaProposal currentProposal, - IkeTrafficSelector[] currentLocalTs, - IkeTrafficSelector[] currentRemoteTs, - int localSpi, - boolean isTransport) - throws ResourceUnavailableException { - List<IkePayload> payloads = - getChildCreatePayloads( - IkeSaPayload.createChildSaResponsePayload( - proposalNumber, currentProposal, ipSecManager, localAddress), - currentRemoteTs /*initTs*/, - currentLocalTs /*respTs*/, - isTransport); - - payloads.add( - new IkeNotifyPayload( - PROTOCOL_ID_ESP, localSpi, NOTIFY_TYPE_REKEY_SA, new byte[0])); - return payloads; - } - - /** Create payload list for creating a new Child SA. */ - private static List<IkePayload> getChildCreatePayloads( - IkeSaPayload saPayload, - IkeTrafficSelector[] initTs, - IkeTrafficSelector[] respTs, - boolean isTransport) - throws ResourceUnavailableException { - List<IkePayload> payloadList = new ArrayList<>(5); - - payloadList.add(saPayload); - payloadList.add(new IkeTsPayload(true /*isInitiator*/, initTs)); - payloadList.add(new IkeTsPayload(false /*isInitiator*/, respTs)); - payloadList.add(new IkeNoncePayload()); - - DhGroupTransform[] dhGroups = - ((ChildProposal) saPayload.proposalList.get(0)) - .saProposal.getDhGroupTransforms(); - if (dhGroups.length != 0 && dhGroups[0].id != DH_GROUP_NONE) { - payloadList.add(new IkeKePayload(dhGroups[0].id)); - } - - if (isTransport) payloadList.add(new IkeNotifyPayload(NOTIFY_TYPE_USE_TRANSPORT_MODE)); - - return payloadList; - } - - /** - * Validate the received response of initial Create Child SA exchange and return the - * negotiation result. - */ - public static CreateChildResult validateAndNegotiateInitChild( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - @ExchangeType int exchangeType, - @ExchangeType int expectedExchangeType, - boolean expectTransport, - IpSecManager ipSecManager, - InetAddress remoteAddress) { - - return validateAndNegotiateChild( - reqPayloads, - respPayloads, - exchangeType, - expectedExchangeType, - true /*isLocalInit*/, - expectTransport, - ipSecManager, - remoteAddress); - } - - /** - * Validate the received rekey-create request against locally built response (based on - * previously negotiated Child SA) and return the negotiation result. - */ - public static CreateChildResult validateAndNegotiateRekeyChildRequest( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - @ExchangeType int exchangeType, - @ExchangeType int expectedExchangeType, - boolean expectTransport, - IpSecManager ipSecManager, - InetAddress remoteAddress) { - - // It is guaranteed that a Rekey-Notify Payload with remote SPI of current Child SA is - // included in the reqPayloads. So we won't validate it again here. - return validateAndNegotiateChild( - reqPayloads, - respPayloads, - exchangeType, - expectedExchangeType, - false /*isLocalInit*/, - expectTransport, - ipSecManager, - remoteAddress); - } - - /** - * Validate the received rekey-create response against locally built request and previously - * negotiated Child SA, and return the negotiation result. - */ - public static CreateChildResult validateAndNegotiateRekeyChildResp( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - @ExchangeType int exchangeType, - @ExchangeType int expectedExchangeType, - boolean expectTransport, - ChildSaRecord expectedChildRecord, - IpSecManager ipSecManager, - InetAddress remoteAddress) { - // Validate rest of payloads and negotiate Child SA. - CreateChildResult childResult = - validateAndNegotiateChild( - reqPayloads, - respPayloads, - exchangeType, - expectedExchangeType, - true /*isLocalInit*/, - expectTransport, - ipSecManager, - remoteAddress); - - // TODO: Validate new Child SA does not have different Traffic Selectors - - return childResult; - } - - /** - * Check if SPI of Child SA that is expected to be rekeyed is included in the provided - * payload list. - */ - public static boolean hasRemoteChildSpiForRekey( - List<IkePayload> payloads, ChildSaRecord expectedRecord) { - List<IkeNotifyPayload> notifyPayloads = - IkePayload.getPayloadListForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, payloads); - - boolean hasExpectedRekeyNotify = false; - for (IkeNotifyPayload notifyPayload : notifyPayloads) { - if (notifyPayload.notifyType == NOTIFY_TYPE_REKEY_SA - && notifyPayload.spi == expectedRecord.getRemoteSpi()) { - hasExpectedRekeyNotify = true; - break; - } - } - - return hasExpectedRekeyNotify; - } - - /** Validate the received payload list and negotiate Child SA. */ - private static CreateChildResult validateAndNegotiateChild( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - @ExchangeType int exchangeType, - @ExchangeType int expectedExchangeType, - boolean isLocalInit, - boolean expectTransport, - IpSecManager ipSecManager, - InetAddress remoteAddress) { - List<IkePayload> inboundPayloads = isLocalInit ? respPayloads : reqPayloads; - - try { - validatePayloadAndExchangeType( - inboundPayloads, - isLocalInit /*isResp*/, - exchangeType, - expectedExchangeType); - } catch (InvalidSyntaxException e) { - return new CreateChildResult(CREATE_STATUS_CHILD_ERROR_INVALID_MSG, e); - } - - List<IkeNotifyPayload> notifyPayloads = - IkePayload.getPayloadListForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_NOTIFY, - IkeNotifyPayload.class, - inboundPayloads); - - boolean hasTransportNotify = false; - for (IkeNotifyPayload notify : notifyPayloads) { - if (notify.isErrorNotify()) { - try { - IkeProtocolException exception = notify.validateAndBuildIkeException(); - if (isLocalInit) { - return new CreateChildResult( - CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY, exception); - } else { - logw("Received unexpected error notification: " + notify.notifyType); - } - } catch (InvalidSyntaxException e) { - return new CreateChildResult(CREATE_STATUS_CHILD_ERROR_INVALID_MSG, e); - } - } - - switch (notify.notifyType) { - case IkeNotifyPayload.NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE: - // TODO: Store it as part of negotiation results that can be retrieved - // by users. - break; - case IkeNotifyPayload.NOTIFY_TYPE_IPCOMP_SUPPORTED: - // Ignore - break; - case IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE: - hasTransportNotify = true; - break; - case IkeNotifyPayload.NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED: - // Ignore - break; - default: - // Unknown and unexpected status notifications are ignored as per RFC7296. - logw( - "Received unknown or unexpected status notifications with notify" - + " type: " - + notify.notifyType); - } - } - - Pair<ChildProposal, ChildProposal> childProposalPair = null; - try { - IkeSaPayload reqSaPayload = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); - IkeSaPayload respSaPayload = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, respPayloads); - - // This method either throws exception or returns non-null pair that contains two - // valid {@link ChildProposal} both with a {@link SecurityParameterIndex} allocated - // inside. - childProposalPair = - IkeSaPayload.getVerifiedNegotiatedChildProposalPair( - reqSaPayload, respSaPayload, ipSecManager, remoteAddress); - ChildSaProposal saProposal = childProposalPair.second.saProposal; - - validateKePayloads(inboundPayloads, isLocalInit /*isResp*/, saProposal); - - if (expectTransport != hasTransportNotify) { - throw new NoValidProposalChosenException( - "Failed the negotiation on Child SA mode (conflicting modes chosen)."); - } - - Pair<IkeTrafficSelector[], IkeTrafficSelector[]> tsPair = - validateAndGetNegotiatedTsPair(reqPayloads, respPayloads); - - return new CreateChildResult( - childProposalPair.first.getChildSpiResource(), - childProposalPair.second.getChildSpiResource(), - saProposal, - tsPair.first, - tsPair.second); - } catch (IkeProtocolException - | ResourceUnavailableException - | SpiUnavailableException e) { - if (childProposalPair != null) { - childProposalPair.first.getChildSpiResource().close(); - childProposalPair.second.getChildSpiResource().close(); - } - - if (e instanceof InvalidSyntaxException) { - return new CreateChildResult( - CREATE_STATUS_CHILD_ERROR_INVALID_MSG, (InvalidSyntaxException) e); - } else if (e instanceof IkeProtocolException) { - return new CreateChildResult( - CREATE_STATUS_CHILD_ERROR_INVALID_MSG, - new InvalidSyntaxException( - "Processing error in received Create Child response", e)); - } else { - return new CreateChildResult( - CREATE_STATUS_CHILD_ERROR_INVALID_MSG, new IkeInternalException(e)); - } - } - } - - // Validate syntax to make sure all necessary payloads exist and exchange type is correct. - private static void validatePayloadAndExchangeType( - List<IkePayload> inboundPayloads, - boolean isResp, - @ExchangeType int exchangeType, - @ExchangeType int expectedExchangeType) - throws InvalidSyntaxException { - boolean hasSaPayload = false; - boolean hasKePayload = false; - boolean hasNoncePayload = false; - boolean hasTsInitPayload = false; - boolean hasTsRespPayload = false; - boolean hasErrorNotify = false; - - for (IkePayload payload : inboundPayloads) { - switch (payload.payloadType) { - case PAYLOAD_TYPE_SA: - hasSaPayload = true; - break; - case PAYLOAD_TYPE_KE: - // Could not decide if KE Payload MUST or MUST NOT be included until SA - // negotiation is done. - hasKePayload = true; - break; - case PAYLOAD_TYPE_NONCE: - hasNoncePayload = true; - break; - case PAYLOAD_TYPE_TS_INITIATOR: - hasTsInitPayload = true; - break; - case PAYLOAD_TYPE_TS_RESPONDER: - hasTsRespPayload = true; - break; - case PAYLOAD_TYPE_NOTIFY: - if (((IkeNotifyPayload) payload).isErrorNotify()) hasErrorNotify = true; - // Do not have enough context to handle all notifications. Handle them - // together in higher layer. - break; - case PAYLOAD_TYPE_CP: - // Handled in child creation state. Note Child Session can only handle - // Config Payload in initial creation and can only handle a Config Reply. - // For interoperability, Config Payloads received in rekey creation - // or with other config types will be ignored. - break; - default: - logw( - "Received unexpected payload in Create Child SA message. Payload" - + " type: " - + payload.payloadType); - } - } - - // Do not need to check exchange type of a request because it has been already verified - // in IkeSessionStateMachine - if (isResp - && exchangeType != expectedExchangeType - && exchangeType != EXCHANGE_TYPE_INFORMATIONAL) { - throw new InvalidSyntaxException("Received invalid exchange type: " + exchangeType); - } - - if (exchangeType == EXCHANGE_TYPE_INFORMATIONAL - && (hasSaPayload - || hasKePayload - || hasNoncePayload - || hasTsInitPayload - || hasTsRespPayload)) { - logw( - "Unexpected payload found in an INFORMATIONAL message: SA, KE, Nonce," - + " TS-Initiator or TS-Responder"); - } - - if (isResp - && !hasErrorNotify - && (!hasSaPayload - || !hasNoncePayload - || !hasTsInitPayload - || !hasTsRespPayload)) { - throw new InvalidSyntaxException( - "SA, Nonce, TS-Initiator or TS-Responder missing."); - } - } - - private static Pair<IkeTrafficSelector[], IkeTrafficSelector[]> - validateAndGetNegotiatedTsPair( - List<IkePayload> reqPayloads, List<IkePayload> respPayloads) - throws TsUnacceptableException { - IkeTrafficSelector[] initTs = - validateAndGetNegotiatedTs(reqPayloads, respPayloads, true /*isInitTs*/); - IkeTrafficSelector[] respTs = - validateAndGetNegotiatedTs(reqPayloads, respPayloads, false /*isInitTs*/); - - return new Pair<IkeTrafficSelector[], IkeTrafficSelector[]>(initTs, respTs); - } - - private static IkeTrafficSelector[] validateAndGetNegotiatedTs( - List<IkePayload> reqPayloads, List<IkePayload> respPayloads, boolean isInitTs) - throws TsUnacceptableException { - int tsType = isInitTs ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER; - IkeTsPayload reqPayload = - IkePayload.getPayloadForTypeInProvidedList( - tsType, IkeTsPayload.class, reqPayloads); - IkeTsPayload respPayload = - IkePayload.getPayloadForTypeInProvidedList( - tsType, IkeTsPayload.class, respPayloads); - - if (!reqPayload.contains(respPayload)) { - throw new TsUnacceptableException(); - } - - // It is guaranteed by decoding inbound TS Payload and constructing outbound TS Payload - // that each TS Payload has at least one IkeTrafficSelector. - return respPayload.trafficSelectors; - } - - @VisibleForTesting - static void validateKePayloads( - List<IkePayload> inboundPayloads, - boolean isResp, - ChildSaProposal negotiatedProposal) - throws IkeProtocolException { - DhGroupTransform[] dhTransforms = negotiatedProposal.getDhGroupTransforms(); - - if (dhTransforms.length > 1) { - throw new IllegalArgumentException( - "Found multiple DH Group Transforms in the negotiated SA proposal"); - } - boolean expectKePayload = - dhTransforms.length == 1 && dhTransforms[0].id != DH_GROUP_NONE; - - IkeKePayload kePayload = - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_KE, IkeKePayload.class, inboundPayloads); - - if (expectKePayload && (kePayload == null || dhTransforms[0].id != kePayload.dhGroup)) { - if (isResp) { - throw new InvalidSyntaxException( - "KE Payload missing or has mismatched DH Group with the negotiated" - + " proposal."); - } else { - throw new InvalidKeException(dhTransforms[0].id); - } - - } else if (!expectKePayload && kePayload != null && isResp) { - // It is valid when the remote request proposed multiple DH Groups with a KE - // payload, and the responder chose DH_GROUP_NONE. - throw new InvalidSyntaxException("Received unexpected KE Payload."); - } - } - - private static void logw(String s) { - getIkeLog().w(TAG, s); - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - CREATE_STATUS_OK, - CREATE_STATUS_CHILD_ERROR_INVALID_MSG, - CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY - }) - @interface CreateStatus {} - - /** The Child SA negotiation succeeds. */ - private static final int CREATE_STATUS_OK = 0; - /** The inbound message is invalid in Child negotiation but is non-fatal for IKE Session. */ - private static final int CREATE_STATUS_CHILD_ERROR_INVALID_MSG = 1; - /** The inbound message includes error notification that failed the Child negotiation. */ - private static final int CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY = 2; - - private static class CreateChildResult { - @CreateStatus public final int status; - public final SecurityParameterIndex initSpi; - public final SecurityParameterIndex respSpi; - public final ChildSaProposal negotiatedProposal; - public final IkeTrafficSelector[] initTs; - public final IkeTrafficSelector[] respTs; - public final IkeException exception; - - private CreateChildResult( - @CreateStatus int status, - SecurityParameterIndex initSpi, - SecurityParameterIndex respSpi, - ChildSaProposal negotiatedProposal, - IkeTrafficSelector[] initTs, - IkeTrafficSelector[] respTs, - IkeException exception) { - this.status = status; - this.initSpi = initSpi; - this.respSpi = respSpi; - this.negotiatedProposal = negotiatedProposal; - this.initTs = initTs; - this.respTs = respTs; - this.exception = exception; - } - - /* Construct a CreateChildResult instance for a successful case. */ - CreateChildResult( - SecurityParameterIndex initSpi, - SecurityParameterIndex respSpi, - ChildSaProposal negotiatedProposal, - IkeTrafficSelector[] initTs, - IkeTrafficSelector[] respTs) { - this( - CREATE_STATUS_OK, - initSpi, - respSpi, - negotiatedProposal, - initTs, - respTs, - null /*exception*/); - } - - /** Construct a CreateChildResult instance for an error case. */ - CreateChildResult(@CreateStatus int status, IkeException exception) { - this( - status, - null /*initSpi*/, - null /*respSpi*/, - null /*negotiatedProposal*/, - null /*initTs*/, - null /*respTs*/, - exception); - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeEapAuthenticatorFactory.java b/src/java/com/android/internal/net/ipsec/ike/IkeEapAuthenticatorFactory.java deleted file mode 100644 index b0d6f127..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/IkeEapAuthenticatorFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import android.content.Context; -import android.net.eap.EapSessionConfig; -import android.os.Looper; - -import com.android.internal.net.eap.EapAuthenticator; -import com.android.internal.net.eap.IEapCallback; - -/** Package private factory for building EapAuthenticator instances. */ -final class IkeEapAuthenticatorFactory { - /** - * Builds and returns a new EapAuthenticator - * - * @param looper Looper for running a message loop - * @param cbHandler Handler for posting callbacks to the given IEapCallback - * @param cb IEapCallback for callbacks to the client - * @param context Context for the EapAuthenticator - */ - public EapAuthenticator newEapAuthenticator( - Looper looper, IEapCallback cb, Context context, EapSessionConfig eapSessionConfig) { - return new EapAuthenticator(looper, cb, context, eapSessionConfig); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java deleted file mode 100644 index b7bf8701..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import android.net.ipsec.ike.ChildSessionCallback; -import android.net.ipsec.ike.ChildSessionOptions; - -import java.util.LinkedList; - -/** - * IkeLocalRequestScheduler caches all local requests scheduled by an IKE Session and notify the IKE - * Session to process the request when it is allowed. - * - * <p>LocalRequestScheduler is running on the IkeSessionStateMachine thread. - */ -public final class IkeLocalRequestScheduler { - private final LinkedList<LocalRequest> mRequestQueue = new LinkedList<>(); - - private final IProcedureConsumer mConsumer; - - private boolean mLocalProcedureOngoing; - private boolean mRemoteProcedureOngoing; - - /** - * Construct an instance of IkeLocalRequestScheduler - * - * @param consumer the interface to initiate new procedure. - */ - public IkeLocalRequestScheduler(IProcedureConsumer consumer) { - mConsumer = consumer; - } - - /** Add a new local request to the queue. */ - public void addRequest(LocalRequest request) { - mRequestQueue.offer(request); - } - - /** Add a new local request to the front of the queue. */ - public void addRequestAtFront(LocalRequest request) { - mRequestQueue.offerFirst(request); - } - - /** - * Notifies the scheduler that the caller is ready for a new procedure - * - * <p>Synchronously triggers the call to onNewProcedureReady. - */ - public void readyForNextProcedure() { - while (!mRequestQueue.isEmpty()) { - LocalRequest request = mRequestQueue.poll(); - if (!request.isCancelled()) { - mConsumer.onNewProcedureReady(request); - return; - } - } - } - - /** - * This class represents a user requested or internally scheduled IKE procedure that will be - * initiated locally. - */ - public static class LocalRequest { - public final int procedureType; - // TODO: Also store specific payloads for INFO exchange. - private boolean mIsCancelled; - - LocalRequest(int type) { - procedureType = type; - mIsCancelled = false; - } - - boolean isCancelled() { - return mIsCancelled; - } - - void cancel() { - mIsCancelled = true; - } - } - - /** - * This class represents a user requested or internally scheduled Child procedure that will be - * initiated locally. - */ - public static class ChildLocalRequest extends LocalRequest { - public final ChildSessionCallback childSessionCallback; - public final ChildSessionOptions childSessionOptions; - - ChildLocalRequest( - int type, ChildSessionCallback childCallback, ChildSessionOptions childOptions) { - super(type); - childSessionOptions = childOptions; - childSessionCallback = childCallback; - } - } - - /** Interface to initiate a new IKE procedure */ - public interface IProcedureConsumer { - /** - * Called when a new IKE procedure can be initiated. - * - * @param localRequest the request to be initiated. - */ - void onNewProcedureReady(LocalRequest localRequest); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java deleted file mode 100644 index 03c419db..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java +++ /dev/null @@ -1,4235 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ErrorType; - -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL; -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_OK; -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_PARTIAL; -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_PROTECTED_ERROR; -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_UNPROTECTED_ERROR; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_VENDOR; - -import android.annotation.IntDef; -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.ResourceUnavailableException; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.ipsec.ike.ChildSessionCallback; -import android.net.ipsec.ike.ChildSessionOptions; -import android.net.ipsec.ike.IkeSaProposal; -import android.net.ipsec.ike.IkeSessionCallback; -import android.net.ipsec.ike.IkeSessionOptions; -import android.net.ipsec.ike.IkeSessionOptions.IkeAuthConfig; -import android.net.ipsec.ike.IkeSessionOptions.IkeAuthDigitalSignRemoteConfig; -import android.net.ipsec.ike.IkeSessionOptions.IkeAuthPskConfig; -import android.net.ipsec.ike.exceptions.IkeException; -import android.net.ipsec.ike.exceptions.IkeInternalException; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; -import android.util.LongSparseArray; -import android.util.Pair; -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.eap.EapAuthenticator; -import com.android.internal.net.eap.IEapCallback; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachine.CreateChildSaHelper; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecord; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.message.IkeAuthDigitalSignPayload; -import com.android.internal.net.ipsec.ike.message.IkeAuthPayload; -import com.android.internal.net.ipsec.ike.message.IkeAuthPskPayload; -import com.android.internal.net.ipsec.ike.message.IkeCertPayload; -import com.android.internal.net.ipsec.ike.message.IkeCertX509CertPayload; -import com.android.internal.net.ipsec.ike.message.IkeDeletePayload; -import com.android.internal.net.ipsec.ike.message.IkeEapPayload; -import com.android.internal.net.ipsec.ike.message.IkeHeader; -import com.android.internal.net.ipsec.ike.message.IkeHeader.ExchangeType; -import com.android.internal.net.ipsec.ike.message.IkeIdPayload; -import com.android.internal.net.ipsec.ike.message.IkeInformationalPayload; -import com.android.internal.net.ipsec.ike.message.IkeKePayload; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResult; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultError; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultOk; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultPartial; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultProtectedError; -import com.android.internal.net.ipsec.ike.message.IkeNoncePayload; -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IkeProposal; -import com.android.internal.net.ipsec.ike.message.IkeTsPayload; -import com.android.internal.net.ipsec.ike.utils.Retransmitter; -import com.android.internal.util.State; - -import dalvik.system.CloseGuard; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.security.GeneralSecurityException; -import java.security.Provider; -import java.security.SecureRandom; -import java.security.cert.TrustAnchor; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -/** - * IkeSessionStateMachine tracks states and manages exchanges of this IKE session. - * - * <p>IkeSessionStateMachine has two types of states. One type are states where there is no ongoing - * procedure affecting IKE session (non-procedure state), including Initial, Idle and Receiving. All - * other states are "procedure" states which are named as follows: - * - * <pre> - * State Name = [Procedure Type] + [Exchange Initiator] + [Exchange Type]. - * - An IKE procedure consists of one or two IKE exchanges: - * Procedure Type = {CreateIke | DeleteIke | Info | RekeyIke | SimulRekeyIke}. - * - Exchange Initiator indicates whether local or remote peer is the exchange initiator: - * Exchange Initiator = {Local | Remote} - * - Exchange type defines the function of this exchange. To make it more descriptive, we separate - * Delete Exchange from generic Informational Exchange: - * Exchange Type = {IkeInit | IkeAuth | Create | Delete | Info} - * </pre> - */ -public class IkeSessionStateMachine extends AbstractSessionStateMachine { - - private static final String TAG = "IkeSessionStateMachine"; - - // TODO: b/140579254 Allow users to configure fragment size. - - // Default fragment size in bytes. - @VisibleForTesting static final int DEFAULT_FRAGMENT_SIZE = 1280; - - // TODO: Add SA_HARD_LIFETIME_MS - - // Time after which IKE SA needs to be rekeyed - @VisibleForTesting static final long SA_SOFT_LIFETIME_MS = TimeUnit.HOURS.toMillis(3L); - - // Default delay time for retrying a request - @VisibleForTesting static final long RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15L); - - // Close IKE Session when all responses during this time were TEMPORARY_FAILURE(s). This - // indicates that something has gone wrong, and we are out of sync. - @VisibleForTesting - static final long TEMP_FAILURE_RETRY_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(5L); - - // TODO: Allow users to configure IKE lifetime - - // Package private IKE exchange subtypes describe the specific function of a IKE - // request/response exchange. It helps IkeSessionStateMachine to do message validation according - // to the subtype specific rules. - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - IKE_EXCHANGE_SUBTYPE_INVALID, - IKE_EXCHANGE_SUBTYPE_IKE_INIT, - IKE_EXCHANGE_SUBTYPE_IKE_AUTH, - IKE_EXCHANGE_SUBTYPE_DELETE_IKE, - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - IKE_EXCHANGE_SUBTYPE_REKEY_IKE, - IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, - IKE_EXCHANGE_SUBTYPE_GENERIC_INFO - }) - @interface IkeExchangeSubType {} - - static final int IKE_EXCHANGE_SUBTYPE_INVALID = 0; - static final int IKE_EXCHANGE_SUBTYPE_IKE_INIT = 1; - static final int IKE_EXCHANGE_SUBTYPE_IKE_AUTH = 2; - static final int IKE_EXCHANGE_SUBTYPE_CREATE_CHILD = 3; - static final int IKE_EXCHANGE_SUBTYPE_DELETE_IKE = 4; - static final int IKE_EXCHANGE_SUBTYPE_DELETE_CHILD = 5; - static final int IKE_EXCHANGE_SUBTYPE_REKEY_IKE = 6; - static final int IKE_EXCHANGE_SUBTYPE_REKEY_CHILD = 7; - static final int IKE_EXCHANGE_SUBTYPE_GENERIC_INFO = 8; - - private static final SparseArray<String> EXCHANGE_SUBTYPE_TO_STRING; - - static { - EXCHANGE_SUBTYPE_TO_STRING = new SparseArray<>(); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_INVALID, "Invalid"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_IKE_INIT, "IKE INIT"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_IKE_AUTH, "IKE AUTH"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_CREATE_CHILD, "Create Child"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_DELETE_IKE, "Delete IKE"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, "Delete Child"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_REKEY_IKE, "Rekey IKE"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, "Rekey Child"); - EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_GENERIC_INFO, "Generic Info"); - } - - /** Package private signals accessible for testing code. */ - private static final int CMD_GENERAL_BASE = CMD_PRIVATE_BASE; - - /** Receive encoded IKE packet on IkeSessionStateMachine. */ - static final int CMD_RECEIVE_IKE_PACKET = CMD_GENERAL_BASE + 1; - /** Receive encoded IKE packet with unrecognized IKE SPI on IkeSessionStateMachine. */ - static final int CMD_RECEIVE_PACKET_INVALID_IKE_SPI = CMD_GENERAL_BASE + 2; - /** Receive an remote request for a Child procedure. */ - static final int CMD_RECEIVE_REQUEST_FOR_CHILD = CMD_GENERAL_BASE + 3; - /** Receive payloads from Child Session for building an outbound IKE message. */ - static final int CMD_OUTBOUND_CHILD_PAYLOADS_READY = CMD_GENERAL_BASE + 4; - /** A Child Session has finished its procedure. */ - static final int CMD_CHILD_PROCEDURE_FINISHED = CMD_GENERAL_BASE + 5; - /** Send request/response payloads to ChildSessionStateMachine for further processing. */ - static final int CMD_HANDLE_FIRST_CHILD_NEGOTIATION = CMD_GENERAL_BASE + 6; - /** Receive a local request to execute from the scheduler */ - static final int CMD_EXECUTE_LOCAL_REQ = CMD_GENERAL_BASE + 7; - /** Trigger a retransmission. */ - public static final int CMD_RETRANSMIT = CMD_GENERAL_BASE + 8; - /** Send EAP request payloads to EapAuthenticator for further processing. */ - static final int CMD_EAP_START_EAP_AUTH = CMD_GENERAL_BASE + 9; - /** Send the outbound IKE-wrapped EAP-Response message. */ - static final int CMD_EAP_OUTBOUND_MSG_READY = CMD_GENERAL_BASE + 10; - /** Proxy to IkeSessionStateMachine handler to notify of errors */ - static final int CMD_EAP_ERRORED = CMD_GENERAL_BASE + 11; - /** Proxy to IkeSessionStateMachine handler to notify of failures */ - static final int CMD_EAP_FAILED = CMD_GENERAL_BASE + 12; - /** Proxy to IkeSessionStateMachine handler to notify of success, to continue to post-auth */ - static final int CMD_EAP_FINISH_EAP_AUTH = CMD_GENERAL_BASE + 14; - /** Force state machine to a target state for testing purposes. */ - static final int CMD_FORCE_TRANSITION = CMD_GENERAL_BASE + 99; - - static final int CMD_IKE_LOCAL_REQUEST_BASE = CMD_GENERAL_BASE + CMD_CATEGORY_SIZE; - static final int CMD_LOCAL_REQUEST_CREATE_IKE = CMD_IKE_LOCAL_REQUEST_BASE + 1; - static final int CMD_LOCAL_REQUEST_DELETE_IKE = CMD_IKE_LOCAL_REQUEST_BASE + 2; - static final int CMD_LOCAL_REQUEST_REKEY_IKE = CMD_IKE_LOCAL_REQUEST_BASE + 3; - static final int CMD_LOCAL_REQUEST_INFO = CMD_IKE_LOCAL_REQUEST_BASE + 4; - - private static final SparseArray<String> CMD_TO_STR; - - static { - CMD_TO_STR = new SparseArray<>(); - CMD_TO_STR.put(CMD_RECEIVE_IKE_PACKET, "Rcv packet"); - CMD_TO_STR.put(CMD_RECEIVE_PACKET_INVALID_IKE_SPI, "Rcv invalid IKE SPI"); - CMD_TO_STR.put(CMD_RECEIVE_REQUEST_FOR_CHILD, "Rcv Child request"); - CMD_TO_STR.put(CMD_OUTBOUND_CHILD_PAYLOADS_READY, "Out child payloads ready"); - CMD_TO_STR.put(CMD_CHILD_PROCEDURE_FINISHED, "Child procedure finished"); - CMD_TO_STR.put(CMD_HANDLE_FIRST_CHILD_NEGOTIATION, "Negotiate first Child"); - CMD_TO_STR.put(CMD_EXECUTE_LOCAL_REQ, "Execute local request"); - CMD_TO_STR.put(CMD_RETRANSMIT, "Retransmit"); - CMD_TO_STR.put(CMD_EAP_START_EAP_AUTH, "Start EAP"); - CMD_TO_STR.put(CMD_EAP_OUTBOUND_MSG_READY, "EAP outbound msg ready"); - CMD_TO_STR.put(CMD_EAP_ERRORED, "EAP errored"); - CMD_TO_STR.put(CMD_EAP_FAILED, "EAP failed"); - CMD_TO_STR.put(CMD_EAP_FINISH_EAP_AUTH, "Finish EAP"); - CMD_TO_STR.put(CMD_LOCAL_REQUEST_CREATE_IKE, "Create IKE"); - CMD_TO_STR.put(CMD_LOCAL_REQUEST_DELETE_IKE, "Delete IKE"); - CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_IKE, "Rekey IKE"); - CMD_TO_STR.put(CMD_LOCAL_REQUEST_INFO, "Info"); - } - - private final IkeSessionOptions mIkeSessionOptions; - - /** Map that stores all IkeSaRecords, keyed by locally generated IKE SPI. */ - private final LongSparseArray<IkeSaRecord> mLocalSpiToIkeSaRecordMap; - /** - * Map that stores all ChildSessionStateMachines, keyed by remotely generated Child SPI for - * sending IPsec packet. Different SPIs may point to the same ChildSessionStateMachine if this - * Child Session is doing Rekey. - */ - private final SparseArray<ChildSessionStateMachine> mRemoteSpiToChildSessionMap; - - private final Context mContext; - private final IpSecManager mIpSecManager; - private final IkeLocalRequestScheduler mScheduler; - private final Executor mUserCbExecutor; - private final IkeSessionCallback mIkeSessionCallback; - private final IkeEapAuthenticatorFactory mEapAuthenticatorFactory; - private final TempFailureHandler mTempFailHandler; - - @VisibleForTesting - @GuardedBy("mChildCbToSessions") - final HashMap<ChildSessionCallback, ChildSessionStateMachine> mChildCbToSessions = - new HashMap<>(); - - /** - * Package private socket that sends and receives encoded IKE message. Initialized in Initial - * State. - */ - @VisibleForTesting IkeSocket mIkeSocket; - - /** Local address assigned on device. Initialized in Initial State. */ - @VisibleForTesting InetAddress mLocalAddress; - /** Remote address configured by users. Initialized in Initial State. */ - @VisibleForTesting InetAddress mRemoteAddress; - /** Local port assigned on device. Initialized in Initial State. */ - @VisibleForTesting int mLocalPort; - - /** Indicates if local node is behind a NAT. */ - @VisibleForTesting boolean mIsLocalBehindNat; - /** Indicates if remote node is behind a NAT. */ - @VisibleForTesting boolean mIsRemoteBehindNat; - - /** Indicates if both sides support fragmentation. Set in IKE INIT */ - @VisibleForTesting boolean mSupportFragment; - - /** Package private IkeSaProposal that represents the negotiated IKE SA proposal. */ - @VisibleForTesting IkeSaProposal mSaProposal; - - @VisibleForTesting IkeCipher mIkeCipher; - @VisibleForTesting IkeMacIntegrity mIkeIntegrity; - @VisibleForTesting IkeMacPrf mIkePrf; - - // FIXME: b/131265898 Pass these parameters from CreateIkeLocalIkeInit to CreateIkeLocalIkeAuth - // as entry data when Android StateMachine can support that. - @VisibleForTesting byte[] mIkeInitRequestBytes; - @VisibleForTesting byte[] mIkeInitResponseBytes; - @VisibleForTesting IkeNoncePayload mIkeInitNoncePayload; - @VisibleForTesting IkeNoncePayload mIkeRespNoncePayload; - - // FIXME: b/131265898 Pass these parameters from CreateIkeLocalIkeAuth through to - // CreateIkeLocalIkeAuthPostEap as entry data when Android StateMachine can support that. - @VisibleForTesting IkeIdPayload mInitIdPayload; - @VisibleForTesting IkeIdPayload mRespIdPayload; - @VisibleForTesting List<IkePayload> mFirstChildReqList; - - // FIXME: b/131265898 Move into CreateIkeLocalIkeAuth, and pass through to - // CreateIkeLocalIkeAuthPostEap once passing entry data is supported - private ChildSessionOptions mFirstChildSessionOptions; - private ChildSessionCallback mFirstChildCallbacks; - - /** Package */ - @VisibleForTesting IkeSaRecord mCurrentIkeSaRecord; - /** Package */ - @VisibleForTesting IkeSaRecord mLocalInitNewIkeSaRecord; - /** Package */ - @VisibleForTesting IkeSaRecord mRemoteInitNewIkeSaRecord; - - /** Package */ - @VisibleForTesting IkeSaRecord mIkeSaRecordSurviving; - /** Package */ - @VisibleForTesting IkeSaRecord mIkeSaRecordAwaitingLocalDel; - /** Package */ - @VisibleForTesting IkeSaRecord mIkeSaRecordAwaitingRemoteDel; - - // States - @VisibleForTesting final State mInitial = new Initial(); - @VisibleForTesting final State mIdle = new Idle(); - @VisibleForTesting final State mChildProcedureOngoing = new ChildProcedureOngoing(); - @VisibleForTesting final State mReceiving = new Receiving(); - @VisibleForTesting final State mCreateIkeLocalIkeInit = new CreateIkeLocalIkeInit(); - @VisibleForTesting final State mCreateIkeLocalIkeAuth = new CreateIkeLocalIkeAuth(); - @VisibleForTesting final State mCreateIkeLocalIkeAuthInEap = new CreateIkeLocalIkeAuthInEap(); - - @VisibleForTesting - final State mCreateIkeLocalIkeAuthPostEap = new CreateIkeLocalIkeAuthPostEap(); - - @VisibleForTesting final State mRekeyIkeLocalCreate = new RekeyIkeLocalCreate(); - @VisibleForTesting final State mSimulRekeyIkeLocalCreate = new SimulRekeyIkeLocalCreate(); - - @VisibleForTesting - final State mSimulRekeyIkeLocalDeleteRemoteDelete = new SimulRekeyIkeLocalDeleteRemoteDelete(); - - @VisibleForTesting final State mSimulRekeyIkeLocalDelete = new SimulRekeyIkeLocalDelete(); - @VisibleForTesting final State mSimulRekeyIkeRemoteDelete = new SimulRekeyIkeRemoteDelete(); - @VisibleForTesting final State mRekeyIkeLocalDelete = new RekeyIkeLocalDelete(); - @VisibleForTesting final State mRekeyIkeRemoteDelete = new RekeyIkeRemoteDelete(); - @VisibleForTesting final State mDeleteIkeLocalDelete = new DeleteIkeLocalDelete(); - // TODO: Add InfoLocal. - - /** Constructor for testing. */ - @VisibleForTesting - public IkeSessionStateMachine( - Looper looper, - Context context, - IpSecManager ipSecManager, - IkeSessionOptions ikeOptions, - ChildSessionOptions firstChildOptions, - Executor userCbExecutor, - IkeSessionCallback ikeSessionCallback, - ChildSessionCallback firstChildSessionCallback, - IkeEapAuthenticatorFactory eapAuthenticatorFactory) { - super(TAG, looper); - - mIkeSessionOptions = ikeOptions; - mEapAuthenticatorFactory = eapAuthenticatorFactory; - - mTempFailHandler = new TempFailureHandler(looper); - - // There are at most three IkeSaRecords co-existing during simultaneous rekeying. - mLocalSpiToIkeSaRecordMap = new LongSparseArray<>(3); - mRemoteSpiToChildSessionMap = new SparseArray<>(); - - mContext = context; - mIpSecManager = ipSecManager; - - mUserCbExecutor = userCbExecutor; - mIkeSessionCallback = ikeSessionCallback; - - mFirstChildSessionOptions = firstChildOptions; - mFirstChildCallbacks = firstChildSessionCallback; - registerChildSessionCallback(firstChildOptions, firstChildSessionCallback, true); - - addState(mInitial); - addState(mCreateIkeLocalIkeInit); - addState(mCreateIkeLocalIkeAuth); - addState(mCreateIkeLocalIkeAuthInEap); - addState(mCreateIkeLocalIkeAuthPostEap); - addState(mIdle); - addState(mChildProcedureOngoing); - addState(mReceiving); - addState(mRekeyIkeLocalCreate); - addState(mSimulRekeyIkeLocalCreate, mRekeyIkeLocalCreate); - addState(mSimulRekeyIkeLocalDeleteRemoteDelete); - addState(mSimulRekeyIkeLocalDelete, mSimulRekeyIkeLocalDeleteRemoteDelete); - addState(mSimulRekeyIkeRemoteDelete, mSimulRekeyIkeLocalDeleteRemoteDelete); - addState(mRekeyIkeLocalDelete); - addState(mRekeyIkeRemoteDelete); - addState(mDeleteIkeLocalDelete); - - setInitialState(mInitial); - mScheduler = - new IkeLocalRequestScheduler( - localReq -> { - sendMessageAtFrontOfQueue(CMD_EXECUTE_LOCAL_REQ, localReq); - }); - - start(); - } - - /** Construct an instance of IkeSessionStateMachine. */ - public IkeSessionStateMachine( - Looper looper, - Context context, - IpSecManager ipSecManager, - IkeSessionOptions ikeOptions, - ChildSessionOptions firstChildOptions, - Executor userCbExecutor, - IkeSessionCallback ikeSessionCallback, - ChildSessionCallback firstChildSessionCallback) { - this( - looper, - context, - ipSecManager, - ikeOptions, - firstChildOptions, - userCbExecutor, - ikeSessionCallback, - firstChildSessionCallback, - new IkeEapAuthenticatorFactory()); - } - - private boolean hasChildSessionCallback(ChildSessionCallback callback) { - synchronized (mChildCbToSessions) { - return mChildCbToSessions.containsKey(callback); - } - } - - /** - * Synchronously builds and registers a child session. - * - * <p>Setup of the child state machines MUST be done in two stages to ensure that if an external - * caller calls openChildSession and then calls closeChildSession before the state machine has - * gotten a chance to negotiate the sessions, a valid callback mapping exists (and does not - * throw an exception that the callback was not found). - * - * <p>In the edge case where a child creation fails, and deletes itself, all pending requests - * will no longer find the session in the map. Assume it has errored/failed, and skip/ignore. - * This is safe, as closeChildSession() (previously) validated that the callback was registered. - */ - @VisibleForTesting - void registerChildSessionCallback( - ChildSessionOptions childOptions, - ChildSessionCallback callbacks, - boolean isFirstChild) { - synchronized (mChildCbToSessions) { - if (!isFirstChild && getCurrentState() == null) { - throw new IllegalStateException( - "Request rejected because IKE Session is being closed. "); - } - - mChildCbToSessions.put( - callbacks, - ChildSessionStateMachineFactory.makeChildSessionStateMachine( - getHandler().getLooper(), - mContext, - childOptions, - mUserCbExecutor, - callbacks, - new ChildSessionSmCallback())); - } - } - - /** Initiates IKE setup procedure. */ - public void openSession() { - sendMessage(CMD_LOCAL_REQUEST_CREATE_IKE, new LocalRequest(CMD_LOCAL_REQUEST_CREATE_IKE)); - } - - /** Schedules a Create Child procedure. */ - public void openChildSession( - ChildSessionOptions childSessionOptions, ChildSessionCallback childSessionCallback) { - if (childSessionCallback == null) { - throw new IllegalArgumentException("Child Session Callback must be provided"); - } - - if (hasChildSessionCallback(childSessionCallback)) { - throw new IllegalArgumentException("Child Session Callback handle already registered"); - } - - registerChildSessionCallback( - childSessionOptions, childSessionCallback, false /*isFirstChild*/); - sendMessage( - CMD_LOCAL_REQUEST_CREATE_CHILD, - new ChildLocalRequest( - CMD_LOCAL_REQUEST_CREATE_CHILD, childSessionCallback, childSessionOptions)); - } - - /** Schedules a Delete Child procedure. */ - public void closeChildSession(ChildSessionCallback childSessionCallback) { - if (childSessionCallback == null) { - throw new IllegalArgumentException("Child Session Callback must be provided"); - } - - if (!hasChildSessionCallback(childSessionCallback)) { - throw new IllegalArgumentException("Child Session Callback handle not registered"); - } - - sendMessage( - CMD_LOCAL_REQUEST_DELETE_CHILD, - new ChildLocalRequest(CMD_LOCAL_REQUEST_DELETE_CHILD, childSessionCallback, null)); - } - - /** Initiates Delete IKE procedure. */ - public void closeSession() { - sendMessage(CMD_LOCAL_REQUEST_DELETE_IKE, new LocalRequest(CMD_LOCAL_REQUEST_DELETE_IKE)); - } - - /** Forcibly close IKE Session. */ - public void killSession() { - // TODO: b/142977160 Support closing IKE Sesison immediately. - } - - private void scheduleRekeySession(LocalRequest rekeyRequest) { - // TODO: Make rekey timeout fuzzy - sendMessageDelayed(CMD_LOCAL_REQUEST_REKEY_IKE, rekeyRequest, SA_SOFT_LIFETIME_MS); - } - - private void scheduleRetry(LocalRequest localRequest) { - sendMessageDelayed(localRequest.procedureType, localRequest, RETRY_INTERVAL_MS); - } - - // TODO: Support initiating Delete IKE exchange when IKE SA expires - - // TODO: Add interfaces to initiate IKE exchanges. - - /** - * This class is for handling temporary failure. - * - * <p>Receiving a TEMPORARY_FAILURE is caused by a temporary condition. IKE Session should be - * closed if it continues to receive this error after several minutes. - */ - @VisibleForTesting - class TempFailureHandler extends Handler { - private static final int TEMP_FAILURE_RETRY_TIMEOUT = 1; - - private boolean mTempFailureReceived = false; - - TempFailureHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (msg.what == TEMP_FAILURE_RETRY_TIMEOUT) { - IOException error = - new IOException( - "Kept receiving TEMPORARY_FAILURE error. State information is out" - + " of sync."); - mUserCbExecutor.execute( - () -> { - mIkeSessionCallback.onClosedExceptionally( - new IkeInternalException(error)); - }); - loge("Fatal error", error); - - closeAllSaRecords(false /*expectSaClosed*/); - quitNow(); - } else { - logWtf("Unknown message.what: " + msg.what); - } - } - - /** Schedule retry when a request got rejected by TEMPORARY_FAILURE. */ - public void handleTempFailure(LocalRequest localRequest) { - logd( - "TempFailureHandler: Receive TEMPORARY FAILURE. Reschedule request: " - + localRequest.procedureType); - - // TODO: Support customized delay time when this is a rekey request and SA is going to - // expire soon. - scheduleRetry(localRequest); - - if (!mTempFailureReceived) { - sendEmptyMessageDelayed(TEMP_FAILURE_RETRY_TIMEOUT, TEMP_FAILURE_RETRY_TIMEOUT_MS); - mTempFailureReceived = true; - } - } - - /** Stop tracking temporary condition when request was not rejected by TEMPORARY_FAILURE. */ - public void reset() { - logd("TempFailureHandler: Reset Temporary failure retry timeout"); - removeMessages(TEMP_FAILURE_RETRY_TIMEOUT); - mTempFailureReceived = false; - } - } - /** - * This class represents a reserved IKE SPI. - * - * <p>This class is created to avoid assigning same SPI to the same address. - * - * <p>Objects of this type are used to track reserved IKE SPI to avoid SPI collision. They can - * be obtained by calling {@link #allocateSecurityParameterIndex()} and must be released by - * calling {@link #close()} when they are no longer needed. - * - * <p>This class follows the pattern of {@link IpSecManager.SecurityParameterIndex}. - * - * <p>TODO: Move this class to a central place, like IkeManager. - */ - public static final class IkeSecurityParameterIndex implements AutoCloseable { - // Remember assigned IKE SPIs to avoid SPI collision. - private static final Set<Pair<InetAddress, Long>> sAssignedIkeSpis = new HashSet<>(); - private static final int MAX_ASSIGN_IKE_SPI_ATTEMPTS = 100; - private static final SecureRandom IKE_SPI_RANDOM = new SecureRandom(); - - private final InetAddress mSourceAddress; - private final long mSpi; - private final CloseGuard mCloseGuard = CloseGuard.get(); - - private IkeSecurityParameterIndex(InetAddress sourceAddress, long spi) { - mSourceAddress = sourceAddress; - mSpi = spi; - mCloseGuard.open("close"); - } - - /** - * Get a new IKE SPI and maintain the reservation. - * - * @return an instance of IkeSecurityParameterIndex. - */ - public static IkeSecurityParameterIndex allocateSecurityParameterIndex( - InetAddress sourceAddress) throws IOException { - // TODO: Create specific Exception for SPI assigning error. - - for (int i = 0; i < MAX_ASSIGN_IKE_SPI_ATTEMPTS; i++) { - long spi = IKE_SPI_RANDOM.nextLong(); - // Zero value can only be used in the IKE responder SPI field of an IKE INIT - // request. - if (spi != 0L - && sAssignedIkeSpis.add(new Pair<InetAddress, Long>(sourceAddress, spi))) { - return new IkeSecurityParameterIndex(sourceAddress, spi); - } - } - - throw new IOException("Failed to generate IKE SPI."); - } - - /** - * Get a new IKE SPI and maintain the reservation. - * - * @return an instance of IkeSecurityParameterIndex. - */ - public static IkeSecurityParameterIndex allocateSecurityParameterIndex( - InetAddress sourceAddress, long requestedSpi) throws IOException { - if (sAssignedIkeSpis.add(new Pair<InetAddress, Long>(sourceAddress, requestedSpi))) { - return new IkeSecurityParameterIndex(sourceAddress, requestedSpi); - } - - throw new IOException( - "Failed to generate IKE SPI for " - + requestedSpi - + " with source address " - + sourceAddress.getHostAddress()); - } - - /** - * Get the underlying SPI held by this object. - * - * @return the underlying IKE SPI. - */ - public long getSpi() { - return mSpi; - } - - /** Release an SPI that was previously reserved. */ - @Override - public void close() { - sAssignedIkeSpis.remove(new Pair<InetAddress, Long>(mSourceAddress, mSpi)); - mCloseGuard.close(); - } - - /** Check that the IkeSecurityParameterIndex was closed properly. */ - @Override - protected void finalize() throws Throwable { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - } - - // TODO: Add methods for building and validating general Informational packet. - - @VisibleForTesting - void addIkeSaRecord(IkeSaRecord record) { - mLocalSpiToIkeSaRecordMap.put(record.getLocalSpi(), record); - - // In IKE_INIT exchange, local SPI was registered with this IkeSessionStateMachine before - // IkeSaRecord is created. Calling this method at the end of exchange will double-register - // the SPI but it is safe because the key and value are not changed. - mIkeSocket.registerIke(record.getLocalSpi(), this); - - scheduleRekeySession(record.getFutureRekeyEvent()); - } - - @VisibleForTesting - void removeIkeSaRecord(IkeSaRecord record) { - mIkeSocket.unregisterIke(record.getLocalSpi()); - mLocalSpiToIkeSaRecordMap.remove(record.getLocalSpi()); - } - - /** - * Receive IKE packet from remote server. - * - * <p>This method is called synchronously from IkeSocket. It proxies the synchronous call as an - * asynchronous job to the IkeSessionStateMachine handler. - * - * @param ikeHeader the decoded IKE header. - * @param ikePacketBytes the byte array of the entire received IKE packet. - */ - public void receiveIkePacket(IkeHeader ikeHeader, byte[] ikePacketBytes) { - sendMessage(CMD_RECEIVE_IKE_PACKET, new ReceivedIkePacket(ikeHeader, ikePacketBytes)); - } - - /** - * ReceivedIkePacket is a package private data container consists of decoded IkeHeader and - * encoded IKE packet in a byte array. - */ - static class ReceivedIkePacket { - /** Decoded IKE header */ - public final IkeHeader ikeHeader; - /** Entire encoded IKE message including IKE header */ - public final byte[] ikePacketBytes; - - ReceivedIkePacket(IkeHeader ikeHeader, byte[] ikePacketBytes) { - this.ikeHeader = ikeHeader; - this.ikePacketBytes = ikePacketBytes; - } - } - - /** Class to group parameters for negotiating the first Child SA. */ - private static class FirstChildNegotiationData { - public final ChildSessionOptions childSessionOptions; - public final ChildSessionCallback childSessionCallback; - public final List<IkePayload> reqPayloads; - public final List<IkePayload> respPayloads; - - FirstChildNegotiationData( - ChildSessionOptions childSessionOptions, - ChildSessionCallback childSessionCallback, - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads) { - this.childSessionOptions = childSessionOptions; - this.childSessionCallback = childSessionCallback; - this.reqPayloads = reqPayloads; - this.respPayloads = respPayloads; - } - } - - /** Class to group parameters for building an outbound message for ChildSessions. */ - private static class ChildOutboundData { - @ExchangeType public final int exchangeType; - public final boolean isResp; - public final List<IkePayload> payloadList; - public final ChildSessionStateMachine childSession; - - ChildOutboundData( - @ExchangeType int exchangeType, - boolean isResp, - List<IkePayload> payloadList, - ChildSessionStateMachine childSession) { - this.exchangeType = exchangeType; - this.isResp = isResp; - this.payloadList = payloadList; - this.childSession = childSession; - } - } - - /** Callback for ChildSessionStateMachine to notify IkeSessionStateMachine. */ - @VisibleForTesting - class ChildSessionSmCallback implements ChildSessionStateMachine.IChildSessionSmCallback { - @Override - public void onChildSaCreated(int remoteSpi, ChildSessionStateMachine childSession) { - mRemoteSpiToChildSessionMap.put(remoteSpi, childSession); - } - - @Override - public void onChildSaDeleted(int remoteSpi) { - mRemoteSpiToChildSessionMap.remove(remoteSpi); - } - - @Override - public void scheduleLocalRequest(ChildLocalRequest futureRequest, long delayedTime) { - sendMessageDelayed(futureRequest.procedureType, futureRequest, delayedTime); - } - - @Override - public void scheduleRetryLocalRequest(ChildLocalRequest childRequest) { - scheduleRetry(childRequest); - } - - @Override - public void onOutboundPayloadsReady( - @ExchangeType int exchangeType, - boolean isResp, - List<IkePayload> payloadList, - ChildSessionStateMachine childSession) { - sendMessage( - CMD_OUTBOUND_CHILD_PAYLOADS_READY, - new ChildOutboundData(exchangeType, isResp, payloadList, childSession)); - } - - @Override - public void onProcedureFinished(ChildSessionStateMachine childSession) { - if (getHandler() == null) { - // If the state machine has quit (because IKE Session is being closed), do not send - // any message. - return; - } - - sendMessage(CMD_CHILD_PROCEDURE_FINISHED, childSession); - } - - @Override - public void onChildSessionClosed(ChildSessionCallback userCallbacks) { - synchronized (mChildCbToSessions) { - mChildCbToSessions.remove(userCallbacks); - } - } - - @Override - public void onFatalIkeSessionError(boolean needsNotifyRemote) { - // TODO: If needsNotifyRemote is true, send a Delete IKE request and then kill the IKE - // Session. Otherwise, directly kill the IKE Session. - } - } - - /** Top level state for handling uncaught exceptions for all subclasses. */ - abstract class ExceptionHandler extends ExceptionHandlerBase { - @Override - protected void cleanUpAndQuit(RuntimeException e) { - // Clean up all SaRecords. - closeAllSaRecords(false /*expectSaClosed*/); - - mUserCbExecutor.execute( - () -> { - mIkeSessionCallback.onClosedExceptionally(new IkeInternalException(e)); - }); - logWtf("Unexpected exception in " + getCurrentState().getName(), e); - quitNow(); - } - - @Override - protected String getCmdString(int cmd) { - return CMD_TO_STR.get(cmd); - } - } - - /** Called when this StateMachine quits. */ - @Override - protected void onQuitting() { - // Clean up all SaRecords. - closeAllSaRecords(true /*expectSaClosed*/); - - synchronized (mChildCbToSessions) { - for (ChildSessionStateMachine child : mChildCbToSessions.values()) { - // Fire asynchronous call for Child Sessions to do cleanup and remove itself - // from the map. - child.killSession(); - } - } - - if (mIkeSocket == null) return; - mIkeSocket.releaseReference(this); - } - - private void closeAllSaRecords(boolean expectSaClosed) { - closeIkeSaRecord(mCurrentIkeSaRecord, expectSaClosed); - closeIkeSaRecord(mLocalInitNewIkeSaRecord, expectSaClosed); - closeIkeSaRecord(mRemoteInitNewIkeSaRecord, expectSaClosed); - - mCurrentIkeSaRecord = null; - mLocalInitNewIkeSaRecord = null; - mRemoteInitNewIkeSaRecord = null; - } - - private void closeIkeSaRecord(IkeSaRecord ikeSaRecord, boolean expectSaClosed) { - if (ikeSaRecord == null) return; - - removeIkeSaRecord(ikeSaRecord); - ikeSaRecord.close(); - - if (!expectSaClosed) return; - - logWtf( - "IkeSaRecord with local SPI: " - + ikeSaRecord.getLocalSpi() - + " is not correctly closed."); - } - - private void handleIkeFatalError(Exception error) { - IkeException ikeException = - error instanceof IkeException - ? (IkeException) error - : new IkeInternalException(error); - - // Clean up all SaRecords. - closeAllSaRecords(false /*expectSaClosed*/); - mUserCbExecutor.execute( - () -> { - mIkeSessionCallback.onClosedExceptionally(ikeException); - }); - loge("IKE Session fatal error in " + getCurrentState().getName(), ikeException); - - quitNow(); - } - - /** Initial state of IkeSessionStateMachine. */ - class Initial extends ExceptionHandler { - @Override - public void enterState() { - try { - mRemoteAddress = mIkeSessionOptions.getServerAddress(); - - boolean isIpv4 = mRemoteAddress instanceof Inet4Address; - FileDescriptor sock = - Os.socket( - isIpv4 ? OsConstants.AF_INET : OsConstants.AF_INET6, - OsConstants.SOCK_DGRAM, - OsConstants.IPPROTO_UDP); - Os.connect(sock, mRemoteAddress, IkeSocket.IKE_SERVER_PORT); - InetSocketAddress localAddr = (InetSocketAddress) Os.getsockname(sock); - mLocalAddress = localAddr.getAddress(); - mLocalPort = localAddr.getPort(); - Os.close(sock); - - mIkeSocket = - IkeSocket.getIkeSocket( - mIkeSessionOptions.getUdpEncapsulationSocket(), - IkeSessionStateMachine.this); - } catch (ErrnoException | SocketException e) { - handleIkeFatalError(e); - } - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_LOCAL_REQUEST_CREATE_IKE: - transitionTo(mCreateIkeLocalIkeInit); - return HANDLED; - case CMD_FORCE_TRANSITION: - transitionTo((State) message.obj); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - /** - * Idle represents a state when there is no ongoing IKE exchange affecting established IKE SA. - */ - class Idle extends LocalRequestQueuer { - @Override - public void enterState() { - mScheduler.readyForNextProcedure(); - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_RECEIVE_IKE_PACKET: - deferMessage(message); - transitionTo(mReceiving); - return HANDLED; - - case CMD_FORCE_TRANSITION: // Testing command - transitionTo((State) message.obj); - return HANDLED; - - case CMD_EXECUTE_LOCAL_REQ: - executeLocalRequest((LocalRequest) message.obj, message); - return HANDLED; - - default: - // Queue local requests, and trigger next procedure - if (isLocalRequest(message.what)) { - handleLocalRequest(message.what, (LocalRequest) message.obj); - - // Synchronously calls through to the scheduler callback, which will - // post the CMD_EXECUTE_LOCAL_REQ to the front of the queue, ensuring - // it is always the next request processed. - mScheduler.readyForNextProcedure(); - return HANDLED; - } - return NOT_HANDLED; - } - } - - private void executeLocalRequest(LocalRequest req, Message message) { - switch (req.procedureType) { - case CMD_LOCAL_REQUEST_REKEY_IKE: - transitionTo(mRekeyIkeLocalCreate); - break; - case CMD_LOCAL_REQUEST_DELETE_IKE: - transitionTo(mDeleteIkeLocalDelete); - break; - case CMD_LOCAL_REQUEST_CREATE_CHILD: // fallthrough - case CMD_LOCAL_REQUEST_REKEY_CHILD: // fallthrough - case CMD_LOCAL_REQUEST_DELETE_CHILD: - deferMessage(message); - transitionTo(mChildProcedureOngoing); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Invalid local request procedure type: " + req.procedureType)); - } - } - } - - /** - * Gets IKE exchange subtype of a inbound IKE request message. - * - * <p>Knowing IKE exchange subtype of a inbound IKE request message helps IkeSessionStateMachine - * to validate this request using the specific rule. - * - * <p>It is not allowed to obtain exchange subtype from a inbound response message for two - * reasons. Firstly, the exchange subtype of a response message is the same with its - * corresponding request message. Secondly, trying to get the exchange subtype from a response - * message will easily fail when the response message contains only error notification payloads. - * - * @param ikeMessage inbound request IKE message to check. - * @return IKE exchange subtype. - */ - @IkeExchangeSubType - private static int getIkeExchangeSubType(IkeMessage ikeMessage) { - IkeHeader ikeHeader = ikeMessage.ikeHeader; - if (ikeHeader.isResponseMsg) { - throw new IllegalStateException("IKE Exchange subtype invalid for response messages."); - } - - switch (ikeHeader.exchangeType) { - // DPD omitted - should never be handled via handleRequestIkeMessage() - case IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT: - return IKE_EXCHANGE_SUBTYPE_IKE_INIT; - case IkeHeader.EXCHANGE_TYPE_IKE_AUTH: - return IKE_EXCHANGE_SUBTYPE_IKE_AUTH; - case IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA: - // It is guaranteed in the decoding process that SA Payload has at least one SA - // Proposal. Since Rekey IKE and Create Child (both initial creation and rekey - // creation) will cause a collision, although the RFC 7296 does not prohibit one SA - // Payload to contain both IKE proposals and Child proposals, containing two types - // does not make sense. IKE libary will reply according to the first SA Proposal - // type and ignore the other type. - IkeSaPayload saPayload = - ikeMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - if (saPayload == null) { - return IKE_EXCHANGE_SUBTYPE_INVALID; - } - - // If the received message has both SA(IKE) Payload and Notify-Rekey Payload, IKE - // library will treat it as a Rekey IKE request and ignore the Notify-Rekey - // Payload to provide better interoperability. - if (saPayload.proposalList.get(0).protocolId == IkePayload.PROTOCOL_ID_IKE) { - return IKE_EXCHANGE_SUBTYPE_REKEY_IKE; - } - - // If a Notify-Rekey Payload is found, this message is for rekeying a Child SA. - List<IkeNotifyPayload> notifyPayloads = - ikeMessage.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - - // It is checked during decoding that there is at most one Rekey notification - // payload. - for (IkeNotifyPayload notifyPayload : notifyPayloads) { - if (notifyPayload.notifyType == IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA) { - return IKE_EXCHANGE_SUBTYPE_REKEY_CHILD; - } - } - - return IKE_EXCHANGE_SUBTYPE_CREATE_CHILD; - case IkeHeader.EXCHANGE_TYPE_INFORMATIONAL: - List<IkeDeletePayload> deletePayloads = - ikeMessage.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_DELETE, IkeDeletePayload.class); - - // If no Delete payload was found, this request is a generic informational request. - if (deletePayloads.isEmpty()) return IKE_EXCHANGE_SUBTYPE_GENERIC_INFO; - - // IKEv2 protocol does not clearly disallow to have both a Delete IKE payload and a - // Delete Child payload in one IKE message. In this case, IKE library will only - // respond to the Delete IKE payload. - for (IkeDeletePayload deletePayload : deletePayloads) { - if (deletePayload.protocolId == IkePayload.PROTOCOL_ID_IKE) { - return IKE_EXCHANGE_SUBTYPE_DELETE_IKE; - } - } - return IKE_EXCHANGE_SUBTYPE_DELETE_CHILD; - default: - throw new IllegalStateException( - "Unrecognized exchange type in the validated IKE header: " - + ikeHeader.exchangeType); - } - } - - // Sends the provided IkeMessage using the current IKE SA record - @VisibleForTesting - void sendEncryptedIkeMessage(IkeMessage msg) { - sendEncryptedIkeMessage(mCurrentIkeSaRecord, msg); - } - - // Sends the provided IkeMessage using the provided IKE SA record - @VisibleForTesting - void sendEncryptedIkeMessage(IkeSaRecord ikeSaRecord, IkeMessage msg) { - byte[][] packetList = - msg.encryptAndEncode( - mIkeIntegrity, - mIkeCipher, - ikeSaRecord, - mSupportFragment, - DEFAULT_FRAGMENT_SIZE); - for (byte[] packet : packetList) { - mIkeSocket.sendIkePacket(packet, mRemoteAddress); - } - if (msg.ikeHeader.isResponseMsg) { - ikeSaRecord.updateLastSentRespAllPackets(Arrays.asList(packetList)); - } - } - - // Builds and sends IKE-level error notification response on the provided IKE SA record - @VisibleForTesting - void buildAndSendErrorNotificationResponse( - IkeSaRecord ikeSaRecord, int messageId, @ErrorType int errorType) { - IkeNotifyPayload error = new IkeNotifyPayload(errorType); - buildAndSendNotificationResponse(ikeSaRecord, messageId, error); - } - - // Builds and sends error notification response on the provided IKE SA record - @VisibleForTesting - void buildAndSendNotificationResponse( - IkeSaRecord ikeSaRecord, int messageId, IkeNotifyPayload notifyPayload) { - IkeMessage msg = - buildEncryptedNotificationMessage( - ikeSaRecord, - new IkeInformationalPayload[] {notifyPayload}, - EXCHANGE_TYPE_INFORMATIONAL, - true /*isResponse*/, - messageId); - - sendEncryptedIkeMessage(ikeSaRecord, msg); - } - - // Builds an Encrypted IKE Informational Message for the given IkeInformationalPayload using the - // current IKE SA record. - @VisibleForTesting - IkeMessage buildEncryptedInformationalMessage( - IkeInformationalPayload[] payloads, boolean isResponse, int messageId) { - return buildEncryptedInformationalMessage( - mCurrentIkeSaRecord, payloads, isResponse, messageId); - } - - // Builds an Encrypted IKE Informational Message for the given IkeInformationalPayload using the - // provided IKE SA record. - @VisibleForTesting - IkeMessage buildEncryptedInformationalMessage( - IkeSaRecord saRecord, - IkeInformationalPayload[] payloads, - boolean isResponse, - int messageId) { - return buildEncryptedNotificationMessage( - saRecord, payloads, IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, isResponse, messageId); - } - - // Builds an Encrypted IKE Message for the given IkeInformationalPayload using the provided IKE - // SA record and exchange type. - @VisibleForTesting - IkeMessage buildEncryptedNotificationMessage( - IkeSaRecord saRecord, - IkeInformationalPayload[] payloads, - @ExchangeType int exchangeType, - boolean isResponse, - int messageId) { - IkeHeader header = - new IkeHeader( - saRecord.getInitiatorSpi(), - saRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - exchangeType, - isResponse /*isResponseMsg*/, - saRecord.isLocalInit /*fromIkeInitiator*/, - messageId); - - return new IkeMessage(header, Arrays.asList(payloads)); - } - - private abstract class LocalRequestQueuer extends ExceptionHandler { - /** - * Reroutes all local requests to the scheduler - * - * @param requestVal The command value of the request - * @param req The instance of the LocalRequest to be queued. - */ - protected void handleLocalRequest(int requestVal, LocalRequest req) { - if (req.isCancelled()) return; - - switch (requestVal) { - case CMD_LOCAL_REQUEST_DELETE_IKE: - mScheduler.addRequestAtFront(req); - return; - - case CMD_LOCAL_REQUEST_REKEY_IKE: // Fallthrough - case CMD_LOCAL_REQUEST_INFO: - mScheduler.addRequest(req); - return; - - case CMD_LOCAL_REQUEST_CREATE_CHILD: // Fallthrough - case CMD_LOCAL_REQUEST_REKEY_CHILD: // Fallthrough - case CMD_LOCAL_REQUEST_DELETE_CHILD: - ChildLocalRequest childReq = (ChildLocalRequest) req; - if (childReq.procedureType != requestVal) { - cleanUpAndQuit( - new IllegalArgumentException( - "ChildLocalRequest procedure type was invalid")); - } - mScheduler.addRequest(childReq); - return; - - default: - cleanUpAndQuit( - new IllegalStateException( - "Unknown local request passed to handleLocalRequest")); - } - } - - /** Check if received signal is a local request. */ - protected boolean isLocalRequest(int msgWhat) { - if ((msgWhat >= CMD_IKE_LOCAL_REQUEST_BASE - && msgWhat < CMD_IKE_LOCAL_REQUEST_BASE + CMD_CATEGORY_SIZE) - || (msgWhat >= CMD_CHILD_LOCAL_REQUEST_BASE - && msgWhat < CMD_CHILD_LOCAL_REQUEST_BASE + CMD_CATEGORY_SIZE)) { - return true; - } - return false; - } - } - - /** - * Base state defines common behaviours when receiving an IKE packet. - * - * <p>State that represents an ongoing IKE procedure MUST extend BusyState to handle received - * IKE packet. Idle state will defer the received packet to a BusyState to process it. - */ - private abstract class BusyState extends LocalRequestQueuer { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_RECEIVE_IKE_PACKET: - handleReceivedIkePacket(message); - return HANDLED; - - case CMD_FORCE_TRANSITION: - transitionTo((State) message.obj); - return HANDLED; - - case CMD_EXECUTE_LOCAL_REQ: - logWtf("Invalid execute local request command in non-idle state"); - return NOT_HANDLED; - - case CMD_RETRANSMIT: - triggerRetransmit(); - return HANDLED; - - default: - // Queue local requests, and trigger next procedure - if (isLocalRequest(message.what)) { - handleLocalRequest(message.what, (LocalRequest) message.obj); - return HANDLED; - } - return NOT_HANDLED; - } - } - - /** - * Handler for retransmission timer firing - * - * <p>By default, the trigger is logged and dropped. States that have a retransmitter should - * override this function, and proxy the call to Retransmitter.retransmit() - */ - protected void triggerRetransmit() { - logWtf("Retransmission trigger dropped in state: " + this.getClass().getSimpleName()); - } - - protected IkeSaRecord getIkeSaRecordForPacket(IkeHeader ikeHeader) { - if (ikeHeader.fromIkeInitiator) { - return mLocalSpiToIkeSaRecordMap.get(ikeHeader.ikeResponderSpi); - } else { - return mLocalSpiToIkeSaRecordMap.get(ikeHeader.ikeInitiatorSpi); - } - } - - protected void handleReceivedIkePacket(Message message) { - // TODO: b/138411550 Notify subclasses when discarding a received packet. Receiving MUST - // go back to Idle state in this case. - - String methodTag = "handleReceivedIkePacket: "; - - ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; - IkeHeader ikeHeader = receivedIkePacket.ikeHeader; - byte[] ikePacketBytes = receivedIkePacket.ikePacketBytes; - IkeSaRecord ikeSaRecord = getIkeSaRecordForPacket(ikeHeader); - - String msgDirection = ikeHeader.isResponseMsg ? "response" : "request"; - - // Drop packets that we don't have an SA for: - if (ikeSaRecord == null) { - // TODO: Print a summary of the IKE message (perhaps the IKE header) - cleanUpAndQuit( - new IllegalStateException( - "Received an IKE " - + msgDirection - + "but found no matching SA for it")); - return; - } - - logd( - methodTag - + "Received an " - + ikeHeader.getBasicInfoString() - + " on IKE SA with local SPI: " - + ikeSaRecord.getLocalSpi() - + ". Packet size: " - + ikePacketBytes.length); - - if (ikeHeader.isResponseMsg) { - int expectedMsgId = ikeSaRecord.getLocalRequestMessageId(); - if (expectedMsgId - 1 == ikeHeader.messageId) { - logd(methodTag + "Received re-transmitted response. Discard it."); - return; - } - - DecodeResult decodeResult = - IkeMessage.decode( - expectedMsgId, - mIkeIntegrity, - mIkeCipher, - ikeSaRecord, - ikeHeader, - ikePacketBytes, - ikeSaRecord.getCollectedFragments(true /*isResp*/)); - switch (decodeResult.status) { - case DECODE_STATUS_OK: - ikeSaRecord.incrementLocalRequestMessageId(); - ikeSaRecord.resetCollectedFragments(true /*isResp*/); - - DecodeResultOk resultOk = (DecodeResultOk) decodeResult; - if (isTempFailure(resultOk.ikeMessage)) { - handleTempFailure(); - } else { - mTempFailHandler.reset(); - } - - handleResponseIkeMessage(resultOk.ikeMessage); - break; - case DECODE_STATUS_PARTIAL: - ikeSaRecord.updateCollectedFragments( - (DecodeResultPartial) decodeResult, true /*isResp*/); - break; - case DECODE_STATUS_PROTECTED_ERROR: - IkeException ikeException = ((DecodeResultError) decodeResult).ikeException; - logi(methodTag + "Protected error", ikeException); - - ikeSaRecord.incrementLocalRequestMessageId(); - ikeSaRecord.resetCollectedFragments(true /*isResp*/); - - handleResponseGenericProcessError( - ikeSaRecord, - new InvalidSyntaxException( - "Generic processing error in the received response", - ikeException)); - break; - case DECODE_STATUS_UNPROTECTED_ERROR: - logi( - methodTag - + "Message authentication or decryption failed on received" - + " response. Discard it", - ((DecodeResultError) decodeResult).ikeException); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Unrecognized decoding status: " + decodeResult.status)); - } - - } else { - int expectedMsgId = ikeSaRecord.getRemoteRequestMessageId(); - if (expectedMsgId - 1 == ikeHeader.messageId) { - - if (ikeSaRecord.isRetransmittedRequest(ikePacketBytes)) { - logd("Received re-transmitted request. Retransmitting response"); - - for (byte[] packet : ikeSaRecord.getLastSentRespAllPackets()) { - mIkeSocket.sendIkePacket(packet, mRemoteAddress); - } - - // TODO:Support resetting remote rekey delete timer. - } else { - logi(methodTag + "Received response with invalid message ID. Discard it."); - } - } else { - DecodeResult decodeResult = - IkeMessage.decode( - expectedMsgId, - mIkeIntegrity, - mIkeCipher, - ikeSaRecord, - ikeHeader, - ikePacketBytes, - ikeSaRecord.getCollectedFragments(false /*isResp*/)); - switch (decodeResult.status) { - case DECODE_STATUS_OK: - ikeSaRecord.incrementRemoteRequestMessageId(); - ikeSaRecord.resetCollectedFragments(false /*isResp*/); - - DecodeResultOk resultOk = (DecodeResultOk) decodeResult; - IkeMessage ikeMessage = resultOk.ikeMessage; - ikeSaRecord.updateLastReceivedReqFirstPacket(resultOk.firstPacket); - - // Handle DPD here. - if (ikeMessage.isDpdRequest()) { - logd(methodTag + "Received DPD request"); - IkeMessage dpdResponse = - buildEncryptedInformationalMessage( - ikeSaRecord, - new IkeInformationalPayload[] {}, - true, - ikeHeader.messageId); - sendEncryptedIkeMessage(ikeSaRecord, dpdResponse); - break; - } - - int ikeExchangeSubType = getIkeExchangeSubType(ikeMessage); - logd( - methodTag - + "Request exchange subtype: " - + EXCHANGE_SUBTYPE_TO_STRING.get(ikeExchangeSubType)); - - if (ikeExchangeSubType == IKE_EXCHANGE_SUBTYPE_INVALID - || ikeExchangeSubType == IKE_EXCHANGE_SUBTYPE_IKE_INIT - || ikeExchangeSubType == IKE_EXCHANGE_SUBTYPE_IKE_AUTH) { - - // Reply with INVALID_SYNTAX and close IKE Session. - buildAndSendErrorNotificationResponse( - mCurrentIkeSaRecord, - ikeHeader.messageId, - ERROR_TYPE_INVALID_SYNTAX); - handleIkeFatalError( - new InvalidSyntaxException( - "Cannot handle message with invalid or unexpected" - + " IkeExchangeSubType: " - + ikeExchangeSubType)); - return; - } - handleRequestIkeMessage(ikeMessage, ikeExchangeSubType, message); - break; - case DECODE_STATUS_PARTIAL: - ikeSaRecord.updateCollectedFragments( - (DecodeResultPartial) decodeResult, false /*isResp*/); - break; - case DECODE_STATUS_PROTECTED_ERROR: - DecodeResultProtectedError resultError = - (DecodeResultProtectedError) decodeResult; - - IkeException ikeException = resultError.ikeException; - logi(methodTag + "Protected error", resultError.ikeException); - - ikeSaRecord.incrementRemoteRequestMessageId(); - ikeSaRecord.resetCollectedFragments(false /*isResp*/); - - ikeSaRecord.updateLastReceivedReqFirstPacket(resultError.firstPacket); - - // IkeException MUST be already wrapped into an IkeProtocolException - handleRequestGenericProcessError( - ikeSaRecord, - ikeHeader.messageId, - (IkeProtocolException) ikeException); - break; - case DECODE_STATUS_UNPROTECTED_ERROR: - logi( - methodTag - + "Message authentication or decryption failed on" - + " received request. Discard it", - ((DecodeResultError) decodeResult).ikeException); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Unrecognized decoding status: " - + decodeResult.status)); - } - } - } - } - - private boolean isTempFailure(IkeMessage message) { - List<IkeNotifyPayload> notifyPayloads = - message.getPayloadListForType(PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - - for (IkeNotifyPayload notify : notifyPayloads) { - if (notify.notifyType == ERROR_TYPE_TEMPORARY_FAILURE) { - return true; - } - } - return false; - } - - protected void handleTempFailure() { - // Log and close IKE Session due to unexpected TEMPORARY_FAILURE. This error should - // only occur during CREATE_CHILD_SA exchange. - handleIkeFatalError( - new InvalidSyntaxException("Received unexpected TEMPORARY_FAILURE")); - - // States that accept a TEMPORARY MUST override this method to schedule a retry. - } - - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - // Subclasses MUST override it if they care - cleanUpAndQuit( - new IllegalStateException( - "Do not support handling an encrypted request: " + ikeExchangeSubType)); - } - - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - // Subclasses MUST override it if they care - cleanUpAndQuit( - new IllegalStateException("Do not support handling an encrypted response")); - } - - /** - * Method for handling generic processing error of a request. - * - * <p>A generic processing error is usally syntax error, unsupported critical payload error - * and major version error. IKE SA that should reply with corresponding error notifications - */ - protected void handleRequestGenericProcessError( - IkeSaRecord ikeSaRecord, int messageId, IkeProtocolException exception) { - IkeNotifyPayload errNotify = exception.buildNotifyPayload(); - sendEncryptedIkeMessage( - ikeSaRecord, - buildEncryptedInformationalMessage( - ikeSaRecord, - new IkeInformationalPayload[] {errNotify}, - true /*isResponse*/, - messageId)); - - // Receiver of INVALID_SYNTAX error notification should delete the IKE SA - if (exception.getErrorType() == ERROR_TYPE_INVALID_SYNTAX) { - handleIkeFatalError(exception); - } - } - - /** - * Method for handling generic processing error of a response. - * - * <p>Detailed error is wrapped in the InvalidSyntaxException, which is usally syntax error, - * unsupported critical payload error and major version error. IKE SA that receives a - * response with these errors should be closed. - */ - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException ikeException) { - // Subclasses MUST override it if they care - cleanUpAndQuit( - new IllegalStateException( - "Do not support handling generic processing error of encrypted" - + " response")); - } - } - - /** - * Retransmitter represents a RAII class to send the initial request, and retransmit as needed. - * - * <p>The Retransmitter class will automatically start transmission upon creation. - */ - @VisibleForTesting - class EncryptedRetransmitter extends Retransmitter { - private final IkeSaRecord mIkeSaRecord; - - @VisibleForTesting - EncryptedRetransmitter(IkeMessage msg) { - this(mCurrentIkeSaRecord, msg); - } - - private EncryptedRetransmitter(IkeSaRecord ikeSaRecord, IkeMessage msg) { - super(getHandler(), msg); - - mIkeSaRecord = ikeSaRecord; - - retransmit(); - } - - @Override - public void send(IkeMessage msg) { - sendEncryptedIkeMessage(mIkeSaRecord, msg); - } - - @Override - public void handleRetransmissionFailure() { - handleIkeFatalError(new IOException("Retransmitting failure")); - } - } - - /** - * DeleteResponderBase represents all states after IKE_INIT and IKE_AUTH. - * - * <p>All post-init states share common functionality of being able to respond to IKE_DELETE - * requests. - */ - private abstract class DeleteResponderBase extends BusyState { - /** Builds a IKE Delete Response for the given IKE SA and request. */ - protected IkeMessage buildIkeDeleteResp(IkeMessage req, IkeSaRecord ikeSaRecord) { - IkeInformationalPayload[] payloads = new IkeInformationalPayload[] {}; - return buildEncryptedInformationalMessage( - ikeSaRecord, payloads, true /* isResp */, req.ikeHeader.messageId); - } - - /** - * Validates that the delete request is acceptable. - * - * <p>The request message must be guaranteed by previous checks to be of SUBTYPE_DELETE_IKE, - * and therefore contains an IkeDeletePayload. This is checked in getIkeExchangeSubType. - */ - protected void validateIkeDeleteReq(IkeMessage req, IkeSaRecord expectedRecord) - throws InvalidSyntaxException { - if (expectedRecord != getIkeSaRecordForPacket(req.ikeHeader)) { - throw new InvalidSyntaxException("Delete request received in wrong SA"); - } - } - - /** - * Helper method for responding to a session deletion request - * - * <p>Note that this method expects that the session is keyed on the current IKE SA session, - * and closing the IKE SA indicates that the remote wishes to end the session as a whole. As - * such, this should not be used in rekey cases where there is any ambiguity as to which IKE - * SA the session is reliant upon. - * - * <p>Note that this method will also quit the state machine. - * - * @param ikeMessage The received session deletion request - */ - protected void handleDeleteSessionRequest(IkeMessage ikeMessage) { - try { - validateIkeDeleteReq(ikeMessage, mCurrentIkeSaRecord); - IkeMessage resp = buildIkeDeleteResp(ikeMessage, mCurrentIkeSaRecord); - - mUserCbExecutor.execute( - () -> { - mIkeSessionCallback.onClosed(); - }); - - sendEncryptedIkeMessage(mCurrentIkeSaRecord, resp); - - removeIkeSaRecord(mCurrentIkeSaRecord); - mCurrentIkeSaRecord.close(); - mCurrentIkeSaRecord = null; - - quitNow(); - } catch (InvalidSyntaxException e) { - // Got deletion of a non-Current IKE SA. Program error. - cleanUpAndQuit(new IllegalStateException(e)); - } - } - } - - /** - * DeleteBase abstracts deletion handling for all states initiating a delete exchange - * - * <p>All subclasses of this state share common functionality that a deletion request is sent, - * and the response is received. - */ - private abstract class DeleteBase extends DeleteResponderBase { - /** Builds a IKE Delete Request for the given IKE SA. */ - protected IkeMessage buildIkeDeleteReq(IkeSaRecord ikeSaRecord) { - IkeInformationalPayload[] payloads = - new IkeInformationalPayload[] {new IkeDeletePayload()}; - return buildEncryptedInformationalMessage( - ikeSaRecord, - payloads, - false /* isResp */, - ikeSaRecord.getLocalRequestMessageId()); - } - - protected void validateIkeDeleteResp(IkeMessage resp, IkeSaRecord expectedSaRecord) - throws InvalidSyntaxException { - if (expectedSaRecord != getIkeSaRecordForPacket(resp.ikeHeader)) { - throw new IllegalStateException("Response received on incorrect SA"); - } - - if (resp.ikeHeader.exchangeType != IkeHeader.EXCHANGE_TYPE_INFORMATIONAL) { - throw new InvalidSyntaxException( - "Invalid exchange type; expected INFORMATIONAL, but got: " - + resp.ikeHeader.exchangeType); - } - - if (!resp.ikePayloadList.isEmpty()) { - throw new InvalidSyntaxException( - "Unexpected payloads - IKE Delete response should be empty."); - } - } - } - - /** - * Receiving represents a state when idle IkeSessionStateMachine receives an incoming packet. - * - * <p>If this incoming packet is fully handled by Receiving state and does not trigger any - * further state transition or deletion of whole IKE Session, IkeSessionStateMachine MUST - * transition back to Idle. - */ - class Receiving extends RekeyIkeHandlerBase { - private boolean mProcedureFinished = true; - - @Override - public void enterState() { - mProcedureFinished = true; - } - - @Override - protected void handleReceivedIkePacket(Message message) { - super.handleReceivedIkePacket(message); - - // If the received packet does not trigger a state transition or the packet causes this - // state machine to quit, transition back to Idle State. In the second case, state - // machine will first go back to Idle and then quit. - if (mProcedureFinished) transitionTo(mIdle); - } - - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_REKEY_IKE: - // Errors in this exchange with no specific protocol error code will all be - // classified to use NO_PROPOSAL_CHOSEN. The reason that we don't use - // NO_ADDITIONAL_SAS is because it indicates "responder is unwilling to accept - // any more Child SAs on this IKE SA.", according to RFC 7296. Sending this - // error may mislead the remote peer. - try { - validateIkeRekeyReq(ikeMessage); - - // TODO: Add support for limited re-negotiation of parameters - - // Build a rekey response payload with our previously selected proposal, - // against which we will validate the received proposals. - IkeSaPayload reqSaPayload = - ikeMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - byte respProposalNumber = - reqSaPayload.getNegotiatedProposalNumber(mSaProposal); - - List<IkePayload> payloadList = - CreateIkeSaHelper.getRekeyIkeSaResponsePayloads( - respProposalNumber, mSaProposal, mLocalAddress); - - // Build IKE header - IkeHeader ikeHeader = - new IkeHeader( - mCurrentIkeSaRecord.getInitiatorSpi(), - mCurrentIkeSaRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - true /*isResponseMsg*/, - mCurrentIkeSaRecord.isLocalInit, - ikeMessage.ikeHeader.messageId); - - IkeMessage responseIkeMessage = new IkeMessage(ikeHeader, payloadList); - - // Build new SA first to ensure that we can find a valid proposal. - mRemoteInitNewIkeSaRecord = - validateAndBuildIkeSa( - ikeMessage, responseIkeMessage, false /*isLocalInit*/); - - sendEncryptedIkeMessage(responseIkeMessage); - - transitionTo(mRekeyIkeRemoteDelete); - mProcedureFinished = false; - } catch (IkeProtocolException e) { - handleRekeyCreationFailure(ikeMessage.ikeHeader.messageId, e); - } catch (GeneralSecurityException e) { - handleRekeyCreationFailure( - ikeMessage.ikeHeader.messageId, - new NoValidProposalChosenException( - "Error in building new IKE SA", e)); - } catch (IOException e) { - handleRekeyCreationFailure( - ikeMessage.ikeHeader.messageId, - new NoValidProposalChosenException( - "IKE SPI allocation collided - they reused an SPI.", e)); - } - return; - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - handleDeleteSessionRequest(ikeMessage); - return; - case IKE_EXCHANGE_SUBTYPE_CREATE_CHILD: // Fall through - case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: // Fall through - case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: - deferMessage( - obtainMessage( - CMD_RECEIVE_REQUEST_FOR_CHILD, - ikeExchangeSubType, - 0 /*placeHolder*/, - ikeMessage)); - transitionTo(mChildProcedureOngoing); - mProcedureFinished = false; - return; - default: - // TODO: Add support for generic INFORMATIONAL request - } - } - - private void handleRekeyCreationFailure(int messageId, IkeProtocolException e) { - loge("Received invalid Rekey IKE request. Reject with error notification", e); - - buildAndSendNotificationResponse( - mCurrentIkeSaRecord, messageId, e.buildNotifyPayload()); - } - } - - /** - * This class represents a state when there is at least one ongoing Child procedure - * (Create/Rekey/Delete Child) - * - * <p>For a locally initiated Child procedure, this state is responsible for notifying Child - * Session to initiate the exchange, building outbound request IkeMessage with Child Session - * provided payload list and redirecting the inbound response to Child Session for validation. - * - * <p>For a remotely initiated Child procedure, this state is responsible for redirecting the - * inbound request to Child Session(s) and building outbound response IkeMessage with Child - * Session provided payload list. Exchange collision on a Child Session will be resolved inside - * the Child Session. - * - * <p>For a remotely initiated IKE procedure, this state will only accept a Delete IKE request - * and reject other types with TEMPORARY_FAILURE, since it causes conflict with the ongoing - * Child procedure. - * - * <p>For most inbound request/response, this state will first pick out and handle IKE related - * payloads and then send the rest of the payloads to Child Session for further validation. It - * is the Child Session's responsibility to check required payloads (and verify the exchange - * type) according to its procedure type. Only when receiving an inbound delete Child request, - * as the only case where multiple Child Sessions will be affected by one IkeMessage, this state - * will only send Delete Payload(s) to Child Session. - */ - class ChildProcedureOngoing extends DeleteBase { - // It is possible that mChildInLocalProcedure is also in mChildInRemoteProcedures when both - // sides initiated exchange for the same Child Session. - private ChildSessionStateMachine mChildInLocalProcedure; - private Set<ChildSessionStateMachine> mChildInRemoteProcedures; - - private ChildLocalRequest mLocalRequestOngoing; - - private int mLastInboundRequestMsgId; - private List<IkePayload> mOutboundRespPayloads; - private Set<ChildSessionStateMachine> mAwaitingChildResponse; - - private EncryptedRetransmitter mRetransmitter; - - @Override - public void enterState() { - mChildInLocalProcedure = null; - mChildInRemoteProcedures = new HashSet<>(); - - mLocalRequestOngoing = null; - - mLastInboundRequestMsgId = 0; - mOutboundRespPayloads = new LinkedList<>(); - mAwaitingChildResponse = new HashSet<>(); - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_RECEIVE_REQUEST_FOR_CHILD: - // Handle remote request (and do state transition) - handleRequestIkeMessage( - (IkeMessage) message.obj, - message.arg1 /*ikeExchangeSubType*/, - null /*ReceivedIkePacket*/); - return HANDLED; - case CMD_OUTBOUND_CHILD_PAYLOADS_READY: - ChildOutboundData outboundData = (ChildOutboundData) message.obj; - int exchangeType = outboundData.exchangeType; - List<IkePayload> outboundPayloads = outboundData.payloadList; - - if (outboundData.isResp) { - handleOutboundResponse( - exchangeType, outboundPayloads, outboundData.childSession); - } else { - handleOutboundRequest(exchangeType, outboundPayloads); - } - - return HANDLED; - case CMD_CHILD_PROCEDURE_FINISHED: - ChildSessionStateMachine childSession = (ChildSessionStateMachine) message.obj; - - if (mChildInLocalProcedure == childSession) { - mChildInLocalProcedure = null; - mLocalRequestOngoing = null; - } - mChildInRemoteProcedures.remove(childSession); - - transitionToIdleIfAllProceduresDone(); - return HANDLED; - case CMD_HANDLE_FIRST_CHILD_NEGOTIATION: - FirstChildNegotiationData childData = (FirstChildNegotiationData) message.obj; - - mChildInLocalProcedure = getChildSession(childData.childSessionCallback); - if (mChildInLocalProcedure == null) { - cleanUpAndQuit(new IllegalStateException("First child not found.")); - return HANDLED; - } - - mChildInLocalProcedure.handleFirstChildExchange( - childData.reqPayloads, - childData.respPayloads, - mLocalAddress, - mRemoteAddress, - getEncapSocketIfNeeded(), - mIkePrf, - mCurrentIkeSaRecord.getSkD()); - return HANDLED; - case CMD_EXECUTE_LOCAL_REQ: - executeLocalRequest((ChildLocalRequest) message.obj); - return HANDLED; - default: - return super.processStateMessage(message); - } - } - - @Override - protected void handleTempFailure() { - mTempFailHandler.handleTempFailure(mLocalRequestOngoing); - } - - private void transitionToIdleIfAllProceduresDone() { - if (mChildInLocalProcedure == null && mChildInRemoteProcedures.isEmpty()) { - transitionTo(mIdle); - } - } - - private ChildSessionStateMachine getChildSession(ChildSessionCallback callbacks) { - synchronized (mChildCbToSessions) { - return mChildCbToSessions.get(callbacks); - } - } - - private UdpEncapsulationSocket getEncapSocketIfNeeded() { - boolean isNatDetected = mIsLocalBehindNat || mIsRemoteBehindNat; - - return (isNatDetected ? mIkeSessionOptions.getUdpEncapsulationSocket() : null); - } - - private void executeLocalRequest(ChildLocalRequest req) { - mChildInLocalProcedure = getChildSession(req.childSessionCallback); - mLocalRequestOngoing = req; - - if (mChildInLocalProcedure == null) { - // This request has been validated to have a recognized target Child Session when - // it was sent to IKE Session at the begginnig. Failing to find this Child Session - // now means the Child creation has failed. - logd( - "Child state machine not found for local request: " - + req.procedureType - + " Creation of Child Session may have been failed."); - - transitionToIdleIfAllProceduresDone(); - return; - } - switch (req.procedureType) { - case CMD_LOCAL_REQUEST_CREATE_CHILD: - mChildInLocalProcedure.createChildSession( - mLocalAddress, - mRemoteAddress, - getEncapSocketIfNeeded(), - mIkePrf, - mCurrentIkeSaRecord.getSkD()); - break; - case CMD_LOCAL_REQUEST_REKEY_CHILD: - mChildInLocalProcedure.rekeyChildSession(); - break; - case CMD_LOCAL_REQUEST_DELETE_CHILD: - mChildInLocalProcedure.deleteChildSession(); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Invalid Child procedure type: " + req.procedureType)); - break; - } - } - - /** - * This method is called when this state receives an inbound request or when mReceiving - * received an inbound Child request and deferred it to this state. - */ - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - // TODO: Grab a remote lock and hand payloads to the Child Session - - mLastInboundRequestMsgId = ikeMessage.ikeHeader.messageId; - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_CREATE_CHILD: - buildAndSendErrorNotificationResponse( - mCurrentIkeSaRecord, - ikeMessage.ikeHeader.messageId, - ERROR_TYPE_NO_ADDITIONAL_SAS); - break; - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - // Send response and quit state machine - handleDeleteSessionRequest(ikeMessage); - - // Return immediately to avoid transitioning to mIdle - return; - case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD: - handleInboundDeleteChildRequest(ikeMessage); - break; - case IKE_EXCHANGE_SUBTYPE_REKEY_IKE: - buildAndSendErrorNotificationResponse( - mCurrentIkeSaRecord, - ikeMessage.ikeHeader.messageId, - ERROR_TYPE_TEMPORARY_FAILURE); - break; - case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD: - handleInboundRekeyChildRequest(ikeMessage); - break; - case IKE_EXCHANGE_SUBTYPE_GENERIC_INFO: - // TODO:b/139943757 Handle general informational request - default: - cleanUpAndQuit( - new IllegalStateException( - "Invalid IKE exchange subtype: " + ikeExchangeSubType)); - return; - } - transitionToIdleIfAllProceduresDone(); - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - mRetransmitter.stopRetransmitting(); - - List<IkePayload> handledPayloads = new LinkedList<>(); - - for (IkePayload payload : ikeMessage.ikePayloadList) { - switch (payload.payloadType) { - case PAYLOAD_TYPE_NOTIFY: - // TODO: Handle fatal IKE error notification and IKE status notification. - break; - case PAYLOAD_TYPE_VENDOR: - // TODO: Handle Vendor ID Payload - handledPayloads.add(payload); - break; - case PAYLOAD_TYPE_CP: - // TODO: Handle IKE related configuration attributes and pass the payload to - // Child to further handle internal IP address attributes. - break; - default: - break; - } - } - - List<IkePayload> payloads = new LinkedList<>(); - payloads.addAll(ikeMessage.ikePayloadList); - payloads.removeAll(handledPayloads); - - mChildInLocalProcedure.receiveResponse(ikeMessage.ikeHeader.exchangeType, payloads); - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException ikeException) { - mRetransmitter.stopRetransmitting(); - - sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); - handleIkeFatalError(ikeException); - } - - private void handleInboundDeleteChildRequest(IkeMessage ikeMessage) { - // It is guaranteed in #getIkeExchangeSubType that at least one Delete Child Payload - // exists. - - HashMap<ChildSessionStateMachine, List<IkePayload>> childToDelPayloadsMap = - new HashMap<>(); - Set<Integer> spiHandled = new HashSet<>(); - - for (IkePayload payload : ikeMessage.ikePayloadList) { - switch (payload.payloadType) { - case PAYLOAD_TYPE_VENDOR: - // TODO: Investigate if Vendor ID Payload can be in an INFORMATIONAL - // message. - break; - case PAYLOAD_TYPE_NOTIFY: - logw( - "Unexpected or unknown notification: " - + ((IkeNotifyPayload) payload).notifyType); - break; - case PAYLOAD_TYPE_DELETE: - IkeDeletePayload delPayload = (IkeDeletePayload) payload; - - for (int spi : delPayload.spisToDelete) { - ChildSessionStateMachine child = mRemoteSpiToChildSessionMap.get(spi); - if (child == null) { - // TODO: Investigate how other implementations handle that. - logw("Child SA not found with received SPI: " + spi); - } else if (!spiHandled.add(spi)) { - logw("Received repeated Child SPI: " + spi); - } else { - // Store Delete Payload with its target ChildSession - if (!childToDelPayloadsMap.containsKey(child)) { - childToDelPayloadsMap.put(child, new LinkedList<>()); - } - List<IkePayload> delPayloads = childToDelPayloadsMap.get(child); - - // Avoid storing repeated Delete Payload - if (!delPayloads.contains(delPayload)) delPayloads.add(delPayload); - } - } - - break; - case PAYLOAD_TYPE_CP: - // TODO: Handle it - break; - default: - logw("Unexpected payload types found: " + payload.payloadType); - } - } - - // If no Child SA is found, only reply with IKE related payloads or an empty - // message - if (childToDelPayloadsMap.isEmpty()) { - logd("No Child SA is found for this request."); - sendEncryptedIkeMessage( - buildEncryptedInformationalMessage( - new IkeInformationalPayload[0], - true /*isResp*/, - ikeMessage.ikeHeader.messageId)); - return; - } - - // Send Delete Payloads to Child Sessions - for (ChildSessionStateMachine child : childToDelPayloadsMap.keySet()) { - child.receiveRequest( - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - EXCHANGE_TYPE_INFORMATIONAL, - childToDelPayloadsMap.get(child)); - mAwaitingChildResponse.add(child); - mChildInRemoteProcedures.add(child); - } - } - - private void handleInboundRekeyChildRequest(IkeMessage ikeMessage) { - // It is guaranteed in #getIkeExchangeSubType that at least one Notify-Rekey Child - // Payload exists. - List<IkePayload> handledPayloads = new LinkedList<>(); - ChildSessionStateMachine targetChild = null; - Set<Integer> unrecognizedSpis = new HashSet<>(); - - for (IkePayload payload : ikeMessage.ikePayloadList) { - switch (payload.payloadType) { - case PAYLOAD_TYPE_VENDOR: - // TODO: Handle it. - handledPayloads.add(payload); - break; - case PAYLOAD_TYPE_NOTIFY: - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; - if (NOTIFY_TYPE_REKEY_SA != notifyPayload.notifyType) break; - - int childSpi = notifyPayload.spi; - ChildSessionStateMachine child = mRemoteSpiToChildSessionMap.get(childSpi); - - if (child == null) { - // Remember unrecognized SPIs and reply error notification if no - // recognized SPI found. - unrecognizedSpis.add(childSpi); - logw("Child SA not found with received SPI: " + childSpi); - } else if (targetChild == null) { - // Each message should have only one Notify-Rekey Payload. If there are - // multiple of them, we only process the first valid one and ignore - // others. - targetChild = mRemoteSpiToChildSessionMap.get(childSpi); - } else { - logw("More than one Notify-Rekey Payload found with SPI: " + childSpi); - handledPayloads.add(notifyPayload); - } - break; - case PAYLOAD_TYPE_CP: - // TODO: Handle IKE related configuration attributes and pass the payload to - // Child to further handle internal IP address attributes. - break; - default: - break; - } - } - - // Reject request with error notification. - if (targetChild == null) { - IkeInformationalPayload[] errorPayloads = - new IkeInformationalPayload[unrecognizedSpis.size()]; - int i = 0; - for (Integer spi : unrecognizedSpis) { - errorPayloads[i++] = - new IkeNotifyPayload( - IkePayload.PROTOCOL_ID_ESP, - spi, - ERROR_TYPE_CHILD_SA_NOT_FOUND, - new byte[0]); - } - - IkeMessage msg = - buildEncryptedNotificationMessage( - mCurrentIkeSaRecord, - errorPayloads, - EXCHANGE_TYPE_INFORMATIONAL, - true /*isResponse*/, - ikeMessage.ikeHeader.messageId); - - sendEncryptedIkeMessage(mCurrentIkeSaRecord, msg); - return; - } - - // Normal path - List<IkePayload> payloads = new LinkedList<>(); - payloads.addAll(ikeMessage.ikePayloadList); - payloads.removeAll(handledPayloads); - - mAwaitingChildResponse.add(targetChild); - mChildInRemoteProcedures.add(targetChild); - - targetChild.receiveRequest( - IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, ikeMessage.ikeHeader.exchangeType, payloads); - } - - private void handleOutboundRequest(int exchangeType, List<IkePayload> outboundPayloads) { - IkeHeader ikeHeader = - new IkeHeader( - mCurrentIkeSaRecord.getInitiatorSpi(), - mCurrentIkeSaRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - exchangeType, - false /*isResp*/, - mCurrentIkeSaRecord.isLocalInit, - mCurrentIkeSaRecord.getLocalRequestMessageId()); - IkeMessage ikeMessage = new IkeMessage(ikeHeader, outboundPayloads); - - mRetransmitter = new EncryptedRetransmitter(ikeMessage); - } - - private void handleOutboundResponse( - int exchangeType, - List<IkePayload> outboundPayloads, - ChildSessionStateMachine childSession) { - // For each request IKE passed to Child, Child will send back to IKE a response. Even - // if the Child Sesison is under simultaneous deletion, it will send back an empty - // payload list. - mOutboundRespPayloads.addAll(outboundPayloads); - mAwaitingChildResponse.remove(childSession); - if (!mAwaitingChildResponse.isEmpty()) return; - - IkeHeader ikeHeader = - new IkeHeader( - mCurrentIkeSaRecord.getInitiatorSpi(), - mCurrentIkeSaRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - exchangeType, - true /*isResp*/, - mCurrentIkeSaRecord.isLocalInit, - mLastInboundRequestMsgId); - IkeMessage ikeMessage = new IkeMessage(ikeHeader, mOutboundRespPayloads); - sendEncryptedIkeMessage(ikeMessage); - } - } - - /** CreateIkeLocalIkeInit represents state when IKE library initiates IKE_INIT exchange. */ - @VisibleForTesting - public class CreateIkeLocalIkeInit extends BusyState { - private IkeSecurityParameterIndex mLocalIkeSpiResource; - private IkeSecurityParameterIndex mRemoteIkeSpiResource; - private Retransmitter mRetransmitter; - - // TODO: Support negotiating IKE fragmentation - - @Override - public void enterState() { - try { - IkeMessage request = buildIkeInitReq(); - - // Register local SPI to receive the IKE INIT response. - mIkeSocket.registerIke( - request.ikeHeader.ikeInitiatorSpi, IkeSessionStateMachine.this); - - mIkeInitRequestBytes = request.encode(); - mIkeInitNoncePayload = - request.getPayloadForType( - IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class); - mRetransmitter = new UnencryptedRetransmitter(request); - } catch (IOException e) { - // Fail to assign IKE SPI - handleIkeFatalError(e); - } - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_RECEIVE_IKE_PACKET: - handleReceivedIkePacket(message); - return HANDLED; - - default: - return super.processStateMessage(message); - } - } - - protected void handleReceivedIkePacket(Message message) { - String methodTag = "handleReceivedIkePacket: "; - - ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; - IkeHeader ikeHeader = receivedIkePacket.ikeHeader; - byte[] ikePacketBytes = receivedIkePacket.ikePacketBytes; - - logd( - methodTag - + "Received an " - + ikeHeader.getBasicInfoString() - + ". Packet size: " - + ikePacketBytes.length); - - if (ikeHeader.isResponseMsg) { - DecodeResult decodeResult = IkeMessage.decode(0, ikeHeader, ikePacketBytes); - - switch (decodeResult.status) { - case DECODE_STATUS_OK: - handleResponseIkeMessage(((DecodeResultOk) decodeResult).ikeMessage); - mIkeInitResponseBytes = ikePacketBytes; - - // SA negotiation failed - if (mCurrentIkeSaRecord == null) break; - - mCurrentIkeSaRecord.incrementLocalRequestMessageId(); - break; - case DECODE_STATUS_PARTIAL: - // Fall through. We don't support IKE fragmentation here. We should never - // get this status. - case DECODE_STATUS_PROTECTED_ERROR: - // IKE INIT response is not protected. So we should never get this status - cleanUpAndQuit( - new IllegalStateException( - "Unexpected decoding status: " + decodeResult.status)); - break; - case DECODE_STATUS_UNPROTECTED_ERROR: - logi( - "Discard unencrypted response with syntax error", - ((DecodeResultError) decodeResult).ikeException); - break; - default: - cleanUpAndQuit( - new IllegalStateException( - "Invalid decoding status: " + decodeResult.status)); - } - - } else { - // TODO: Also prettyprint IKE header in the log. - logi("Received a request while waiting for IKE_INIT response. Discard it."); - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - boolean ikeInitSuccess = false; - try { - validateIkeInitResp(mRetransmitter.getMessage(), ikeMessage); - - mCurrentIkeSaRecord = - IkeSaRecord.makeFirstIkeSaRecord( - mRetransmitter.getMessage(), - ikeMessage, - mLocalIkeSpiResource, - mRemoteIkeSpiResource, - mIkePrf, - mIkeIntegrity == null ? 0 : mIkeIntegrity.getKeyLength(), - mIkeCipher.getKeyLength(), - new LocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE)); - - addIkeSaRecord(mCurrentIkeSaRecord); - ikeInitSuccess = true; - - transitionTo(mCreateIkeLocalIkeAuth); - } catch (IkeProtocolException | GeneralSecurityException | IOException e) { - // TODO: Try another DH group to buld KE Payload if receiving InvalidKeException - handleIkeFatalError(e); - } finally { - if (!ikeInitSuccess) { - if (mLocalIkeSpiResource != null) { - mLocalIkeSpiResource.close(); - mLocalIkeSpiResource = null; - } - if (mRemoteIkeSpiResource != null) { - mRemoteIkeSpiResource.close(); - mRemoteIkeSpiResource = null; - } - } - } - } - - private IkeMessage buildIkeInitReq() throws IOException { - // Generate IKE SPI - mLocalIkeSpiResource = - IkeSecurityParameterIndex.allocateSecurityParameterIndex(mLocalAddress); - long initSpi = mLocalIkeSpiResource.getSpi(); - long respSpi = 0; - - // It is validated in IkeSessionOptions.Builder to ensure IkeSessionOptions has at least - // one IkeSaProposal and all SaProposals are valid for IKE SA negotiation. - IkeSaProposal[] saProposals = mIkeSessionOptions.getSaProposals(); - List<IkePayload> payloadList = - CreateIkeSaHelper.getIkeInitSaRequestPayloads( - saProposals, - initSpi, - respSpi, - mLocalAddress, - mRemoteAddress, - mLocalPort, - IkeSocket.IKE_SERVER_PORT); - payloadList.add( - new IkeNotifyPayload( - IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED)); - - // TODO: Add Notification Payloads according to user configurations. - - // Build IKE header - IkeHeader ikeHeader = - new IkeHeader( - initSpi, - respSpi, - IkePayload.PAYLOAD_TYPE_SA, - IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, - false /*isResponseMsg*/, - true /*fromIkeInitiator*/, - 0 /*messageId*/); - - return new IkeMessage(ikeHeader, payloadList); - } - - private void validateIkeInitResp(IkeMessage reqMsg, IkeMessage respMsg) - throws IkeProtocolException, IOException { - IkeHeader respIkeHeader = respMsg.ikeHeader; - mRemoteIkeSpiResource = - IkeSecurityParameterIndex.allocateSecurityParameterIndex( - mIkeSessionOptions.getServerAddress(), respIkeHeader.ikeResponderSpi); - - int exchangeType = respIkeHeader.exchangeType; - if (exchangeType != IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT) { - throw new InvalidSyntaxException( - "Expected EXCHANGE_TYPE_IKE_SA_INIT but received: " + exchangeType); - } - - IkeSaPayload respSaPayload = null; - IkeKePayload respKePayload = null; - - /** - * There MAY be multiple NAT_DETECTION_SOURCE_IP payloads in a message if the sender - * does not know which of several network attachments will be used to send the packet. - */ - List<IkeNotifyPayload> natSourcePayloads = new LinkedList<>(); - IkeNotifyPayload natDestPayload = null; - - boolean hasNoncePayload = false; - - for (IkePayload payload : respMsg.ikePayloadList) { - switch (payload.payloadType) { - case IkePayload.PAYLOAD_TYPE_SA: - respSaPayload = (IkeSaPayload) payload; - break; - case IkePayload.PAYLOAD_TYPE_KE: - respKePayload = (IkeKePayload) payload; - break; - case IkePayload.PAYLOAD_TYPE_CERT_REQUEST: - throw new UnsupportedOperationException( - "Do not support handling Cert Request Payload."); - // TODO: Handle it when using certificate based authentication. Otherwise, - // ignore it. - case IkePayload.PAYLOAD_TYPE_NONCE: - hasNoncePayload = true; - mIkeRespNoncePayload = (IkeNoncePayload) payload; - break; - case IkePayload.PAYLOAD_TYPE_VENDOR: - // Do not support any vendor defined protocol extensions. Ignore - // all Vendor ID Payloads. - break; - case IkePayload.PAYLOAD_TYPE_NOTIFY: - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; - - if (notifyPayload.isErrorNotify()) { - throw notifyPayload.validateAndBuildIkeException(); - } - - switch (notifyPayload.notifyType) { - case NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP: - natSourcePayloads.add(notifyPayload); - break; - case NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP: - if (natDestPayload != null) { - throw new InvalidSyntaxException( - "More than one" - + " NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP" - + " found"); - } - natDestPayload = notifyPayload; - break; - case NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED: - mSupportFragment = true; - break; - default: - // Unknown and unexpected status notifications are ignored as per - // RFC7296. - logw( - "Received unknown or unexpected status notifications with" - + " notify type: " - + notifyPayload.notifyType); - } - - break; - default: - logw( - "Received unexpected payload in IKE INIT response. Payload type: " - + payload.payloadType); - } - } - - if (respSaPayload == null - || respKePayload == null - || natSourcePayloads.isEmpty() - || natDestPayload == null - || !hasNoncePayload) { - throw new InvalidSyntaxException( - "SA, KE, Nonce, Notify-NAT-Detection-Source, or" - + " Notify-NAT-Detection-Destination payload missing."); - } - - IkeSaPayload reqSaPayload = - reqMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - mSaProposal = - IkeSaPayload.getVerifiedNegotiatedIkeProposalPair( - reqSaPayload, respSaPayload, mRemoteAddress) - .second - .saProposal; - - // Build IKE crypto tools using mSaProposal. It is ensured that mSaProposal is valid and - // has exactly one Transform for each Transform type. Only exception is when - // combined-mode cipher is used, there will be either no integrity algorithm or an - // INTEGRITY_ALGORITHM_NONE type algorithm. - Provider provider = IkeMessage.getSecurityProvider(); - mIkeCipher = IkeCipher.create(mSaProposal.getEncryptionTransforms()[0], provider); - if (!mIkeCipher.isAead()) { - mIkeIntegrity = - IkeMacIntegrity.create(mSaProposal.getIntegrityTransforms()[0], provider); - } - mIkePrf = IkeMacPrf.create(mSaProposal.getPrfTransforms()[0], provider); - - IkeKePayload reqKePayload = - reqMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class); - if (reqKePayload.dhGroup != respKePayload.dhGroup - && respKePayload.dhGroup != mSaProposal.getDhGroupTransforms()[0].id) { - throw new InvalidSyntaxException("Received KE payload with mismatched DH group."); - } - - // NAT detection - long initIkeSpi = respMsg.ikeHeader.ikeInitiatorSpi; - long respIkeSpi = respMsg.ikeHeader.ikeResponderSpi; - mIsLocalBehindNat = true; - mIsRemoteBehindNat = true; - - // Check if local node is behind NAT - byte[] expectedLocalNatData = - IkeNotifyPayload.generateNatDetectionData( - initIkeSpi, respIkeSpi, mLocalAddress, mLocalPort); - mIsLocalBehindNat = !Arrays.equals(expectedLocalNatData, natDestPayload.notifyData); - - // Check if the remote node is behind NAT - byte[] expectedRemoteNatData = - IkeNotifyPayload.generateNatDetectionData( - initIkeSpi, respIkeSpi, mRemoteAddress, IkeSocket.IKE_SERVER_PORT); - for (IkeNotifyPayload natPayload : natSourcePayloads) { - // If none of the received hash matches the expected value, the remote node is - // behind NAT. - if (Arrays.equals(expectedRemoteNatData, natPayload.notifyData)) { - mIsRemoteBehindNat = false; - } - } - } - - @Override - public void exitState() { - super.exitState(); - mRetransmitter.stopRetransmitting(); - } - - private class UnencryptedRetransmitter extends Retransmitter { - private UnencryptedRetransmitter(IkeMessage msg) { - super(getHandler(), msg); - - retransmit(); - } - - @Override - public void send(IkeMessage msg) { - // Sends unencrypted - mIkeSocket.sendIkePacket(msg.encode(), mRemoteAddress); - } - - @Override - public void handleRetransmissionFailure() { - handleIkeFatalError(new IOException("Retransmitting IKE INIT request failure")); - } - } - } - - /** - * CreateIkeLocalIkeAuthBase represents the common state and functionality required to perform - * IKE AUTH exchanges in both the EAP and non-EAP flows. - */ - abstract class CreateIkeLocalIkeAuthBase extends DeleteBase { - protected Retransmitter mRetransmitter; - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - // TODO: b/139482382 If receiving a remote request while waiting for the last IKE AUTH - // response, defer it to next state. - - protected IkeMessage buildIkeAuthReqMessage(List<IkePayload> payloadList) { - // Build IKE header - IkeHeader ikeHeader = - new IkeHeader( - mCurrentIkeSaRecord.getInitiatorSpi(), - mCurrentIkeSaRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - false /*isResponseMsg*/, - true /*fromIkeInitiator*/, - mCurrentIkeSaRecord.getLocalRequestMessageId()); - - return new IkeMessage(ikeHeader, payloadList); - } - - protected void authenticatePsk( - byte[] psk, IkeAuthPayload authPayload, IkeIdPayload respIdPayload) - throws AuthenticationFailedException { - if (authPayload.authMethod != IkeAuthPayload.AUTH_METHOD_PRE_SHARED_KEY) { - throw new AuthenticationFailedException( - "Expected the remote/server to use PSK-based authentication but" - + " they used: " - + authPayload.authMethod); - } - - IkeAuthPskPayload pskPayload = (IkeAuthPskPayload) authPayload; - pskPayload.verifyInboundSignature( - psk, - mIkeInitResponseBytes, - mCurrentIkeSaRecord.nonceInitiator, - respIdPayload.getEncodedPayloadBody(), - mIkePrf, - mCurrentIkeSaRecord.getSkPr()); - } - - protected List<IkePayload> extractChildPayloadsFromMessage(IkeMessage ikeMessage) - throws InvalidSyntaxException { - IkeSaPayload saPayload = - ikeMessage.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - IkeTsPayload tsInitPayload = - ikeMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_TS_INITIATOR, IkeTsPayload.class); - IkeTsPayload tsRespPayload = - ikeMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_TS_RESPONDER, IkeTsPayload.class); - - List<IkeNotifyPayload> notifyPayloads = - ikeMessage.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - - boolean hasErrorNotify = false; - List<IkePayload> list = new LinkedList<>(); - for (IkeNotifyPayload payload : notifyPayloads) { - if (payload.isNewChildSaNotify()) { - list.add(payload); - if (payload.isErrorNotify()) { - hasErrorNotify = true; - } - } - } - - // If there is no error notification, SA, TS-initiator and TS-responder MUST all be - // included in this message. - if (!hasErrorNotify - && (saPayload == null || tsInitPayload == null || tsRespPayload == null)) { - throw new InvalidSyntaxException( - "SA, TS-Initiator or TS-Responder payload is missing."); - } - - list.add(saPayload); - list.add(tsInitPayload); - list.add(tsRespPayload); - return list; - } - - protected void performFirstChildNegotiation( - List<IkePayload> childReqList, List<IkePayload> childRespList) { - childReqList.add(mIkeInitNoncePayload); - childRespList.add(mIkeRespNoncePayload); - - deferMessage( - obtainMessage( - CMD_HANDLE_FIRST_CHILD_NEGOTIATION, - new FirstChildNegotiationData( - mFirstChildSessionOptions, - mFirstChildCallbacks, - childReqList, - childRespList))); - - mUserCbExecutor.execute( - () -> { - mIkeSessionCallback.onOpened(null /*sessionConfiguration*/); - // TODO: Construct and pass a real IkeSessionConfiguration - }); - transitionTo(mChildProcedureOngoing); - } - } - - /** - * CreateIkeLocalIkeAuth represents state when IKE library initiates IKE_AUTH exchange. - * - * <p>If using EAP, CreateIkeLocalIkeAuth will transition to CreateIkeLocalIkeAuthInEap state - * after validating the IKE AUTH response. - */ - class CreateIkeLocalIkeAuth extends CreateIkeLocalIkeAuthBase { - private boolean mUseEap; - - @Override - public void enterState() { - try { - super.enterState(); - mRetransmitter = new EncryptedRetransmitter(buildIkeAuthReq()); - mUseEap = - (IkeSessionOptions.IKE_AUTH_METHOD_EAP - == mIkeSessionOptions.getLocalAuthConfig().mAuthMethod); - } catch (ResourceUnavailableException e) { - // Handle IPsec SPI assigning failure. - handleIkeFatalError(e); - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - int exchangeType = ikeMessage.ikeHeader.exchangeType; - if (exchangeType != IkeHeader.EXCHANGE_TYPE_IKE_AUTH) { - throw new InvalidSyntaxException( - "Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType); - } - - List<IkePayload> childReqList = - extractChildPayloadsFromMessage(mRetransmitter.getMessage()); - - if (mUseEap) { - validateIkeAuthRespWithEapPayload(ikeMessage); - - // childReqList needed after EAP completed, so persist to IkeSessionStateMachine - // state. - mFirstChildReqList = childReqList; - - IkeEapPayload ikeEapPayload = - ikeMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class); - - deferMessage(obtainMessage(CMD_EAP_START_EAP_AUTH, ikeEapPayload)); - transitionTo(mCreateIkeLocalIkeAuthInEap); - } else { - validateIkeAuthRespWithChildPayloads(ikeMessage); - - performFirstChildNegotiation( - childReqList, extractChildPayloadsFromMessage(ikeMessage)); - } - } catch (IkeProtocolException e) { - if (!mUseEap) { - // Notify the remote because they may have set up the IKE SA. - sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); - } - handleIkeFatalError(e); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException ikeException) { - mRetransmitter.stopRetransmitting(); - - if (!mUseEap) { - // Notify the remote because they may have set up the IKE SA. - sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); - } - handleIkeFatalError(ikeException); - } - - private IkeMessage buildIkeAuthReq() throws ResourceUnavailableException { - List<IkePayload> payloadList = new LinkedList<>(); - - // Build Identification payloads - mInitIdPayload = - new IkeIdPayload( - true /*isInitiator*/, mIkeSessionOptions.getLocalIdentification()); - IkeIdPayload respIdPayload = - new IkeIdPayload( - false /*isInitiator*/, mIkeSessionOptions.getRemoteIdentification()); - payloadList.add(mInitIdPayload); - payloadList.add(respIdPayload); - - // Build Authentication payload - IkeAuthConfig authConfig = mIkeSessionOptions.getLocalAuthConfig(); - switch (authConfig.mAuthMethod) { - case IkeSessionOptions.IKE_AUTH_METHOD_PSK: - IkeAuthPskPayload pskPayload = - new IkeAuthPskPayload( - ((IkeAuthPskConfig) authConfig).mPsk, - mIkeInitRequestBytes, - mCurrentIkeSaRecord.nonceResponder, - mInitIdPayload.getEncodedPayloadBody(), - mIkePrf, - mCurrentIkeSaRecord.getSkPi()); - payloadList.add(pskPayload); - break; - case IkeSessionOptions.IKE_AUTH_METHOD_PUB_KEY_SIGNATURE: - // TODO: Support authentication based on public key signature. - throw new UnsupportedOperationException( - "Do not support public-key based authentication."); - case IkeSessionOptions.IKE_AUTH_METHOD_EAP: - // Do not include AUTH payload when using EAP. - break; - default: - cleanUpAndQuit( - new IllegalArgumentException( - "Unrecognized authentication method: " - + authConfig.mAuthMethod)); - } - - payloadList.addAll( - CreateChildSaHelper.getInitChildCreateReqPayloads( - mIpSecManager, - mLocalAddress, - mFirstChildSessionOptions, - true /*isFirstChild*/)); - - return buildIkeAuthReqMessage(payloadList); - } - - private void validateIkeAuthRespWithEapPayload(IkeMessage respMsg) - throws IkeProtocolException { - IkeEapPayload ikeEapPayload = - respMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class); - if (ikeEapPayload == null) { - throw new AuthenticationFailedException("Missing EAP payload"); - } - - // TODO: check that we don't receive any ChildSaRespPayloads here - - List<IkePayload> nonEapPayloads = new LinkedList<>(); - nonEapPayloads.addAll(respMsg.ikePayloadList); - nonEapPayloads.remove(ikeEapPayload); - validateIkeAuthResp(nonEapPayloads); - } - - private void validateIkeAuthRespWithChildPayloads(IkeMessage respMsg) - throws IkeProtocolException { - // Extract and validate existence of payloads for first Child SA setup. - List<IkePayload> childSaRespPayloads = extractChildPayloadsFromMessage(respMsg); - - List<IkePayload> nonChildPayloads = new LinkedList<>(); - nonChildPayloads.addAll(respMsg.ikePayloadList); - nonChildPayloads.removeAll(childSaRespPayloads); - - validateIkeAuthResp(nonChildPayloads); - } - - private void validateIkeAuthResp(List<IkePayload> payloadList) throws IkeProtocolException { - // Validate IKE Authentication - IkeAuthPayload authPayload = null; - List<IkeCertPayload> certPayloads = new LinkedList<>(); - - for (IkePayload payload : payloadList) { - switch (payload.payloadType) { - case IkePayload.PAYLOAD_TYPE_ID_RESPONDER: - mRespIdPayload = (IkeIdPayload) payload; - if (!mIkeSessionOptions - .getRemoteIdentification() - .equals(mRespIdPayload.ikeId)) { - throw new AuthenticationFailedException( - "Unrecognized Responder Identification."); - } - break; - case IkePayload.PAYLOAD_TYPE_AUTH: - authPayload = (IkeAuthPayload) payload; - break; - case IkePayload.PAYLOAD_TYPE_CERT: - certPayloads.add((IkeCertPayload) payload); - break; - case IkePayload.PAYLOAD_TYPE_NOTIFY: - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; - if (notifyPayload.isErrorNotify()) { - throw notifyPayload.validateAndBuildIkeException(); - } else { - // Unknown and unexpected status notifications are ignored as per - // RFC7296. - logw( - "Received unknown or unexpected status notifications with" - + " notify type: " - + notifyPayload.notifyType); - } - break; - default: - logw( - "Received unexpected payload in IKE AUTH response. Payload" - + " type: " - + payload.payloadType); - } - } - - // Verify existence of payloads - if (mRespIdPayload == null || authPayload == null) { - throw new AuthenticationFailedException("ID-Responder or Auth payload is missing."); - } - - // Authenticate the remote peer. - authenticate(authPayload, mRespIdPayload, certPayloads); - } - - private void authenticate( - IkeAuthPayload authPayload, - IkeIdPayload respIdPayload, - List<IkeCertPayload> certPayloads) - throws AuthenticationFailedException { - switch (mIkeSessionOptions.getRemoteAuthConfig().mAuthMethod) { - case IkeSessionOptions.IKE_AUTH_METHOD_PSK: - authenticatePsk( - ((IkeAuthPskConfig) mIkeSessionOptions.getRemoteAuthConfig()).mPsk, - authPayload, - respIdPayload); - break; - case IkeSessionOptions.IKE_AUTH_METHOD_PUB_KEY_SIGNATURE: - authenticateDigitalSignature( - certPayloads, - ((IkeAuthDigitalSignRemoteConfig) - mIkeSessionOptions.getRemoteAuthConfig()) - .mTrustAnchor, - authPayload, - respIdPayload); - break; - default: - cleanUpAndQuit( - new IllegalArgumentException( - "Unrecognized auth method: " + authPayload.authMethod)); - } - } - - private void authenticateDigitalSignature( - List<IkeCertPayload> certPayloads, - TrustAnchor trustAnchor, - IkeAuthPayload authPayload, - IkeIdPayload respIdPayload) - throws AuthenticationFailedException { - if (authPayload.authMethod != IkeAuthPayload.AUTH_METHOD_RSA_DIGITAL_SIGN - && authPayload.authMethod != IkeAuthPayload.AUTH_METHOD_GENERIC_DIGITAL_SIGN) { - throw new AuthenticationFailedException( - "Expected the remote/server to use digital-signature-based authentication" - + " but they used: " - + authPayload.authMethod); - } - - X509Certificate endCert = null; - List<X509Certificate> certList = new LinkedList<>(); - - // TODO: b/122676944 Extract CRL from IkeCrlPayload when we support IkeCrlPayload - for (IkeCertPayload certPayload : certPayloads) { - X509Certificate cert = ((IkeCertX509CertPayload) certPayload).certificate; - - // The first certificate MUST be the end entity certificate. - if (endCert == null) endCert = cert; - certList.add(cert); - } - - if (endCert == null) { - throw new AuthenticationFailedException( - "The remote/server failed to provide a end certificate"); - } - - Set<TrustAnchor> trustAnchorSet = new HashSet<>(); - trustAnchorSet.add(trustAnchor); - - IkeCertPayload.validateCertificates( - endCert, certList, null /*crlList*/, trustAnchorSet); - - IkeAuthDigitalSignPayload signPayload = (IkeAuthDigitalSignPayload) authPayload; - signPayload.verifyInboundSignature( - endCert, - mIkeInitResponseBytes, - mCurrentIkeSaRecord.nonceInitiator, - respIdPayload.getEncodedPayloadBody(), - mIkePrf, - mCurrentIkeSaRecord.getSkPr()); - } - - @Override - public void exitState() { - mRetransmitter.stopRetransmitting(); - } - } - - /** - * CreateIkeLocalIkeAuthInEap represents the state when the IKE library authenticates the client - * with an EAP session. - */ - class CreateIkeLocalIkeAuthInEap extends CreateIkeLocalIkeAuthBase { - private EapAuthenticator mEapAuthenticator; - - @Override - public void enterState() { - IkeSessionOptions.IkeAuthEapConfig ikeAuthEapConfig = - (IkeSessionOptions.IkeAuthEapConfig) mIkeSessionOptions.getLocalAuthConfig(); - - mEapAuthenticator = - mEapAuthenticatorFactory.newEapAuthenticator( - getHandler().getLooper(), - new IkeEapCallback(), - mContext, - ikeAuthEapConfig.mEapConfig); - } - - @Override - public boolean processStateMessage(Message msg) { - switch (msg.what) { - case CMD_EAP_START_EAP_AUTH: - IkeEapPayload ikeEapPayload = (IkeEapPayload) msg.obj; - mEapAuthenticator.processEapMessage(ikeEapPayload.eapMessage); - - return HANDLED; - case CMD_EAP_OUTBOUND_MSG_READY: - byte[] eapMsgBytes = (byte[]) msg.obj; - IkeEapPayload eapPayload = new IkeEapPayload(eapMsgBytes); - - // Setup new retransmitter with EAP response - mRetransmitter = - new EncryptedRetransmitter( - buildIkeAuthReqMessage(Arrays.asList(eapPayload))); - - return HANDLED; - case CMD_EAP_ERRORED: - handleIkeFatalError(new AuthenticationFailedException((Throwable) msg.obj)); - return HANDLED; - case CMD_EAP_FAILED: - AuthenticationFailedException exception = - new AuthenticationFailedException("EAP Authentication Failed"); - - handleIkeFatalError(exception); - return HANDLED; - case CMD_EAP_FINISH_EAP_AUTH: - deferMessage(msg); - transitionTo(mCreateIkeLocalIkeAuthPostEap); - - return HANDLED; - default: - return super.processStateMessage(msg); - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - mRetransmitter.stopRetransmitting(); - - int exchangeType = ikeMessage.ikeHeader.exchangeType; - if (exchangeType != IkeHeader.EXCHANGE_TYPE_IKE_AUTH) { - throw new InvalidSyntaxException( - "Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType); - } - - IkeEapPayload eapPayload = null; - for (IkePayload payload : ikeMessage.ikePayloadList) { - switch (payload.payloadType) { - case IkePayload.PAYLOAD_TYPE_EAP: - eapPayload = (IkeEapPayload) payload; - break; - case IkePayload.PAYLOAD_TYPE_NOTIFY: - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; - if (notifyPayload.isErrorNotify()) { - throw notifyPayload.validateAndBuildIkeException(); - } else { - // Unknown and unexpected status notifications are ignored as per - // RFC7296. - logw( - "Received unknown or unexpected status notifications with" - + " notify type: " - + notifyPayload.notifyType); - } - break; - default: - logw( - "Received unexpected payload in IKE AUTH response. Payload" - + " type: " - + payload.payloadType); - } - } - - if (eapPayload == null) { - throw new AuthenticationFailedException("EAP Payload is missing."); - } - - mEapAuthenticator.processEapMessage(eapPayload.eapMessage); - } catch (IkeProtocolException exception) { - handleIkeFatalError(exception); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException ikeException) { - mRetransmitter.stopRetransmitting(); - handleIkeFatalError(ikeException); - } - - private class IkeEapCallback implements IEapCallback { - @Override - public void onSuccess(byte[] msk, byte[] emsk) { - // Extended MSK not used in IKEv2, drop. - sendMessage(CMD_EAP_FINISH_EAP_AUTH, msk); - } - - @Override - public void onFail() { - sendMessage(CMD_EAP_FAILED); - } - - @Override - public void onResponse(byte[] eapMsg) { - sendMessage(CMD_EAP_OUTBOUND_MSG_READY, eapMsg); - } - - @Override - public void onError(Throwable cause) { - sendMessage(CMD_EAP_ERRORED, cause); - } - } - } - - /** - * CreateIkeLocalIkeAuthPostEap represents the state when the IKE library is performing the - * post-EAP PSK-base authentication run. - */ - class CreateIkeLocalIkeAuthPostEap extends CreateIkeLocalIkeAuthBase { - private byte[] mEapMsk = new byte[0]; - - @Override - public boolean processStateMessage(Message msg) { - switch (msg.what) { - case CMD_EAP_FINISH_EAP_AUTH: - mEapMsk = (byte[]) msg.obj; - - IkeAuthPskPayload pskPayload = - new IkeAuthPskPayload( - mEapMsk, - mIkeInitRequestBytes, - mCurrentIkeSaRecord.nonceResponder, - mInitIdPayload.getEncodedPayloadBody(), - mIkePrf, - mCurrentIkeSaRecord.getSkPi()); - IkeMessage postEapAuthMsg = buildIkeAuthReqMessage(Arrays.asList(pskPayload)); - mRetransmitter = new EncryptedRetransmitter(postEapAuthMsg); - - return HANDLED; - default: - return super.processStateMessage(msg); - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - int exchangeType = ikeMessage.ikeHeader.exchangeType; - if (exchangeType != IkeHeader.EXCHANGE_TYPE_IKE_AUTH) { - throw new InvalidSyntaxException( - "Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType); - } - - // Extract and validate existence of payloads for first Child SA setup. - List<IkePayload> childSaRespPayloads = extractChildPayloadsFromMessage(ikeMessage); - - List<IkePayload> nonChildPayloads = new LinkedList<>(); - nonChildPayloads.addAll(ikeMessage.ikePayloadList); - nonChildPayloads.removeAll(childSaRespPayloads); - - validateIkeAuthRespPostEap(nonChildPayloads); - - performFirstChildNegotiation(mFirstChildReqList, childSaRespPayloads); - } catch (IkeProtocolException e) { - // Notify the remote because they may have set up the IKE SA. - sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); - handleIkeFatalError(e); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException ikeException) { - mRetransmitter.stopRetransmitting(); - // Notify the remote because they may have set up the IKE SA. - sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); - handleIkeFatalError(ikeException); - } - - private void validateIkeAuthRespPostEap(List<IkePayload> payloadList) - throws IkeProtocolException { - IkeAuthPayload authPayload = null; - - for (IkePayload payload : payloadList) { - switch (payload.payloadType) { - case IkePayload.PAYLOAD_TYPE_AUTH: - authPayload = (IkeAuthPayload) payload; - break; - case IkePayload.PAYLOAD_TYPE_NOTIFY: - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; - if (notifyPayload.isErrorNotify()) { - throw notifyPayload.validateAndBuildIkeException(); - } else { - // Unknown and unexpected status notifications are ignored as per - // RFC7296. - logw( - "Received unknown or unexpected status notifications with" - + " notify type: " - + notifyPayload.notifyType); - } - break; - default: - logw( - "Received unexpected payload in IKE AUTH response. Payload" - + " type: " - + payload.payloadType); - } - } - - // Verify existence of payloads - if (authPayload == null) { - throw new AuthenticationFailedException("Post-EAP Auth payload missing."); - } - - authenticatePsk(mEapMsk, authPayload, mRespIdPayload); - } - - @Override - public void exitState() { - mRetransmitter.stopRetransmitting(); - } - } - - private abstract class RekeyIkeHandlerBase extends DeleteBase { - private void validateIkeRekeyCommon(IkeMessage ikeMessage) throws InvalidSyntaxException { - boolean hasSaPayload = false; - boolean hasKePayload = false; - boolean hasNoncePayload = false; - for (IkePayload payload : ikeMessage.ikePayloadList) { - switch (payload.payloadType) { - case IkePayload.PAYLOAD_TYPE_SA: - hasSaPayload = true; - break; - case IkePayload.PAYLOAD_TYPE_KE: - hasKePayload = true; - break; - case IkePayload.PAYLOAD_TYPE_NONCE: - hasNoncePayload = true; - break; - case IkePayload.PAYLOAD_TYPE_VENDOR: - // Vendor payloads allowed, but not verified - break; - case IkePayload.PAYLOAD_TYPE_NOTIFY: - // Notification payloads allowed, but left to handler methods to process. - break; - default: - logw( - "Received unexpected payload in IKE REKEY request. Payload type: " - + payload.payloadType); - } - } - - if (!hasSaPayload || !hasKePayload || !hasNoncePayload) { - throw new InvalidSyntaxException("SA, KE or Nonce payload missing."); - } - } - - @VisibleForTesting - void validateIkeRekeyReq(IkeMessage ikeMessage) throws InvalidSyntaxException { - // Skip validation of exchange type since it has been done during decoding request. - - List<IkeNotifyPayload> notificationPayloads = - ikeMessage.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - for (IkeNotifyPayload notifyPayload : notificationPayloads) { - if (notifyPayload.isErrorNotify()) { - logw("Error notifications invalid in request: " + notifyPayload.notifyType); - } - } - - validateIkeRekeyCommon(ikeMessage); - } - - @VisibleForTesting - void validateIkeRekeyResp(IkeMessage reqMsg, IkeMessage respMsg) - throws InvalidSyntaxException { - int exchangeType = respMsg.ikeHeader.exchangeType; - if (exchangeType != IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA - && exchangeType != IkeHeader.EXCHANGE_TYPE_INFORMATIONAL) { - throw new InvalidSyntaxException( - "Expected Rekey response (CREATE_CHILD_SA or INFORMATIONAL) but received: " - + exchangeType); - } - - List<IkeNotifyPayload> notificationPayloads = - respMsg.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - for (IkeNotifyPayload notifyPayload : notificationPayloads) { - if (notifyPayload.isErrorNotify()) { - // Error notifications found. Stop validation for SA negotiation. - return; - } - } - - validateIkeRekeyCommon(respMsg); - - // Verify DH groups matching - IkeKePayload reqKePayload = - reqMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class); - IkeKePayload respKePayload = - respMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class); - if (reqKePayload.dhGroup != respKePayload.dhGroup) { - throw new InvalidSyntaxException("Received KE payload with mismatched DH group."); - } - } - - // It doesn't make sense to include multiple error notify payloads in one response. If it - // happens, IKE Session will only handle the most severe one. - protected boolean handleErrorNotifyIfExists(IkeMessage respMsg, boolean isSimulRekey) { - IkeNotifyPayload invalidSyntaxNotifyPayload = null; - IkeNotifyPayload tempFailureNotifyPayload = null; - IkeNotifyPayload firstErrorNotifyPayload = null; - - List<IkeNotifyPayload> notificationPayloads = - respMsg.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - for (IkeNotifyPayload notifyPayload : notificationPayloads) { - if (!notifyPayload.isErrorNotify()) continue; - - if (firstErrorNotifyPayload == null) firstErrorNotifyPayload = notifyPayload; - - if (ERROR_TYPE_INVALID_SYNTAX == notifyPayload.notifyType) { - invalidSyntaxNotifyPayload = notifyPayload; - } else if (ERROR_TYPE_TEMPORARY_FAILURE == notifyPayload.notifyType) { - tempFailureNotifyPayload = notifyPayload; - } - } - - // No error Notify Payload included in this response. - if (firstErrorNotifyPayload == null) return NOT_HANDLED; - - // Handle Invalid Syntax if it exists - if (invalidSyntaxNotifyPayload != null) { - try { - IkeProtocolException exception = - invalidSyntaxNotifyPayload.validateAndBuildIkeException(); - handleIkeFatalError(exception); - } catch (InvalidSyntaxException e) { - // Error notify payload has invalid syntax - handleIkeFatalError(e); - } - return HANDLED; - } - - if (tempFailureNotifyPayload != null) { - // Handle Temporary Failure if exists - loge("Received TEMPORARY_FAILURE for rekey IKE. Already handled during decoding."); - } else { - // Handle other errors - loge( - "Received error notification: " - + firstErrorNotifyPayload.notifyType - + " for rekey IKE. Schedule a retry"); - if (!isSimulRekey) { - scheduleRetry(mCurrentIkeSaRecord.getFutureRekeyEvent()); - } - } - - if (isSimulRekey) { - transitionTo(mRekeyIkeRemoteDelete); - } else { - transitionTo(mIdle); - } - return HANDLED; - } - - protected IkeSaRecord validateAndBuildIkeSa( - IkeMessage reqMsg, IkeMessage respMessage, boolean isLocalInit) - throws IkeProtocolException, GeneralSecurityException, IOException { - InetAddress initAddr = isLocalInit ? mLocalAddress : mRemoteAddress; - InetAddress respAddr = isLocalInit ? mRemoteAddress : mLocalAddress; - - Pair<IkeProposal, IkeProposal> negotiatedProposals = null; - try { - IkeSaPayload reqSaPayload = - reqMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - IkeSaPayload respSaPayload = - respMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - - // Throw exception or return valid negotiated proposal with allocated SPIs - negotiatedProposals = - IkeSaPayload.getVerifiedNegotiatedIkeProposalPair( - reqSaPayload, respSaPayload, mRemoteAddress); - IkeProposal reqProposal = negotiatedProposals.first; - IkeProposal respProposal = negotiatedProposals.second; - - Provider provider = IkeMessage.getSecurityProvider(); - IkeMacPrf newPrf; - IkeCipher newCipher; - IkeMacIntegrity newIntegrity = null; - - newCipher = - IkeCipher.create( - respProposal.saProposal.getEncryptionTransforms()[0], provider); - if (!newCipher.isAead()) { - newIntegrity = - IkeMacIntegrity.create( - respProposal.saProposal.getIntegrityTransforms()[0], provider); - } - newPrf = IkeMacPrf.create(respProposal.saProposal.getPrfTransforms()[0], provider); - - // Build new SaRecord - IkeSaRecord newSaRecord = - IkeSaRecord.makeRekeyedIkeSaRecord( - mCurrentIkeSaRecord, - mIkePrf, - reqMsg, - respMessage, - reqProposal.getIkeSpiResource(), - respProposal.getIkeSpiResource(), - newPrf, - newIntegrity == null ? 0 : newIntegrity.getKeyLength(), - newCipher.getKeyLength(), - isLocalInit, - new LocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE)); - - addIkeSaRecord(newSaRecord); - - mIkeCipher = newCipher; - mIkePrf = newPrf; - mIkeIntegrity = newIntegrity; - - return newSaRecord; - } catch (IkeProtocolException | GeneralSecurityException | IOException e) { - if (negotiatedProposals != null) { - negotiatedProposals.first.getIkeSpiResource().close(); - negotiatedProposals.second.getIkeSpiResource().close(); - } - throw e; - } - } - } - - /** RekeyIkeLocalCreate represents state when IKE library initiates Rekey IKE exchange. */ - class RekeyIkeLocalCreate extends RekeyIkeHandlerBase { - protected Retransmitter mRetransmitter; - - @Override - public void enterState() { - try { - mRetransmitter = new EncryptedRetransmitter(buildIkeRekeyReq()); - } catch (IOException e) { - loge("Fail to assign IKE SPI for rekey. Schedule a retry.", e); - scheduleRetry(mCurrentIkeSaRecord.getFutureRekeyEvent()); - transitionTo(mIdle); - } - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - protected void handleTempFailure() { - mTempFailHandler.handleTempFailure(mCurrentIkeSaRecord.getFutureRekeyEvent()); - } - - /** - * Builds a IKE Rekey request, reusing the current proposal - * - * <p>As per RFC 7296, rekey messages are of format: { HDR { SK { SA, Ni, KEi } } } - * - * <p>This method currently reuses agreed upon proposal. - */ - private IkeMessage buildIkeRekeyReq() throws IOException { - // TODO: Evaluate if we need to support different proposals for rekeys - IkeSaProposal[] saProposals = new IkeSaProposal[] {mSaProposal}; - - // No need to allocate SPIs; they will be allocated as part of the - // getRekeyIkeSaRequestPayloads - List<IkePayload> payloadList = - CreateIkeSaHelper.getRekeyIkeSaRequestPayloads(saProposals, mLocalAddress); - - // Build IKE header - IkeHeader ikeHeader = - new IkeHeader( - mCurrentIkeSaRecord.getInitiatorSpi(), - mCurrentIkeSaRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - false /*isResponseMsg*/, - mCurrentIkeSaRecord.isLocalInit, - mCurrentIkeSaRecord.getLocalRequestMessageId()); - - return new IkeMessage(ikeHeader, payloadList); - } - - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - handleDeleteSessionRequest(ikeMessage); - break; - default: - // TODO: Implement simultaneous rekey - buildAndSendErrorNotificationResponse( - mCurrentIkeSaRecord, - ikeMessage.ikeHeader.messageId, - ERROR_TYPE_TEMPORARY_FAILURE); - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - // Validate syntax - validateIkeRekeyResp(mRetransmitter.getMessage(), ikeMessage); - - // Handle error notifications if they exist - if (handleErrorNotifyIfExists(ikeMessage, false /*isSimulRekey*/) == NOT_HANDLED) { - // No error notifications included. Negotiate new SA - mLocalInitNewIkeSaRecord = - validateAndBuildIkeSa( - mRetransmitter.getMessage(), ikeMessage, true /*isLocalInit*/); - transitionTo(mRekeyIkeLocalDelete); - } - - // Stop retransmissions - mRetransmitter.stopRetransmitting(); - } catch (IkeProtocolException e) { - if (e instanceof InvalidSyntaxException) { - handleProcessRespOrSaCreationFailureAndQuit(e); - } else { - handleProcessRespOrSaCreationFailureAndQuit( - new InvalidSyntaxException( - "Error in processing IKE Rekey-Create response", e)); - } - - } catch (GeneralSecurityException | IOException e) { - handleProcessRespOrSaCreationFailureAndQuit( - new IkeInternalException("Error in creating a new IKE SA during rekey", e)); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException ikeException) { - handleProcessRespOrSaCreationFailureAndQuit(ikeException); - } - - private void handleProcessRespOrSaCreationFailureAndQuit(IkeException exception) { - // We don't retry rekey if failure was caused by invalid response or SA creation error. - // Reason is there is no way to notify the remote side the old SA is still alive but the - // new one has failed. - - mRetransmitter.stopRetransmitting(); - - sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); - handleIkeFatalError(exception); - } - } - - /** - * SimulRekeyIkeLocalCreate represents the state where IKE library has replied to rekey request - * sent from the remote and is waiting for a rekey response for a locally initiated rekey - * request. - * - * <p>SimulRekeyIkeLocalCreate extends RekeyIkeLocalCreate so that it can call super class to - * validate incoming rekey response against locally initiated rekey request. - */ - class SimulRekeyIkeLocalCreate extends RekeyIkeLocalCreate { - @Override - public void enterState() { - mRetransmitter = new EncryptedRetransmitter(null); - // TODO: Populate super.mRetransmitter from state initialization data - // Do not send request. - } - - public IkeMessage buildRequest() { - throw new UnsupportedOperationException( - "Do not support sending request in " + getCurrentState().getName()); - } - - @Override - public void exitState() { - // Do nothing. - } - - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_RECEIVE_IKE_PACKET: - ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; - IkeHeader ikeHeader = receivedIkePacket.ikeHeader; - - if (mRemoteInitNewIkeSaRecord == getIkeSaRecordForPacket(ikeHeader)) { - deferMessage(message); - } else { - handleReceivedIkePacket(message); - } - return HANDLED; - - default: - return super.processStateMessage(message); - } - } - - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - deferMessage(message); - return; - default: - // TODO: Add more cases for other types of request. - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - validateIkeRekeyResp(mRetransmitter.getMessage(), ikeMessage); - - // TODO: Check and handle error notifications before SA negotiation - - mLocalInitNewIkeSaRecord = - validateAndBuildIkeSa( - mRetransmitter.getMessage(), ikeMessage, true /*isLocalInit*/); - transitionTo(mSimulRekeyIkeLocalDeleteRemoteDelete); - } catch (IkeProtocolException e) { - // TODO: Handle processing errors. - } catch (GeneralSecurityException e) { - // TODO: Fatal - kill session. - } catch (IOException e) { - // TODO: SPI allocation collided - delete new IKE SA, retry rekey. - } - } - } - - /** RekeyIkeDeleteBase represents common behaviours of deleting stage during rekeying IKE SA. */ - private abstract class RekeyIkeDeleteBase extends DeleteBase { - @Override - public boolean processStateMessage(Message message) { - switch (message.what) { - case CMD_RECEIVE_IKE_PACKET: - ReceivedIkePacket receivedIkePacket = (ReceivedIkePacket) message.obj; - IkeHeader ikeHeader = receivedIkePacket.ikeHeader; - - // Verify that this message is correctly authenticated and encrypted: - IkeSaRecord ikeSaRecord = getIkeSaRecordForPacket(ikeHeader); - boolean isMessageOnNewSa = false; - if (ikeSaRecord != null && mIkeSaRecordSurviving == ikeSaRecord) { - DecodeResult decodeResult = - IkeMessage.decode( - ikeHeader.isResponseMsg - ? ikeSaRecord.getLocalRequestMessageId() - : ikeSaRecord.getRemoteRequestMessageId(), - mIkeIntegrity, - mIkeCipher, - ikeSaRecord, - ikeHeader, - receivedIkePacket.ikePacketBytes, - ikeSaRecord.getCollectedFragments(ikeHeader.isResponseMsg)); - isMessageOnNewSa = - (decodeResult.status == DECODE_STATUS_PROTECTED_ERROR) - || (decodeResult.status == DECODE_STATUS_OK) - || (decodeResult.status == DECODE_STATUS_PARTIAL); - } - - // Authenticated request received on the new/surviving SA; treat it as - // an acknowledgement that the remote has successfully rekeyed. - if (isMessageOnNewSa) { - State nextState = mIdle; - - // This is the first IkeMessage seen on the new SA. It cannot be a response. - // Likewise, if it a request, it must not be a retransmission. Verify msgId. - // If either condition happens, consider rekey a success, but immediately - // kill the session. - if (ikeHeader.isResponseMsg - || ikeSaRecord.getRemoteRequestMessageId() - ikeHeader.messageId - != 0) { - nextState = mDeleteIkeLocalDelete; - } else { - deferMessage(message); - } - - // Locally close old (and losing) IKE SAs. As a result of not waiting for - // delete responses, the old SA can be left in a state where the stored ID - // is no longer correct. However, this finishRekey() call will remove that - // SA, so it doesn't matter. - finishRekey(); - transitionTo(nextState); - } else { - handleReceivedIkePacket(message); - } - - return HANDLED; - default: - return super.processStateMessage(message); - // TODO: Add more cases for other packet types. - } - } - - // Rekey timer for old (and losing) SAs will be cancelled as part of the closing of the SA. - protected void finishRekey() { - mCurrentIkeSaRecord = mIkeSaRecordSurviving; - mLocalInitNewIkeSaRecord = null; - mRemoteInitNewIkeSaRecord = null; - - mIkeSaRecordSurviving = null; - - if (mIkeSaRecordAwaitingLocalDel != null) { - removeIkeSaRecord(mIkeSaRecordAwaitingLocalDel); - mIkeSaRecordAwaitingLocalDel.close(); - mIkeSaRecordAwaitingLocalDel = null; - } - - if (mIkeSaRecordAwaitingRemoteDel != null) { - removeIkeSaRecord(mIkeSaRecordAwaitingRemoteDel); - mIkeSaRecordAwaitingRemoteDel.close(); - mIkeSaRecordAwaitingRemoteDel = null; - } - - synchronized (mChildCbToSessions) { - for (ChildSessionStateMachine child : mChildCbToSessions.values()) { - child.setSkD(mCurrentIkeSaRecord.getSkD()); - } - } - - // TODO: Update prf of all child sessions - } - } - - /** - * SimulRekeyIkeLocalDeleteRemoteDelete represents the deleting stage during simultaneous - * rekeying when IKE library is waiting for both a Delete request and a Delete response. - */ - class SimulRekeyIkeLocalDeleteRemoteDelete extends RekeyIkeDeleteBase { - private Retransmitter mRetransmitter; - - @Override - public void enterState() { - // Detemine surviving IKE SA. According to RFC 7296: "The new IKE SA containing the - // lowest nonce SHOULD be deleted by the node that created it, and the other surviving - // new IKE SA MUST inherit all the Child SAs." - if (mLocalInitNewIkeSaRecord.compareTo(mRemoteInitNewIkeSaRecord) > 0) { - mIkeSaRecordSurviving = mLocalInitNewIkeSaRecord; - mIkeSaRecordAwaitingLocalDel = mCurrentIkeSaRecord; - mIkeSaRecordAwaitingRemoteDel = mRemoteInitNewIkeSaRecord; - } else { - mIkeSaRecordSurviving = mRemoteInitNewIkeSaRecord; - mIkeSaRecordAwaitingLocalDel = mLocalInitNewIkeSaRecord; - mIkeSaRecordAwaitingRemoteDel = mCurrentIkeSaRecord; - } - mRetransmitter = - new EncryptedRetransmitter( - mIkeSaRecordAwaitingLocalDel, - buildIkeDeleteReq(mIkeSaRecordAwaitingLocalDel)); - // TODO: Set timer awaiting for delete request. - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - IkeSaRecord ikeSaRecordForPacket = getIkeSaRecordForPacket(ikeMessage.ikeHeader); - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - try { - validateIkeDeleteReq(ikeMessage, mIkeSaRecordAwaitingRemoteDel); - IkeMessage respMsg = - buildIkeDeleteResp(ikeMessage, mIkeSaRecordAwaitingRemoteDel); - removeIkeSaRecord(mIkeSaRecordAwaitingRemoteDel); - // TODO: Encode and send response and close - // mIkeSaRecordAwaitingRemoteDel. - // TODO: Stop timer awating delete request. - transitionTo(mSimulRekeyIkeLocalDelete); - } catch (InvalidSyntaxException e) { - logd("Validation failed for delete request", e); - // TODO: Shutdown - fatal error - } - return; - default: - // TODO: Reply with TEMPORARY_FAILURE - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - validateIkeDeleteResp(ikeMessage, mIkeSaRecordAwaitingLocalDel); - finishDeleteIkeSaAwaitingLocalDel(); - } catch (InvalidSyntaxException e) { - loge("Invalid syntax on IKE Delete response. Shutting down anyways", e); - finishDeleteIkeSaAwaitingLocalDel(); - } catch (IllegalStateException e) { - // Response received on incorrect SA - cleanUpAndQuit(e); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException exception) { - if (mIkeSaRecordAwaitingLocalDel == ikeSaRecord) { - loge("Invalid syntax on IKE Delete response. Shutting down anyways", exception); - finishDeleteIkeSaAwaitingLocalDel(); - } else { - cleanUpAndQuit( - new IllegalStateException("Delete response received on incorrect SA")); - } - } - - private void finishDeleteIkeSaAwaitingLocalDel() { - mRetransmitter.stopRetransmitting(); - - removeIkeSaRecord(mIkeSaRecordAwaitingLocalDel); - mIkeSaRecordAwaitingLocalDel.close(); - mIkeSaRecordAwaitingLocalDel = null; - - transitionTo(mSimulRekeyIkeRemoteDelete); - } - - @Override - public void exitState() { - finishRekey(); - mRetransmitter.stopRetransmitting(); - // TODO: Stop awaiting delete request timer. - } - } - - /** - * SimulRekeyIkeLocalDelete represents the state when IKE library is waiting for a Delete - * response during simultaneous rekeying. - */ - class SimulRekeyIkeLocalDelete extends RekeyIkeDeleteBase { - private Retransmitter mRetransmitter; - - @Override - public void enterState() { - mRetransmitter = new EncryptedRetransmitter(mIkeSaRecordAwaitingLocalDel, null); - // TODO: Populate mRetransmitter from state initialization data. - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - // Always return a TEMPORARY_FAILURE. In no case should we accept a message on an SA - // that is going away. All messages on the new SA is caught in RekeyIkeDeleteBase - buildAndSendErrorNotificationResponse( - mIkeSaRecordAwaitingLocalDel, - ikeMessage.ikeHeader.messageId, - ERROR_TYPE_TEMPORARY_FAILURE); - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - validateIkeDeleteResp(ikeMessage, mIkeSaRecordAwaitingLocalDel); - finishRekey(); - transitionTo(mIdle); - } catch (InvalidSyntaxException e) { - loge( - "Invalid syntax on IKE Delete response. Shutting down old IKE SA and" - + " finishing rekey", - e); - finishRekey(); - transitionTo(mIdle); - } catch (IllegalStateException e) { - // Response received on incorrect SA - cleanUpAndQuit(e); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException exception) { - if (mIkeSaRecordAwaitingLocalDel == ikeSaRecord) { - loge( - "Invalid syntax on IKE Delete response. Shutting down old IKE SA and" - + " finishing rekey", - exception); - finishRekey(); - transitionTo(mIdle); - } else { - cleanUpAndQuit( - new IllegalStateException("Delete response received on incorrect SA")); - } - } - } - - /** - * SimulRekeyIkeRemoteDelete represents the state that waiting for a Delete request during - * simultaneous rekeying. - */ - class SimulRekeyIkeRemoteDelete extends RekeyIkeDeleteBase { - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - // At this point, the incoming request can ONLY be on mIkeSaRecordAwaitingRemoteDel - if - // it was on the surviving SA, it is deferred and the rekey is finished. It is likewise - // impossible to have this on the local-deleted SA, since the delete has already been - // acknowledged in the SimulRekeyIkeLocalDeleteRemoteDelete state. - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - try { - validateIkeDeleteReq(ikeMessage, mIkeSaRecordAwaitingRemoteDel); - - IkeMessage respMsg = - buildIkeDeleteResp(ikeMessage, mIkeSaRecordAwaitingRemoteDel); - sendEncryptedIkeMessage(mIkeSaRecordAwaitingRemoteDel, respMsg); - - finishRekey(); - transitionTo(mIdle); - } catch (InvalidSyntaxException e) { - // Program error. - cleanUpAndQuit(new IllegalStateException(e)); - } - return; - default: - buildAndSendErrorNotificationResponse( - mIkeSaRecordAwaitingRemoteDel, - ikeMessage.ikeHeader.messageId, - ERROR_TYPE_TEMPORARY_FAILURE); - } - } - } - - /** - * RekeyIkeLocalDelete represents the deleting stage when IKE library is initiating a Rekey - * procedure. - * - * <p>RekeyIkeLocalDelete and SimulRekeyIkeLocalDelete have same behaviours in - * processStateMessage(). While RekeyIkeLocalDelete overrides enterState() and exitState() - * methods for initiating and finishing the deleting stage for IKE rekeying. - */ - class RekeyIkeLocalDelete extends SimulRekeyIkeLocalDelete { - private Retransmitter mRetransmitter; - - @Override - public void enterState() { - mIkeSaRecordSurviving = mLocalInitNewIkeSaRecord; - mIkeSaRecordAwaitingLocalDel = mCurrentIkeSaRecord; - mRetransmitter = - new EncryptedRetransmitter( - mIkeSaRecordAwaitingLocalDel, - buildIkeDeleteReq(mIkeSaRecordAwaitingLocalDel)); - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - public void exitState() { - mRetransmitter.stopRetransmitting(); - } - } - - /** - * RekeyIkeRemoteDelete represents the deleting stage when responding to a Rekey procedure. - * - * <p>RekeyIkeRemoteDelete and SimulRekeyIkeRemoteDelete have same behaviours in - * processStateMessage(). While RekeyIkeLocalDelete overrides enterState() and exitState() - * methods for waiting incoming delete request and for finishing the deleting stage for IKE - * rekeying. - */ - class RekeyIkeRemoteDelete extends SimulRekeyIkeRemoteDelete { - @Override - public void enterState() { - mIkeSaRecordSurviving = mRemoteInitNewIkeSaRecord; - mIkeSaRecordAwaitingRemoteDel = mCurrentIkeSaRecord; - - sendMessageDelayed(TIMEOUT_REKEY_REMOTE_DELETE, REKEY_DELETE_TIMEOUT_MS); - } - - @Override - public boolean processStateMessage(Message message) { - // Intercept rekey delete timeout. Assume rekey succeeded since no retransmissions - // were received. - if (message.what == TIMEOUT_REKEY_REMOTE_DELETE) { - finishRekey(); - transitionTo(mIdle); - - return HANDLED; - } else { - return super.processStateMessage(message); - } - } - - @Override - public void exitState() { - removeMessages(TIMEOUT_REKEY_REMOTE_DELETE); - } - } - - /** DeleteIkeLocalDelete initiates a deletion request of the current IKE Session. */ - class DeleteIkeLocalDelete extends DeleteBase { - private Retransmitter mRetransmitter; - - @Override - public void enterState() { - mRetransmitter = new EncryptedRetransmitter(buildIkeDeleteReq(mCurrentIkeSaRecord)); - } - - @Override - protected void triggerRetransmit() { - mRetransmitter.retransmit(); - } - - @Override - protected void handleRequestIkeMessage( - IkeMessage ikeMessage, int ikeExchangeSubType, Message message) { - switch (ikeExchangeSubType) { - case IKE_EXCHANGE_SUBTYPE_DELETE_IKE: - handleDeleteSessionRequest(ikeMessage); - return; - default: - buildAndSendErrorNotificationResponse( - mCurrentIkeSaRecord, - ikeMessage.ikeHeader.messageId, - ERROR_TYPE_TEMPORARY_FAILURE); - } - } - - @Override - protected void handleResponseIkeMessage(IkeMessage ikeMessage) { - try { - validateIkeDeleteResp(ikeMessage, mCurrentIkeSaRecord); - mUserCbExecutor.execute( - () -> { - mIkeSessionCallback.onClosed(); - }); - - removeIkeSaRecord(mCurrentIkeSaRecord); - mCurrentIkeSaRecord.close(); - mCurrentIkeSaRecord = null; - quitNow(); - } catch (InvalidSyntaxException e) { - handleResponseGenericProcessError(mCurrentIkeSaRecord, e); - } - } - - @Override - protected void handleResponseGenericProcessError( - IkeSaRecord ikeSaRecord, InvalidSyntaxException exception) { - loge("Invalid syntax on IKE Delete response. Shutting down anyways", exception); - handleIkeFatalError(exception); - quitNow(); - } - - @Override - public void exitState() { - mRetransmitter.stopRetransmitting(); - } - } - - /** - * Helper class to generate IKE SA creation payloads, in both request and response directions. - */ - private static class CreateIkeSaHelper { - public static List<IkePayload> getIkeInitSaRequestPayloads( - IkeSaProposal[] saProposals, - long initIkeSpi, - long respIkeSpi, - InetAddress localAddr, - InetAddress remoteAddr, - int localPort, - int remotePort) - throws IOException { - List<IkePayload> payloadList = - getCreateIkeSaPayloads(IkeSaPayload.createInitialIkeSaPayload(saProposals)); - - // Though RFC says Notify-NAT payload is "just after the Ni and Nr payloads (before the - // optional CERTREQ payload)", it also says recipient MUST NOT reject " messages in - // which the payloads were not in the "right" order" due to the lack of clarity of the - // payload order. - payloadList.add( - new IkeNotifyPayload( - NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, - IkeNotifyPayload.generateNatDetectionData( - initIkeSpi, respIkeSpi, localAddr, localPort))); - payloadList.add( - new IkeNotifyPayload( - NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, - IkeNotifyPayload.generateNatDetectionData( - initIkeSpi, respIkeSpi, remoteAddr, remotePort))); - return payloadList; - } - - public static List<IkePayload> getRekeyIkeSaRequestPayloads( - IkeSaProposal[] saProposals, InetAddress localAddr) throws IOException { - if (localAddr == null) { - throw new IllegalArgumentException("Local address was null for rekey"); - } - - return getCreateIkeSaPayloads( - IkeSaPayload.createRekeyIkeSaRequestPayload(saProposals, localAddr)); - } - - public static List<IkePayload> getRekeyIkeSaResponsePayloads( - byte respProposalNumber, IkeSaProposal saProposal, InetAddress localAddr) - throws IOException { - if (localAddr == null) { - throw new IllegalArgumentException("Local address was null for rekey"); - } - - return getCreateIkeSaPayloads( - IkeSaPayload.createRekeyIkeSaResponsePayload( - respProposalNumber, saProposal, localAddr)); - } - - /** - * Builds the initial or rekey IKE creation payloads. - * - * <p>Will return a non-empty list of IkePayloads, the first of which WILL be the SA payload - */ - private static List<IkePayload> getCreateIkeSaPayloads(IkeSaPayload saPayload) - throws IOException { - if (saPayload.proposalList.size() == 0) { - throw new IllegalArgumentException("Invalid SA proposal list - was empty"); - } - - List<IkePayload> payloadList = new ArrayList<>(3); - - payloadList.add(saPayload); - payloadList.add(new IkeNoncePayload()); - - // SaPropoals.Builder guarantees that each SA proposal has at least one DH group. - DhGroupTransform dhGroupTransform = - ((IkeProposal) saPayload.proposalList.get(0)) - .saProposal - .getDhGroupTransforms()[0]; - payloadList.add(new IkeKePayload(dhGroupTransform.id)); - - return payloadList; - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java deleted file mode 100644 index 035af175..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java +++ /dev/null @@ -1,1098 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static android.net.ipsec.ike.IkeManager.getIkeLog; - -import android.annotation.Nullable; -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.ResourceUnavailableException; -import android.net.IpSecManager.SecurityParameterIndex; -import android.net.IpSecManager.SpiUnavailableException; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.IpSecTransform; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IkeSecurityParameterIndex; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.message.IkeKePayload; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultPartial; -import com.android.internal.net.ipsec.ike.message.IkeNoncePayload; -import com.android.internal.net.ipsec.ike.message.IkePayload; - -import dalvik.system.CloseGuard; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.List; - -/** - * SaRecord represents common information of an IKE SA and a Child SA. - * - * <p>When doing rekey, there can be multiple SAs in the same IkeSessionStateMachine or - * ChildSessionStateMachine, where they use same cryptographic algorithms but with different keys. - * We store cryptographic algorithms and unchanged SA configurations in IkeSessionOptions or - * ChildSessionOptions and store changed information including keys, SPIs, and nonces in SaRecord. - * - * <p>All keys are named by the key type plus the source of the traffic this key is protecting. For - * example, "mSkAi" represents the integrity key that protects traffic from the SA initiator to the - * SA responder. - * - * <p>Except for keys, all other paramters (SPIs, nonces and messages) are named by the creator. For - * example, "initSPI" represents a SPI that is created by the SA initiator. - */ -public abstract class SaRecord implements AutoCloseable { - private static ISaRecordHelper sSaRecordHelper = new SaRecordHelper(); - private static IIpSecTransformHelper sIpSecTransformHelper = new IpSecTransformHelper(); - - /** Flag indicates if this SA is locally initiated */ - public final boolean isLocalInit; - - public final byte[] nonceInitiator; - public final byte[] nonceResponder; - - private final byte[] mSkAi; - private final byte[] mSkAr; - private final byte[] mSkEi; - private final byte[] mSkEr; - - private final LocalRequest mFutureRekeyEvent; - - private final CloseGuard mCloseGuard = CloseGuard.get(); - - /** Package private */ - SaRecord( - boolean localInit, - byte[] nonceInit, - byte[] nonceResp, - byte[] skAi, - byte[] skAr, - byte[] skEi, - byte[] skEr, - LocalRequest futureRekeyEvent) { - isLocalInit = localInit; - nonceInitiator = nonceInit; - nonceResponder = nonceResp; - - mSkAi = skAi; - mSkAr = skAr; - mSkEi = skEi; - mSkEr = skEr; - - logKey("SK_ai", skAi); - logKey("SK_ar", skAr); - logKey("SK_ei", skEi); - logKey("SK_er", skEr); - - mFutureRekeyEvent = futureRekeyEvent; - - mCloseGuard.open("close"); - } - - private void logKey(String type, byte[] key) { - getIkeLog().d(getTag(), type + ": " + getIkeLog().pii(key)); - } - - protected abstract String getTag(); - - /** - * Get the integrity key for calculate integrity checksum for an outbound packet. - * - * @return the integrity key in a byte array, which will be empty if integrity algorithm is not - * used in this SA. - */ - public byte[] getOutboundIntegrityKey() { - return isLocalInit ? mSkAi : mSkAr; - } - - /** - * Get the integrity key to authenticate an inbound packet. - * - * @return the integrity key in a byte array, which will be empty if integrity algorithm is not - * used in this SA. - */ - public byte[] getInboundIntegrityKey() { - return isLocalInit ? mSkAr : mSkAi; - } - - /** - * Get the encryption key for protecting an outbound packet. - * - * @return the encryption key in a byte array. - */ - public byte[] getOutboundEncryptionKey() { - return isLocalInit ? mSkEi : mSkEr; - } - - /** - * Get the decryption key for an inbound packet. - * - * @return the decryption key in a byte array. - */ - public byte[] getInboundDecryptionKey() { - return isLocalInit ? mSkEr : mSkEi; - } - - /** Check that the SaRecord was closed properly. */ - @Override - protected void finalize() throws Throwable { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - - @Override - public void close() { - mFutureRekeyEvent.cancel(); - } - - /** Package private */ - LocalRequest getFutureRekeyEvent() { - return mFutureRekeyEvent; - } - - /** Package private */ - @VisibleForTesting - static void setSaRecordHelper(ISaRecordHelper helper) { - sSaRecordHelper = helper; - } - - /** Package private */ - @VisibleForTesting - static void setIpSecTransformHelper(IIpSecTransformHelper helper) { - sIpSecTransformHelper = helper; - } - - /** - * SaRecordHelper implements methods for constructing SaRecord. - * - * <p>Package private - */ - static class SaRecordHelper implements ISaRecordHelper { - @Override - public IkeSaRecord makeFirstIkeSaRecord( - IkeMessage initRequest, - IkeMessage initResponse, - IkeSaRecordConfig ikeSaRecordConfig) - throws GeneralSecurityException { - // Extract nonces - byte[] nonceInit = - initRequest.getPayloadForType( - IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class) - .nonceData; - byte[] nonceResp = - initResponse.getPayloadForType( - IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class) - .nonceData; - - // Get SKEYSEED - byte[] sharedDhKey = getSharedKey(initRequest, initResponse); - byte[] sKeySeed = - ikeSaRecordConfig.prf.generateSKeySeed(nonceInit, nonceResp, sharedDhKey); - - return makeIkeSaRecord(sKeySeed, nonceInit, nonceResp, ikeSaRecordConfig); - } - - @Override - public IkeSaRecord makeRekeyedIkeSaRecord( - IkeSaRecord oldSaRecord, - IkeMacPrf oldPrf, - IkeMessage rekeyRequest, - IkeMessage rekeyResponse, - IkeSaRecordConfig ikeSaRecordConfig) - throws GeneralSecurityException { - // Extract nonces - byte[] nonceInit = - rekeyRequest.getPayloadForType( - IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class) - .nonceData; - byte[] nonceResp = - rekeyResponse.getPayloadForType( - IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class) - .nonceData; - - // Get SKEYSEED - IkeMessage localMsg = ikeSaRecordConfig.isLocalInit ? rekeyRequest : rekeyResponse; - IkeMessage remoteMsg = ikeSaRecordConfig.isLocalInit ? rekeyResponse : rekeyRequest; - - byte[] sharedDhKey = getSharedKey(localMsg, remoteMsg); - byte[] sKeySeed = - oldPrf.generateRekeyedSKeySeed( - oldSaRecord.mSkD, nonceInit, nonceResp, sharedDhKey); - - return makeIkeSaRecord(sKeySeed, nonceInit, nonceResp, ikeSaRecordConfig); - } - - private byte[] getSharedKey(IkeMessage keLocalMessage, IkeMessage keRemoteMessage) - throws GeneralSecurityException { - IkeKePayload keLocalPayload = - keLocalMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class); - IkeKePayload keRemotePayload = - keRemoteMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class); - - return IkeKePayload.getSharedKey( - keLocalPayload.localPrivateKey, keRemotePayload.keyExchangeData); - } - - /** - * Package private method for calculating keys and construct IkeSaRecord. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2), Generating Keying Material</a> - */ - @VisibleForTesting - IkeSaRecord makeIkeSaRecord( - byte[] sKeySeed, - byte[] nonceInit, - byte[] nonceResp, - IkeSaRecordConfig ikeSaRecordConfig) { - // Build data to sign for generating the keying material. - ByteBuffer bufferToSign = - ByteBuffer.allocate( - nonceInit.length + nonceResp.length + 2 * IkePayload.SPI_LEN_IKE); - - IkeSecurityParameterIndex initSpi = ikeSaRecordConfig.initSpi; - IkeSecurityParameterIndex respSpi = ikeSaRecordConfig.respSpi; - IkeMacPrf prf = ikeSaRecordConfig.prf; - int integrityKeyLength = ikeSaRecordConfig.integrityKeyLength; - int encryptionKeyLength = ikeSaRecordConfig.encryptionKeyLength; - - bufferToSign - .put(nonceInit) - .put(nonceResp) - .putLong(initSpi.getSpi()) - .putLong(respSpi.getSpi()); - - // Get length of the keying material according to RFC 7296, 2.13 and 2.14. The length of - // SK_D is always equal to the length of PRF key. - int skDLength = prf.getKeyLength(); - int keyMaterialLen = - skDLength - + 2 * integrityKeyLength - + 2 * encryptionKeyLength - + 2 * prf.getKeyLength(); - byte[] keyMat = prf.generateKeyMat(sKeySeed, bufferToSign.array(), keyMaterialLen); - - // Extract keys. - byte[] skD = new byte[skDLength]; - byte[] skAi = new byte[integrityKeyLength]; - byte[] skAr = new byte[integrityKeyLength]; - byte[] skEi = new byte[encryptionKeyLength]; - byte[] skEr = new byte[encryptionKeyLength]; - byte[] skPi = new byte[prf.getKeyLength()]; - byte[] skPr = new byte[prf.getKeyLength()]; - - ByteBuffer keyMatBuffer = ByteBuffer.wrap(keyMat); - keyMatBuffer.get(skD).get(skAi).get(skAr).get(skEi).get(skEr).get(skPi).get(skPr); - return new IkeSaRecord( - initSpi, - respSpi, - ikeSaRecordConfig.isLocalInit, - nonceInit, - nonceResp, - skD, - skAi, - skAr, - skEi, - skEr, - skPi, - skPr, - ikeSaRecordConfig.futureRekeyEvent); - } - - @Override - public ChildSaRecord makeChildSaRecord( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - ChildSaRecordConfig childSaRecordConfig) - throws GeneralSecurityException, ResourceUnavailableException, - SpiUnavailableException, IOException { - // Extract nonces. Encoding/Decoding of payload list guarantees that there is only one - // nonce payload in the reqPayloads and respPayloads lists - byte[] nonceInit = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_NONCE, - IkeNoncePayload.class, - reqPayloads) - .nonceData; - byte[] nonceResp = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_NONCE, - IkeNoncePayload.class, - respPayloads) - .nonceData; - - // Check if KE Payload exists and get DH shared key. Encoding/Decoding of payload list - // guarantees that there is either no KE payload in the reqPayloads and respPayloads - // lists, or only one KE payload in each list. - byte[] sharedDhKey = new byte[0]; - IkeKePayload keInitPayload = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads); - if (keInitPayload != null) { - IkeKePayload keRespPayload = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class, respPayloads); - sharedDhKey = - IkeKePayload.getSharedKey( - keInitPayload.localPrivateKey, keRespPayload.keyExchangeData); - } - - return makeChildSaRecord(sharedDhKey, nonceInit, nonceResp, childSaRecordConfig); - } - /** - * Package private method for calculating keys, build IpSecTransforms and construct - * ChildSaRecord. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.17">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2), Generating Keying Material for Child SAs</a> - */ - @VisibleForTesting - ChildSaRecord makeChildSaRecord( - byte[] sharedKey, - byte[] nonceInit, - byte[] nonceResp, - ChildSaRecordConfig childSaRecordConfig) - throws ResourceUnavailableException, SpiUnavailableException, IOException { - // Build data to sign for generating the keying material. - ByteBuffer bufferToSign = - ByteBuffer.allocate(sharedKey.length + nonceInit.length + nonceResp.length); - bufferToSign.put(sharedKey).put(nonceInit).put(nonceResp); - - // Get length of the keying material according to RFC 7296, 2.17. - int encryptionKeyLength = childSaRecordConfig.encryptionAlgo.getKeyLength(); - int integrityKeyLength = - childSaRecordConfig.hasIntegrityAlgo - ? childSaRecordConfig.integrityAlgo.getKeyLength() - : 0; - int keyMaterialLen = 2 * encryptionKeyLength + 2 * integrityKeyLength; - byte[] keyMat = - childSaRecordConfig.ikePrf.generateKeyMat( - childSaRecordConfig.skD, bufferToSign.array(), keyMaterialLen); - - // Extract keys according to the order that keys carrying data from initiator to - // responder are taken before keys for the other direction and encryption keys are taken - // before integrity keys. - byte[] skEi = new byte[encryptionKeyLength]; - byte[] skAi = new byte[integrityKeyLength]; - byte[] skEr = new byte[encryptionKeyLength]; - byte[] skAr = new byte[integrityKeyLength]; - - ByteBuffer keyMatBuffer = ByteBuffer.wrap(keyMat); - keyMatBuffer.get(skEi).get(skAi).get(skEr).get(skAr); - - // IpSecTransform for traffic from the initiator - IpSecTransform initTransform = null; - // IpSecTransform for traffic from the responder - IpSecTransform respTransform = null; - try { - // Build IpSecTransform - initTransform = - sIpSecTransformHelper.makeIpSecTransform( - childSaRecordConfig.context, - childSaRecordConfig.initAddress /*source address*/, - childSaRecordConfig.udpEncapSocket, - childSaRecordConfig.respSpi /*destination SPI*/, - childSaRecordConfig.integrityAlgo, - childSaRecordConfig.encryptionAlgo, - skAi, - skEi, - childSaRecordConfig.isTransport); - respTransform = - sIpSecTransformHelper.makeIpSecTransform( - childSaRecordConfig.context, - childSaRecordConfig.respAddress /*source address*/, - childSaRecordConfig.udpEncapSocket, - childSaRecordConfig.initSpi /*destination SPI*/, - childSaRecordConfig.integrityAlgo, - childSaRecordConfig.encryptionAlgo, - skAr, - skEr, - childSaRecordConfig.isTransport); - - int initSpi = childSaRecordConfig.initSpi.getSpi(); - int respSpi = childSaRecordConfig.respSpi.getSpi(); - - boolean isLocalInit = childSaRecordConfig.isLocalInit; - int inSpi = isLocalInit ? initSpi : respSpi; - int outSpi = isLocalInit ? respSpi : initSpi; - IpSecTransform inTransform = isLocalInit ? respTransform : initTransform; - IpSecTransform outTransform = isLocalInit ? initTransform : respTransform; - - return new ChildSaRecord( - inSpi, - outSpi, - isLocalInit, - nonceInit, - nonceResp, - skAi, - skAr, - skEi, - skEr, - inTransform, - outTransform, - childSaRecordConfig.futureRekeyEvent); - - } catch (Exception e) { - if (initTransform != null) initTransform.close(); - if (respTransform != null) respTransform.close(); - throw e; - } - } - } - - /** - * IpSecTransformHelper implements the IIpSecTransformHelper interface for constructing {@link - * IpSecTransform}}. - * - * <p>Package private - */ - static class IpSecTransformHelper implements IIpSecTransformHelper { - private static final String TAG = "IpSecTransformHelper"; - - @Override - public IpSecTransform makeIpSecTransform( - Context context, - InetAddress sourceAddress, - UdpEncapsulationSocket udpEncapSocket, - IpSecManager.SecurityParameterIndex spi, - @Nullable IkeMacIntegrity integrityAlgo, - IkeCipher encryptionAlgo, - byte[] integrityKey, - byte[] encryptionKey, - boolean isTransport) - throws ResourceUnavailableException, SpiUnavailableException, IOException { - IpSecTransform.Builder builder = new IpSecTransform.Builder(context); - - if (encryptionAlgo.isAead()) { - builder.setAuthenticatedEncryption( - encryptionAlgo.buildIpSecAlgorithmWithKey(encryptionKey)); - } else { - builder.setEncryption(encryptionAlgo.buildIpSecAlgorithmWithKey(encryptionKey)); - builder.setAuthentication(integrityAlgo.buildIpSecAlgorithmWithKey(integrityKey)); - } - - if (udpEncapSocket != null && sourceAddress instanceof Inet6Address) { - getIkeLog().wtf(TAG, "Kernel does not support UDP encapsulation for IPv6 SAs"); - } - if (udpEncapSocket != null && sourceAddress instanceof Inet4Address) { - builder.setIpv4Encapsulation(udpEncapSocket, IkeSocket.IKE_SERVER_PORT); - } - - if (isTransport) { - return builder.buildTransportModeTransform(sourceAddress, spi); - } else { - return builder.buildTunnelModeTransform(sourceAddress, spi); - } - } - } - - /** Package private class to group parameters for building a ChildSaRecord. */ - @VisibleForTesting - static final class ChildSaRecordConfig { - public final Context context; - public final SecurityParameterIndex initSpi; - public final SecurityParameterIndex respSpi; - public final InetAddress initAddress; - public final InetAddress respAddress; - @Nullable public final UdpEncapsulationSocket udpEncapSocket; - public final IkeMacPrf ikePrf; - @Nullable public final IkeMacIntegrity integrityAlgo; - public final IkeCipher encryptionAlgo; - public final byte[] skD; - public final boolean isTransport; - public final boolean isLocalInit; - public final boolean hasIntegrityAlgo; - public final ChildLocalRequest futureRekeyEvent; - - ChildSaRecordConfig( - Context context, - SecurityParameterIndex initSpi, - SecurityParameterIndex respSpi, - InetAddress localAddress, - InetAddress remoteAddress, - @Nullable UdpEncapsulationSocket udpEncapSocket, - IkeMacPrf ikePrf, - @Nullable IkeMacIntegrity integrityAlgo, - IkeCipher encryptionAlgo, - byte[] skD, - boolean isTransport, - boolean isLocalInit, - ChildLocalRequest futureRekeyEvent) { - this.context = context; - this.initSpi = initSpi; - this.respSpi = respSpi; - this.initAddress = isLocalInit ? localAddress : remoteAddress; - this.respAddress = isLocalInit ? remoteAddress : localAddress; - this.udpEncapSocket = udpEncapSocket; - this.ikePrf = ikePrf; - this.integrityAlgo = integrityAlgo; - this.encryptionAlgo = encryptionAlgo; - this.skD = skD; - this.isTransport = isTransport; - this.isLocalInit = isLocalInit; - hasIntegrityAlgo = (integrityAlgo != null); - this.futureRekeyEvent = futureRekeyEvent; - } - } - - /** IkeSaRecord represents an IKE SA. */ - public static class IkeSaRecord extends SaRecord implements Comparable<IkeSaRecord> { - private static final String TAG = "IkeSaRecord"; - - /** SPI of IKE SA initiator */ - private final IkeSecurityParameterIndex mInitiatorSpiResource; - /** SPI of IKE SA responder */ - private final IkeSecurityParameterIndex mResponderSpiResource; - - private final byte[] mSkD; - private final byte[] mSkPi; - private final byte[] mSkPr; - - private int mLocalRequestMessageId; - private int mRemoteRequestMessageId; - - private DecodeResultPartial mCollectedReqFragments; - private DecodeResultPartial mCollectedRespFragments; - - private byte[] mLastRecivedReqFirstPacket; - private List<byte[]> mLastSentRespAllPackets; - - /** Package private */ - IkeSaRecord( - IkeSecurityParameterIndex initSpi, - IkeSecurityParameterIndex respSpi, - boolean localInit, - byte[] nonceInit, - byte[] nonceResp, - byte[] skD, - byte[] skAi, - byte[] skAr, - byte[] skEi, - byte[] skEr, - byte[] skPi, - byte[] skPr, - LocalRequest futureRekeyEvent) { - super(localInit, nonceInit, nonceResp, skAi, skAr, skEi, skEr, futureRekeyEvent); - - mInitiatorSpiResource = initSpi; - mResponderSpiResource = respSpi; - - mSkD = skD; - mSkPi = skPi; - mSkPr = skPr; - - mLocalRequestMessageId = 0; - mRemoteRequestMessageId = 0; - - mCollectedReqFragments = null; - mCollectedRespFragments = null; - - logKey("SK_d", skD); - logKey("SK_pi", skPi); - logKey("SK_pr", skPr); - } - - /** - * Package private interface for IkeSessionStateMachien to construct an IkeSaRecord - * instance. - */ - static IkeSaRecord makeFirstIkeSaRecord( - IkeMessage initRequest, - IkeMessage initResponse, - IkeSecurityParameterIndex initSpi, - IkeSecurityParameterIndex respSpi, - IkeMacPrf prf, - int integrityKeyLength, - int encryptionKeyLength, - LocalRequest futureRekeyEvent) - throws GeneralSecurityException { - return sSaRecordHelper.makeFirstIkeSaRecord( - initRequest, - initResponse, - new IkeSaRecordConfig( - initSpi, - respSpi, - prf, - integrityKeyLength, - encryptionKeyLength, - true /*isLocalInit*/, - futureRekeyEvent)); - } - - /** Package private */ - static IkeSaRecord makeRekeyedIkeSaRecord( - IkeSaRecord oldSaRecord, - IkeMacPrf oldPrf, - IkeMessage rekeyRequest, - IkeMessage rekeyResponse, - IkeSecurityParameterIndex initSpi, - IkeSecurityParameterIndex respSpi, - IkeMacPrf prf, - int integrityKeyLength, - int encryptionKeyLength, - boolean isLocalInit, - LocalRequest futureRekeyEvent) - throws GeneralSecurityException { - return sSaRecordHelper.makeRekeyedIkeSaRecord( - oldSaRecord, - oldPrf, - rekeyRequest, - rekeyResponse, - new IkeSaRecordConfig( - initSpi, - respSpi, - prf, - integrityKeyLength, - encryptionKeyLength, - isLocalInit, - futureRekeyEvent)); - } - - private void logKey(String type, byte[] key) { - getIkeLog().d(TAG, type + ": " + getIkeLog().pii(key)); - } - - @Override - protected String getTag() { - return TAG; - } - - /** Package private */ - long getInitiatorSpi() { - return mInitiatorSpiResource.getSpi(); - } - - /** Package private */ - long getResponderSpi() { - return mResponderSpiResource.getSpi(); - } - - /** Package private */ - long getLocalSpi() { - return isLocalInit ? mInitiatorSpiResource.getSpi() : mResponderSpiResource.getSpi(); - } - - /** Package private */ - long getRemoteSpi() { - return isLocalInit ? mResponderSpiResource.getSpi() : mInitiatorSpiResource.getSpi(); - } - - /** Package private */ - byte[] getSkD() { - return mSkD; - } - - /** - * Get the PRF key of IKE initiator for building an outbound Auth Payload. - * - * @return the PRF key in a byte array. - */ - public byte[] getSkPi() { - return mSkPi; - } - - /** - * Get the PRF key of IKE responder for validating an inbound Auth Payload. - * - * @return the PRF key in a byte array. - */ - public byte[] getSkPr() { - return mSkPr; - } - - /** - * Compare with a specific IkeSaRecord - * - * @param record IkeSaRecord to be compared. - * @return a negative integer if input IkeSaRecord contains lowest nonce; a positive integer - * if this IkeSaRecord has lowest nonce; return zero if lowest nonces of two - * IkeSaRecords match. - */ - public int compareTo(IkeSaRecord record) { - // TODO: Implement it b/122924815. - return 1; - } - - /** - * Get current message ID for the local requesting window. - * - * <p>Called for building an outbound request or for validating the message ID of an inbound - * response. - * - * @return the local request message ID. - */ - public int getLocalRequestMessageId() { - return mLocalRequestMessageId; - } - - /** - * Get current message ID for the remote requesting window. - * - * <p>Called for validating the message ID of an inbound request. If the message ID of the - * inbound request is smaller than the current remote message ID by one, it means the - * message is a retransmitted request. - * - * @return the remote request message ID - */ - public int getRemoteRequestMessageId() { - return mRemoteRequestMessageId; - } - - /** - * Increment the local request message ID by one. - * - * <p>It should be called when IKE library has received an authenticated and protected - * response with the correct local request message ID. - */ - public void incrementLocalRequestMessageId() { - mLocalRequestMessageId++; - } - - /** - * Increment the remote request message ID by one. - * - * <p>It should be called when IKE library has received an authenticated and protected - * request with the correct remote request message ID. - */ - public void incrementRemoteRequestMessageId() { - mRemoteRequestMessageId++; - } - - /** Return all collected IKE fragments that have been collected. */ - public DecodeResultPartial getCollectedFragments(boolean isResp) { - return isResp ? mCollectedRespFragments : mCollectedReqFragments; - } - - /** - * Update collected IKE fragments when receiving new IKE fragment. - * - * <p>TODO: b/140264067 Investigate if we need to support reassembling timeout. It is safe - * to do not support it because as an initiator, we will re-transmit the request anyway. As - * a responder, caching these fragments until getting a complete message won't affect - * anything. - */ - public void updateCollectedFragments( - DecodeResultPartial updatedFragments, boolean isResp) { - if (isResp) { - mCollectedRespFragments = updatedFragments; - } else { - mCollectedReqFragments = updatedFragments; - } - } - - /** Reset collected IKE fragemnts */ - public void resetCollectedFragments(boolean isResp) { - updateCollectedFragments(null, isResp); - } - - /** Update first packet of last received request. */ - public void updateLastReceivedReqFirstPacket(byte[] reqPacket) { - mLastRecivedReqFirstPacket = reqPacket; - } - - /** Update all packets of last sent response. */ - public void updateLastSentRespAllPackets(List<byte[]> respPacketList) { - mLastSentRespAllPackets = respPacketList; - } - - /** Returns if received IKE packet is the first packet of a re-transmistted request. */ - public boolean isRetransmittedRequest(byte[] request) { - return Arrays.equals(mLastRecivedReqFirstPacket, request); - } - - /** Get all encoded packets of last sent response. */ - public List<byte[]> getLastSentRespAllPackets() { - return mLastSentRespAllPackets; - } - - /** Release IKE SPI resource. */ - @Override - public void close() { - super.close(); - mInitiatorSpiResource.close(); - mResponderSpiResource.close(); - } - } - - /** Package private class that groups parameters to construct an IkeSaRecord instance. */ - @VisibleForTesting - static class IkeSaRecordConfig { - public final IkeSecurityParameterIndex initSpi; - public final IkeSecurityParameterIndex respSpi; - public final IkeMacPrf prf; - public final int integrityKeyLength; - public final int encryptionKeyLength; - public final boolean isLocalInit; - public final LocalRequest futureRekeyEvent; - - IkeSaRecordConfig( - IkeSecurityParameterIndex initSpi, - IkeSecurityParameterIndex respSpi, - IkeMacPrf prf, - int integrityKeyLength, - int encryptionKeyLength, - boolean isLocalInit, - LocalRequest futureRekeyEvent) { - this.initSpi = initSpi; - this.respSpi = respSpi; - this.prf = prf; - this.integrityKeyLength = integrityKeyLength; - this.encryptionKeyLength = encryptionKeyLength; - this.isLocalInit = isLocalInit; - this.futureRekeyEvent = futureRekeyEvent; - } - } - - /** ChildSaRecord represents an Child SA. */ - public static class ChildSaRecord extends SaRecord implements Comparable<ChildSaRecord> { - private static final String TAG = "ChildSaRecord"; - - /** Locally generated SPI for receiving IPsec Packet. */ - private final int mInboundSpi; - /** Remotely generated SPI for sending IPsec Packet. */ - private final int mOutboundSpi; - - /** IPsec Transform applied to traffic towards the host. */ - private final IpSecTransform mInboundTransform; - /** IPsec Transform applied to traffic from the host. */ - private final IpSecTransform mOutboundTransform; - - /** Package private */ - ChildSaRecord( - int inSpi, - int outSpi, - boolean localInit, - byte[] nonceInit, - byte[] nonceResp, - byte[] skAi, - byte[] skAr, - byte[] skEi, - byte[] skEr, - IpSecTransform inTransform, - IpSecTransform outTransform, - ChildLocalRequest futureRekeyEvent) { - super(localInit, nonceInit, nonceResp, skAi, skAr, skEi, skEr, futureRekeyEvent); - - mInboundSpi = inSpi; - mOutboundSpi = outSpi; - mInboundTransform = inTransform; - mOutboundTransform = outTransform; - } - - /** - * Package private interface for ChildSessionStateMachine to construct a ChildSaRecord - * instance. - */ - static ChildSaRecord makeChildSaRecord( - Context context, - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - SecurityParameterIndex initSpi, - SecurityParameterIndex respSpi, - InetAddress localAddress, - InetAddress remoteAddress, - @Nullable UdpEncapsulationSocket udpEncapSocket, - IkeMacPrf prf, - @Nullable IkeMacIntegrity integrityAlgo, - IkeCipher encryptionAlgo, - byte[] skD, - boolean isTransport, - boolean isLocalInit, - ChildLocalRequest futureRekeyEvent) - throws GeneralSecurityException, ResourceUnavailableException, - SpiUnavailableException, IOException { - return sSaRecordHelper.makeChildSaRecord( - reqPayloads, - respPayloads, - new ChildSaRecordConfig( - context, - initSpi, - respSpi, - localAddress, - remoteAddress, - udpEncapSocket, - prf, - integrityAlgo, - encryptionAlgo, - skD, - isTransport, - isLocalInit, - futureRekeyEvent)); - } - - @Override - protected String getTag() { - return TAG; - } - - /** Package private */ - int getLocalSpi() { - return mInboundSpi; - } - - /** Package private */ - int getRemoteSpi() { - return mOutboundSpi; - } - - /** Package private */ - IpSecTransform getInboundIpSecTransform() { - return mInboundTransform; - } - - /** Package private */ - IpSecTransform getOutboundIpSecTransform() { - return mOutboundTransform; - } - - /** - * Compare with a specific ChildSaRecord - * - * @param record ChildSaRecord to be compared. - * @return a negative integer if input ChildSaRecord contains lowest nonce; a positive - * integer if this ChildSaRecord has lowest nonce; return zero if lowest nonces of two - * ChildSaRecord match. - */ - public int compareTo(ChildSaRecord record) { - // TODO: Implement it b/122924815 - return 1; - } - - /** Release IpSecTransform pair. */ - @Override - public void close() { - super.close(); - mInboundTransform.close(); - mOutboundTransform.close(); - } - } - - /** - * ISaRecordHelper provides a package private interface for constructing SaRecord. - * - * <p>ISaRecordHelper exists so that the interface is injectable for testing. - */ - interface ISaRecordHelper { - /** - * Construct IkeSaRecord as results of IKE initial exchange. - * - * @param initRequest IKE_INIT request. - * @param initResponse IKE_INIT request. - * @param ikeSaRecordConfig that contains IKE SPI resources and negotiated algorithm - * information for constructing an IkeSaRecord instance. - * @return ikeSaRecord for initial IKE SA. - * @throws GeneralSecurityException if the DH public key in the response is invalid. - */ - IkeSaRecord makeFirstIkeSaRecord( - IkeMessage initRequest, - IkeMessage initResponse, - IkeSaRecordConfig ikeSaRecordConfig) - throws GeneralSecurityException; - - /** - * Construct new IkeSaRecord when doing rekey. - * - * @param oldSaRecord old IKE SA - * @param oldPrf the PRF function from the old SA - * @param rekeyRequest Rekey IKE request. - * @param rekeyResponse Rekey IKE response. - * @param ikeSaRecordConfig that contains IKE SPI resources and negotiated algorithm - * information for constructing an IkeSaRecord instance. - * @return ikeSaRecord for new IKE SA. - */ - IkeSaRecord makeRekeyedIkeSaRecord( - IkeSaRecord oldSaRecord, - IkeMacPrf oldPrf, - IkeMessage rekeyRequest, - IkeMessage rekeyResponse, - IkeSaRecordConfig ikeSaRecordConfig) - throws GeneralSecurityException; - - /** - * Construct ChildSaRecord and generate IpSecTransform pairs. - * - * @param reqPayloads payload list in request. - * @param respPayloads payload list in response. - * @param childSaRecordConfig the grouped parameters for constructing ChildSaRecord. - * @return new Child SA. - */ - ChildSaRecord makeChildSaRecord( - List<IkePayload> reqPayloads, - List<IkePayload> respPayloads, - ChildSaRecordConfig childSaRecordConfig) - throws GeneralSecurityException, ResourceUnavailableException, - SpiUnavailableException, IOException; - } - - /** - * IIpSecTransformHelper provides a package private interface to construct {@link - * IpSecTransform} - * - * <p>IIpSecTransformHelper exists so that the interface is injectable for testing. - */ - @VisibleForTesting - interface IIpSecTransformHelper { - /** - * Construct an instance of {@link IpSecTransform} - * - * @param context current context - * @param sourceAddress the source {@code InetAddress} of traffic on sockets of interfaces - * that will use this transform - * @param udpEncapSocket the UDP-Encap socket that allows IpSec traffic to pass through a - * NAT. Null if no NAT exists. - * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed - * traffic - * @param integrityAlgo specifying the authentication algorithm to be applied. - * @param encryptionAlgo specifying the encryption algorithm or authenticated encryption - * algorithm to be applied. - * @param integrityKey the negotiated authentication key to be applied. - * @param encryptionKey the negotiated encryption key to be applied. - * @param isTransport the flag indicates if a transport or a tunnel mode transform will be - * built. - * @return an instance of {@link IpSecTransform} - * @throws ResourceUnavailableException indicating that too many transforms are active - * @throws SpiUnavailableException indicating the rare case where an SPI collides with an - * existing transform - * @throws IOException indicating other errors - */ - IpSecTransform makeIpSecTransform( - Context context, - InetAddress sourceAddress, - UdpEncapsulationSocket udpEncapSocket, - IpSecManager.SecurityParameterIndex spi, - @Nullable IkeMacIntegrity integrityAlgo, - IkeCipher encryptionAlgo, - byte[] integrityKey, - byte[] encryptionKey, - boolean isTransport) - throws ResourceUnavailableException, SpiUnavailableException, IOException; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCipher.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCipher.java deleted file mode 100644 index 33a8b37c..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCipher.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; - -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.SecureRandom; - -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; - -/** - * IkeCipher contains common information of normal and combined mode encryption algorithms. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public abstract class IkeCipher extends IkeCrypto { - private static final int KEY_LEN_3DES = 24; - - private static final int IV_LEN_3DES = 8; - private static final int IV_LEN_AES_CBC = 16; - private static final int IV_LEN_AES_GCM = 8; - - private final boolean mIsAead; - private final int mIvLen; - - protected final Cipher mCipher; - - protected IkeCipher( - int algorithmId, - int keyLength, - int ivLength, - String algorithmName, - boolean isAead, - Provider provider) { - super(algorithmId, keyLength, algorithmName); - mIvLen = ivLength; - mIsAead = isAead; - - try { - mCipher = Cipher.getInstance(getAlgorithmName(), provider); - } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - throw new IllegalArgumentException("Failed to construct " + getTypeString(), e); - } - } - - /** - * Contruct an instance of IkeCipher. - * - * @param encryptionTransform the valid negotiated EncryptionTransform. - * @param provider the security provider. - * @return an instance of IkeCipher. - */ - public static IkeCipher create(EncryptionTransform encryptionTransform, Provider provider) { - int algorithmId = encryptionTransform.id; - - // Use specifiedKeyLength for algorithms with variable key length. Since - // specifiedKeyLength are encoded in bits, it needs to be converted to bytes. - switch (algorithmId) { - case SaProposal.ENCRYPTION_ALGORITHM_3DES: - return new IkeNormalModeCipher( - algorithmId, KEY_LEN_3DES, IV_LEN_3DES, "DESede/CBC/NoPadding", provider); - case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC: - return new IkeNormalModeCipher( - algorithmId, - encryptionTransform.getSpecifiedKeyLength() / 8, - IV_LEN_AES_CBC, - "AES/CBC/NoPadding", - provider); - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8: - // Fall through - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12: - // Fall through - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16: - // Fall through - return new IkeCombinedModeCipher( - algorithmId, - encryptionTransform.getSpecifiedKeyLength() / 8, - IV_LEN_AES_GCM, - "AES/GCM/NoPadding", - provider); - default: - throw new IllegalArgumentException( - "Unrecognized Encryption Algorithm ID: " + algorithmId); - } - } - - /** - * Check if this encryption algorithm is a combined-mode/AEAD algorithm. - * - * @return if this encryption algorithm is a combined-mode/AEAD algorithm. - */ - public boolean isAead() { - return mIsAead; - } - - /** - * Get the block size (in bytes). - * - * @return the block size (in bytes). - */ - public int getBlockSize() { - // Currently all supported encryption algorithms are block ciphers. So the return value will - // not be zero. - return mCipher.getBlockSize(); - } - - /** - * Get initialization vector (IV) length. - * - * @return the IV length. - */ - public int getIvLen() { - return mIvLen; - } - - /** - * Generate initialization vector (IV). - * - * @return the initialization vector (IV). - */ - public byte[] generateIv() { - byte[] iv = new byte[getIvLen()]; - new SecureRandom().nextBytes(iv); - return iv; - } - - protected void validateKeyLenOrThrow(byte[] key) { - if (key.length != getKeyLength()) { - throw new IllegalArgumentException( - "Expected key with length of : " - + getKeyLength() - + " Received key with length of : " - + key.length); - } - } - - /** - * Build IpSecAlgorithm from this IkeCipher. - * - * <p>Build IpSecAlgorithm that represents the same encryption algorithm with this IkeCipher - * instance with provided encryption key. - * - * @param key the encryption key in byte array. - * @return the IpSecAlgorithm. - */ - public abstract IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key); - - /** - * Returns algorithm type as a String. - * - * @return the algorithm type as a String. - */ - @Override - public String getTypeString() { - return "Encryption Algorithm"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCombinedModeCipher.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCombinedModeCipher.java deleted file mode 100644 index 4bb1d34b..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCombinedModeCipher.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import java.nio.ByteBuffer; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Provider; -import java.security.spec.AlgorithmParameterSpec; -import java.util.Arrays; - -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * IkeCipher represents a negotiated combined-mode cipher(AEAD) encryption algorithm. - * - * <p>Checksum mentioned in this class is also known as authentication tag or Integrity Checksum - * Vector(ICV) - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - * @see <a href="https://tools.ietf.org/html/rfc5282">RFC 5282,Using Authenticated Encryption - * Algorithms with the Encrypted Payload of the Internet Key Exchange version 2 (IKEv2) - * Protocol</a> - */ -public final class IkeCombinedModeCipher extends IkeCipher { - private static final int SALT_LEN_GCM = 4; - - private final int mChecksumLen; - private final int mSaltLen; - - /** Package private */ - IkeCombinedModeCipher( - int algorithmId, int keyLength, int ivLength, String algorithmName, Provider provider) { - super(algorithmId, keyLength, ivLength, algorithmName, true /*isAead*/, provider); - switch (algorithmId) { - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8: - mSaltLen = SALT_LEN_GCM; - mChecksumLen = 8; - break; - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12: - mSaltLen = SALT_LEN_GCM; - mChecksumLen = 12; - break; - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16: - mSaltLen = SALT_LEN_GCM; - mChecksumLen = 16; - break; - default: - throw new IllegalArgumentException( - "Unrecognized Encryption Algorithm ID: " + algorithmId); - } - } - - private byte[] doCipherAction( - byte[] data, byte[] additionalAuthData, byte[] keyBytes, byte[] ivBytes, int opmode) - throws AEADBadTagException { - try { - // Provided key consists of encryption/decryption key plus 4-byte salt. Salt is used - // with IV to build the nonce. - ByteBuffer secretKeyAndSaltBuffer = ByteBuffer.wrap(keyBytes); - byte[] secretKeyBytes = new byte[keyBytes.length - mSaltLen]; - byte[] salt = new byte[mSaltLen]; - secretKeyAndSaltBuffer.get(secretKeyBytes); - secretKeyAndSaltBuffer.get(salt); - - SecretKeySpec key = new SecretKeySpec(secretKeyBytes, getAlgorithmName()); - - ByteBuffer nonceBuffer = ByteBuffer.allocate(mSaltLen + ivBytes.length); - nonceBuffer.put(salt); - nonceBuffer.put(ivBytes); - - mCipher.init(opmode, key, getParamSpec(nonceBuffer.array())); - mCipher.updateAAD(additionalAuthData); - - ByteBuffer inputBuffer = ByteBuffer.wrap(data); - - int outputLen = data.length; - if (opmode == Cipher.ENCRYPT_MODE) outputLen += mChecksumLen; - ByteBuffer outputBuffer = ByteBuffer.allocate(outputLen); - - mCipher.doFinal(inputBuffer, outputBuffer); - return outputBuffer.array(); - } catch (AEADBadTagException e) { - // Checksum failed in decryption - throw (AEADBadTagException) e; - } catch (InvalidKeyException - | InvalidAlgorithmParameterException - | IllegalBlockSizeException - | BadPaddingException - | ShortBufferException e) { - String errorMessage = - Cipher.ENCRYPT_MODE == opmode - ? "Failed to encrypt data: " - : "Failed to decrypt data: "; - throw new IllegalArgumentException(errorMessage, e); - } - } - - private AlgorithmParameterSpec getParamSpec(byte[] nonce) { - switch (getAlgorithmId()) { - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8: - // Fall through - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12: - // Fall through - case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16: - return new GCMParameterSpec(mChecksumLen * 8, nonce); - default: - throw new IllegalArgumentException( - "Unrecognized Encryption Algorithm ID: " + getAlgorithmId()); - } - } - - /** - * Encrypt padded data and calculate checksum for it. - * - * @param paddedData the padded data to encrypt. - * @param additionalAuthData additional data to authenticate (also known as associated data). - * @param keyBytes the encryption key. - * @param ivBytes the initialization vector (IV). - * @return the encrypted and padded data with checksum. - */ - public byte[] encrypt( - byte[] paddedData, byte[] additionalAuthData, byte[] keyBytes, byte[] ivBytes) { - try { - return doCipherAction( - paddedData, additionalAuthData, keyBytes, ivBytes, Cipher.ENCRYPT_MODE); - } catch (AEADBadTagException e) { - throw new IllegalArgumentException("Failed to encrypt data: ", e); - } - } - - /** - * Authenticate and decrypt the padded data with checksum. - * - * @param paddedDataWithChecksum the padded data with checksum. - * @param additionalAuthData additional data to authenticate (also known as associated data). - * @param keyBytes the decryption key. - * @param ivBytes the initialization vector (IV). - * @return the decrypted and padded data - * @throws AEADBadTagException if authentication or decryption fails - */ - public byte[] decrypt( - byte[] paddedDataWithChecksum, - byte[] additionalAuthData, - byte[] keyBytes, - byte[] ivBytes) - throws AEADBadTagException { - - byte[] decryptPaddedDataAndAuthTag = - doCipherAction( - paddedDataWithChecksum, - additionalAuthData, - keyBytes, - ivBytes, - Cipher.DECRYPT_MODE); - - int decryptPaddedDataLen = decryptPaddedDataAndAuthTag.length - mChecksumLen; - return Arrays.copyOf(decryptPaddedDataAndAuthTag, decryptPaddedDataLen); - } - - /** - * Gets key length of this algorithm (in bytes). - * - * @return the key length (in bytes). - */ - @Override - public int getKeyLength() { - return super.getKeyLength() + mSaltLen; - } - - /** - * Returns length of checksum. - * - * @return the length of checksum in bytes. - */ - public int getChecksumLen() { - return mChecksumLen; - } - - @Override - public IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key) { - validateKeyLenOrThrow(key); - return new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, key, mChecksumLen * 8); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCrypto.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCrypto.java deleted file mode 100644 index 65a676b5..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCrypto.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -/** - * IkeCrypto is an abstract class that represents common information for all negotiated - * cryptographic algorithms that are used to build IKE SA and protect IKE message. - */ -abstract class IkeCrypto { - private final int mAlgorithmId; - private final int mKeyLength; - private final String mAlgorithmName; - - protected IkeCrypto(int algorithmId, int keyLength, String algorithmName) { - mAlgorithmId = algorithmId; - mKeyLength = keyLength; - mAlgorithmName = algorithmName; - } - - protected int getAlgorithmId() { - return mAlgorithmId; - } - - protected String getAlgorithmName() { - return mAlgorithmName; - } - - /** - * Gets key length of this algorithm (in bytes). - * - * @return the key length (in bytes). - */ - public int getKeyLength() { - return mKeyLength; - } - - /** - * Returns algorithm type as a String. - * - * @return the algorithm type as a String. - */ - public abstract String getTypeString(); -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMac.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMac.java deleted file mode 100644 index ee45cc9e..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMac.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import com.android.internal.net.crypto.KeyGenerationUtils.ByteSigner; - -import java.nio.ByteBuffer; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; - -import javax.crypto.Cipher; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.SecretKeySpec; - -/** - * IkeMac is an abstract class that represents common information for all negotiated algorithms that - * generates Message Authentication Code (MAC), e.g. PRF and integrity algorithm. - */ -abstract class IkeMac extends IkeCrypto implements ByteSigner { - // STOPSHIP: b/130190639 Catch unchecked exceptions, notify users and close the IKE session. - private final boolean mIsEncryptAlgo; - private final Mac mMac; - private final Cipher mCipher; - - protected IkeMac( - int algorithmId, - int keyLength, - String algorithmName, - boolean isEncryptAlgo, - Provider provider) { - super(algorithmId, keyLength, algorithmName); - - mIsEncryptAlgo = isEncryptAlgo; - - try { - if (mIsEncryptAlgo) { - mMac = null; - mCipher = Cipher.getInstance(getAlgorithmName(), provider); - } else { - mMac = Mac.getInstance(getAlgorithmName(), provider); - mCipher = null; - } - } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - throw new IllegalArgumentException("Failed to construct " + getTypeString(), e); - } - } - - /** - * Signs the bytes to generate a Message Authentication Code (MAC). - * - * <p>Caller is responsible for providing valid key according to their use cases (e.g. PSK, - * SK_p, SK_d ...). - * - * @param keyBytes the key to sign data. - * @param dataToSign the data to be signed. - * @return the calculated MAC. - */ - @Override - public byte[] signBytes(byte[] keyBytes, byte[] dataToSign) { - try { - SecretKeySpec secretKey = new SecretKeySpec(keyBytes, getAlgorithmName()); - - if (mIsEncryptAlgo) { - throw new UnsupportedOperationException( - "Do not support " + getTypeString() + " using encryption algorithm."); - } else { - ByteBuffer inputBuffer = ByteBuffer.wrap(dataToSign); - mMac.init(secretKey); - mMac.update(inputBuffer); - - return mMac.doFinal(); - } - } catch (InvalidKeyException | IllegalStateException e) { - throw new IllegalArgumentException("Failed to generate MAC: ", e); - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacIntegrity.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacIntegrity.java deleted file mode 100644 index 8f2173f6..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacIntegrity.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; - -import java.security.Provider; -import java.util.Arrays; - -import javax.crypto.Cipher; -import javax.crypto.Mac; - -/** - * IkeMacIntegrity represents a negotiated integrity algorithm. - * - * <p>For integrity algorithms based on encryption algorithm, all operations will be done by a - * {@link Cipher}. Otherwise, all operations will be done by a {@link Mac}. - * - * <p>@see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ -public class IkeMacIntegrity extends IkeMac { - // STOPSHIP: b/130190639 Catch unchecked exceptions, notify users and close the IKE session. - private final int mChecksumLength; - - private IkeMacIntegrity( - @SaProposal.IntegrityAlgorithm int algorithmId, - int keyLength, - String algorithmName, - boolean isEncryptAlgo, - Provider provider, - int checksumLength) { - super(algorithmId, keyLength, algorithmName, isEncryptAlgo, provider); - mChecksumLength = checksumLength; - } - - /** - * Construct an instance of IkeMacIntegrity. - * - * @param integrityTransform the valid negotiated IntegrityTransform. - * @param provider the security provider. - * @return an instance of IkeMacIntegrity. - */ - public static IkeMacIntegrity create(IntegrityTransform integrityTransform, Provider provider) { - int algorithmId = integrityTransform.id; - - int keyLength = 0; - String algorithmName = ""; - boolean isEncryptAlgo = false; - int checksumLength = 0; - - switch (algorithmId) { - case SaProposal.INTEGRITY_ALGORITHM_NONE: - throw new IllegalArgumentException("Integrity algorithm is not found."); - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96: - keyLength = 20; - algorithmName = "HmacSHA1"; - checksumLength = 12; - break; - case SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96: - keyLength = 16; - isEncryptAlgo = true; - checksumLength = 12; - - // TODO:Set mAlgorithmName - throw new UnsupportedOperationException( - "Do not support INTEGRITY_ALGORITHM_AES_XCBC_96."); - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128: - keyLength = 32; - algorithmName = "HmacSHA256"; - checksumLength = 16; - break; - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192: - keyLength = 48; - algorithmName = "HmacSHA384"; - checksumLength = 24; - break; - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256: - keyLength = 64; - algorithmName = "HmacSHA512"; - checksumLength = 32; - break; - default: - throw new IllegalArgumentException( - "Unrecognized Integrity Algorithm ID: " + algorithmId); - } - - return new IkeMacIntegrity( - algorithmId, keyLength, algorithmName, isEncryptAlgo, provider, checksumLength); - } - - /** - * Gets integrity checksum length (in bytes). - * - * <p>IKE defines a fixed truncation length for each integirty algorithm as its checksum length. - * - * @return the integrity checksum length (in bytes). - */ - public int getChecksumLen() { - return mChecksumLength; - } - - /** - * Signs the bytes to generate an integrity checksum. - * - * @param keyBytes the negotiated integrity key. - * @param dataToAuthenticate the data to authenticate. - * @return the integrity checksum. - */ - public byte[] generateChecksum(byte[] keyBytes, byte[] dataToAuthenticate) { - if (getKeyLength() != keyBytes.length) { - throw new IllegalArgumentException( - "Expected key length: " - + getKeyLength() - + " Received key length: " - + keyBytes.length); - } - - byte[] signedBytes = signBytes(keyBytes, dataToAuthenticate); - return Arrays.copyOfRange(signedBytes, 0, mChecksumLength); - } - - /** - * Build IpSecAlgorithm from this IkeMacIntegrity. - * - * <p>Build IpSecAlgorithm that represents the same integrity algorithm with this - * IkeMacIntegrity instance with provided integrity key. - * - * @param key the integrity key in byte array. - * @return the IpSecAlgorithm. - */ - public IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key) { - if (key.length != getKeyLength()) { - throw new IllegalArgumentException( - "Expected key with length of : " - + getKeyLength() - + " Received key with length of : " - + key.length); - } - - switch (getAlgorithmId()) { - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96: - return new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, key, mChecksumLength * 8); - case SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96: - // TODO:Consider supporting AES128_XCBC in IpSecTransform. - throw new IllegalArgumentException( - "Do not support IpSecAlgorithm with AES128_XCBC."); - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128: - return new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA256, key, mChecksumLength * 8); - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192: - return new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA384, key, mChecksumLength * 8); - case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256: - return new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA512, key, mChecksumLength * 8); - default: - throw new IllegalArgumentException( - "Unrecognized Integrity Algorithm ID: " + getAlgorithmId()); - } - } - - /** - * Returns algorithm type as a String. - * - * @return the algorithm type as a String. - */ - @Override - public String getTypeString() { - return "Integrity Algorithm."; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacPrf.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacPrf.java deleted file mode 100644 index 1d81aaed..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacPrf.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.crypto.KeyGenerationUtils; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; - -import java.nio.ByteBuffer; -import java.security.Provider; - -import javax.crypto.Cipher; -import javax.crypto.Mac; - -/** - * IkeMacPrf represents a negotiated pseudorandom function. - * - * <p>Pseudorandom function is usually used for IKE SA authentication and generating keying - * materials. - * - * <p>For pseudorandom functions based on integrity algorithms, all operations will be done by a - * {@link Mac}. For pseudorandom functions based on encryption algorithms, all operations will be - * done by a {@link Cipher}. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public class IkeMacPrf extends IkeMac { - // STOPSHIP: b/130190639 Catch unchecked exceptions, notify users and close the IKE session. - - private IkeMacPrf( - @SaProposal.PseudorandomFunction int algorithmId, - int keyLength, - String algorithmName, - boolean isEncryptAlgo, - Provider provider) { - super(algorithmId, keyLength, algorithmName, isEncryptAlgo, provider); - } - - /** - * Construct an instance of IkeMacPrf. - * - * @param prfTransform the valid negotiated PrfTransform. - * @param provider the security provider. - * @return an instance of IkeMacPrf. - */ - public static IkeMacPrf create(PrfTransform prfTransform, Provider provider) { - int algorithmId = prfTransform.id; - - int keyLength = 0; - String algorithmName = ""; - boolean isEncryptAlgo = false; - - switch (algorithmId) { - case SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1: - keyLength = 20; - algorithmName = "HmacSHA1"; - break; - case SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC: - keyLength = 16; - isEncryptAlgo = true; - - // TODO:Set mAlgorithmName - throw new UnsupportedOperationException( - "Do not support PSEUDORANDOM_FUNCTION_AES128_XCBC."); - default: - throw new IllegalArgumentException("Unrecognized PRF ID: " + algorithmId); - } - - return new IkeMacPrf(algorithmId, keyLength, algorithmName, isEncryptAlgo, provider); - } - - /** - * Generates SKEYSEED based on the nonces and shared DH secret. - * - * @param nonceInit the IKE initiator nonce. - * @param nonceResp the IKE responder nonce. - * @param sharedDhKey the DH shared key. - * @return the byte array of SKEYSEED. - */ - public byte[] generateSKeySeed(byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey) { - // TODO: If it is PSEUDORANDOM_FUNCTION_AES128_XCBC, only use first 8 bytes of each nonce. - - ByteBuffer keyBuffer = ByteBuffer.allocate(nonceInit.length + nonceResp.length); - keyBuffer.put(nonceInit).put(nonceResp); - - return signBytes(keyBuffer.array(), sharedDhKey); - } - - /** - * Generates a rekey SKEYSEED based on the nonces and shared DH secret. - * - * @param skD the secret for deriving new keys - * @param nonceInit the IKE initiator nonce. - * @param nonceResp the IKE responder nonce. - * @param sharedDhKey the DH shared key. - * @return the byte array of SKEYSEED. - */ - public byte[] generateRekeyedSKeySeed( - byte[] skD, byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey) { - // TODO: If it is PSEUDORANDOM_FUNCTION_AES128_XCBC, only use first 8 bytes of each nonce. - - ByteBuffer dataToSign = - ByteBuffer.allocate(sharedDhKey.length + nonceInit.length + nonceResp.length); - dataToSign.put(sharedDhKey).put(nonceInit).put(nonceResp); - - return signBytes(skD, dataToSign.array()); - } - - /** - * Derives keying materials from IKE/Child SA negotiation. - * - * <p>prf+(K, S) outputs a pseudorandom stream by using negotiated PRF iteratively. In this way - * it can generate long enough keying material containing all the keys for this IKE/Child SA. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296 Internet Key - * Exchange Protocol Version 2 (IKEv2) 2.13. Generating Keying Material </a> - * @param keyBytes the key to sign data. SKEYSEED is used for generating KEYMAT for IKE SA. SK_d - * is used for generating KEYMAT for Child SA. - * @param dataToSign the data to be signed. - * @param keyMaterialLen the length of keying materials. - * @return the byte array of keying materials - */ - public byte[] generateKeyMat(byte[] keyBytes, byte[] dataToSign, int keyMaterialLen) { - return KeyGenerationUtils.prfPlus(this, keyBytes, dataToSign, keyMaterialLen); - } - - /** - * Returns algorithm type as a String. - * - * @return the algorithm type as a String. - */ - @Override - public String getTypeString() { - return "Pseudorandom Function"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeNormalModeCipher.java b/src/java/com/android/internal/net/ipsec/ike/crypto/IkeNormalModeCipher.java deleted file mode 100644 index e4904d4f..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/crypto/IkeNormalModeCipher.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import java.nio.ByteBuffer; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Provider; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * IkeCipher represents a negotiated normal mode encryption algorithm. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class IkeNormalModeCipher extends IkeCipher { - /** Package private */ - IkeNormalModeCipher( - int algorithmId, int keyLength, int ivLength, String algorithmName, Provider provider) { - super(algorithmId, keyLength, ivLength, algorithmName, false /*isAead*/, provider); - } - - private byte[] doCipherAction(byte[] data, byte[] keyBytes, byte[] ivBytes, int opmode) - throws IllegalBlockSizeException { - if (getKeyLength() != keyBytes.length) { - throw new IllegalArgumentException( - "Expected key length: " - + getKeyLength() - + " Received key length: " - + keyBytes.length); - } - try { - SecretKeySpec key = new SecretKeySpec(keyBytes, getAlgorithmName()); - IvParameterSpec iv = new IvParameterSpec(ivBytes); - mCipher.init(opmode, key, iv); - - ByteBuffer inputBuffer = ByteBuffer.wrap(data); - ByteBuffer outputBuffer = ByteBuffer.allocate(data.length); - - mCipher.doFinal(inputBuffer, outputBuffer); - return outputBuffer.array(); - } catch (InvalidKeyException - | InvalidAlgorithmParameterException - | BadPaddingException - | ShortBufferException e) { - String errorMessage = - Cipher.ENCRYPT_MODE == opmode - ? "Failed to encrypt data: " - : "Failed to decrypt data: "; - throw new IllegalArgumentException(errorMessage, e); - } - } - - /** - * Encrypt padded data. - * - * @param paddedData the padded data to encrypt. - * @param keyBytes the encryption key. - * @param ivBytes the initialization vector (IV). - * @return the encrypted and padded data. - */ - public byte[] encrypt(byte[] paddedData, byte[] keyBytes, byte[] ivBytes) { - try { - return doCipherAction(paddedData, keyBytes, ivBytes, Cipher.ENCRYPT_MODE); - } catch (IllegalBlockSizeException e) { - throw new IllegalArgumentException("Failed to encrypt data: ", e); - } - } - - /** - * Decrypt the encrypted and padded data. - * - * @param encryptedData the encrypted and padded data. - * @param keyBytes the decryption key. - * @param ivBytes the initialization vector (IV). - * @return the decrypted and padded data. - * @throws IllegalBlockSizeException if the total encryptedData length is not a multiple of - * block size. - */ - public byte[] decrypt(byte[] encryptedData, byte[] keyBytes, byte[] ivBytes) - throws IllegalBlockSizeException { - return doCipherAction(encryptedData, keyBytes, ivBytes, Cipher.DECRYPT_MODE); - } - - @Override - public IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key) { - validateKeyLenOrThrow(key); - - switch (getAlgorithmId()) { - case SaProposal.ENCRYPTION_ALGORITHM_3DES: - // TODO: Consider supporting 3DES in IpSecTransform. - throw new UnsupportedOperationException("Do not support 3Des encryption."); - case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC: - return new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, key); - default: - throw new IllegalArgumentException( - "Unrecognized Encryption Algorithm ID: " + getAlgorithmId()); - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidKeException.java b/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidKeException.java deleted file mode 100644 index ae2330c7..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidKeException.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.exceptions; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -/** - * This exception is thrown when the received KE payload in the request is different from accepted - * Diffie-Hellman group. - * - * <p>Responder should include an INVALID_KE_PAYLOAD Notify payload in a response message for both - * IKE INI exchange and other SA negotiation exchanges after IKE is setup.. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-1.3">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class InvalidKeException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 2; - - /** - * Construct an instance of InvalidKeException - * - * @param dhGroup the expected DH group - */ - public InvalidKeException(int dhGroup) { - super(ERROR_TYPE_INVALID_KE_PAYLOAD, integerToByteArray(dhGroup, EXPECTED_ERROR_DATA_LEN)); - } - - /** - * Construct a instance of InvalidKeException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public InvalidKeException(byte[] notifyData) { - super(ERROR_TYPE_INVALID_KE_PAYLOAD, notifyData); - } - - /** - * Return the expected DH Group included in this exception. - * - * @return the expected DH Group. - */ - public int getDhGroup() { - return byteArrayToInteger(getErrorData()); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidMessageIdException.java b/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidMessageIdException.java deleted file mode 100644 index 9c5cffa3..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/InvalidMessageIdException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.exceptions; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MESSAGE_ID; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -/** - * This exception is thrown when the message ID is out of window size. - * - * <p>Notifications based on this exception contains the four-octet invalid message ID. It MUST only - * ever be sent in an INFORMATIONAL request. Sending this notification is OPTIONAL, and - * notifications of this type MUST be rate limited. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.3">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class InvalidMessageIdException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 4; - - /** - * Construct a instance of InvalidMessageIdException - * - * @param messageId the invalid Message ID. - */ - public InvalidMessageIdException(int messageId) { - super( - ERROR_TYPE_INVALID_MESSAGE_ID, - integerToByteArray(messageId, EXPECTED_ERROR_DATA_LEN)); - } - - /** - * Construct a instance of InvalidMessageIdException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public InvalidMessageIdException(byte[] notifyData) { - super(ERROR_TYPE_INVALID_MESSAGE_ID, notifyData); - } - - /** - * Return the invalid message ID included in this exception. - * - * @return the message ID. - */ - public int getMessageId() { - return byteArrayToInteger(getErrorData()); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/TemporaryFailureException.java b/src/java/com/android/internal/net/ipsec/ike/exceptions/TemporaryFailureException.java deleted file mode 100644 index 57ef8cda..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/TemporaryFailureException.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.exceptions; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -/** - * This exception is thrown when local node or remote peer receives a request that cannot be - * completed due to a temporary condition such as a rekeying operation. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.7">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class TemporaryFailureException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 0; - - /** - * Construct an instance of TemporaryFailureException. - * - * @param message the descriptive message. - */ - public TemporaryFailureException(String message) { - super(ERROR_TYPE_TEMPORARY_FAILURE, message); - } - - /** - * Construct a instance of TemporaryFailureException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public TemporaryFailureException(byte[] notifyData) { - super(ERROR_TYPE_TEMPORARY_FAILURE, notifyData); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/TsUnacceptableException.java b/src/java/com/android/internal/net/ipsec/ike/exceptions/TsUnacceptableException.java deleted file mode 100644 index ef1152a0..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/TsUnacceptableException.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.exceptions; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -/** - * This exception is thrown if the remote sever proposed unacceptable TS. - * - * <p>If remote server is the exchange initiator, IKE library should respond with a TS_UNACCEPTABLE - * Notify message. If the remote server is the exchange responder, IKE library should initiate a - * Delete IKE exchange and close the IKE Session. - */ -public final class TsUnacceptableException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 0; - - /** Construct an instance of TsUnacceptableException. */ - public TsUnacceptableException() { - super(ERROR_TYPE_TS_UNACCEPTABLE); - } - - /** - * Construct a instance of TsUnacceptableException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public TsUnacceptableException(byte[] notifyData) { - super(ERROR_TYPE_TS_UNACCEPTABLE, notifyData); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/UnrecognizedIkeProtocolException.java b/src/java/com/android/internal/net/ipsec/ike/exceptions/UnrecognizedIkeProtocolException.java deleted file mode 100644 index 3d1d5087..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/UnrecognizedIkeProtocolException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.exceptions; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -/** - * This exception represents an unrecognized error notification in a received response. - * - * <p>When receiving an unrecognized error notification in a response, IKE Session MUST assume that - * the corresponding request has failed entirely. If it is in a request, IKE Session MUST ignore it. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.10.1">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class UnrecognizedIkeProtocolException extends IkeProtocolException { - /** Constructs an instance of UnrecognizedIkeProtocolException */ - public UnrecognizedIkeProtocolException(int errorType, byte[] notifyData) { - super(errorType, notifyData); - } - - @Override - protected boolean isValidDataLength(int dataLen) { - // Unrecognized error does not have an expected error data length. Any non-negative length - // is valid - return dataLen >= 0; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/exceptions/UnsupportedCriticalPayloadException.java b/src/java/com/android/internal/net/ipsec/ike/exceptions/UnsupportedCriticalPayloadException.java deleted file mode 100644 index ab1f75e4..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/exceptions/UnsupportedCriticalPayloadException.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.exceptions; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import java.util.ArrayList; -import java.util.List; - -/** - * This exception is thrown when payload type is not supported and critical bit is set - * - * <p>Include UNSUPPORTED_CRITICAL_PAYLOAD Notify payloads in a response message. Each payload - * contains only one payload type. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.5">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class UnsupportedCriticalPayloadException extends IkeProtocolException { - private static final int EXPECTED_ERROR_DATA_LEN = 1; - - public final List<Integer> payloadTypeList; - - /** - * Construct an instance of UnsupportedCriticalPayloadException. - * - * <p>To keep IkeProtocolException simpler, we only pass the first payload type to the - * superclass which can be retrieved by users. - * - * @param payloadList the list of all unsupported critical payload types. - */ - public UnsupportedCriticalPayloadException(List<Integer> payloadList) { - super( - ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, - integerToByteArray(payloadList.get(0), EXPECTED_ERROR_DATA_LEN)); - payloadTypeList = payloadList; - } - - /** - * Construct a instance of UnsupportedCriticalPayloadException from a notify payload. - * - * @param notifyData the notify data included in the payload. - */ - public UnsupportedCriticalPayloadException(byte[] notifyData) { - super(ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, notifyData); - payloadTypeList = new ArrayList<>(1); - payloadTypeList.add(byteArrayToInteger(notifyData)); - } - - /** - * Return the all the unsupported critical payloads included in this exception. - * - * @return the unsupported critical payload list. - */ - public List<Integer> getUnsupportedCriticalPayloadList() { - return payloadTypeList; - } - - @Override - protected boolean isValidDataLength(int dataLen) { - return EXPECTED_ERROR_DATA_LEN == dataLen; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java deleted file mode 100644 index a4803af4..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import android.annotation.StringDef; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.message.IkeAuthPayload.AuthMethod; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.ProviderException; -import java.security.Signature; -import java.security.SignatureException; -import java.security.cert.X509Certificate; -import java.util.Arrays; - -/** - * IkeAuthDigitalSignPayload represents Authentication Payload using a specific or generic digital - * signature authentication method. - * - * <p>If AUTH_METHOD_RSA_DIGITAL_SIGN is used, then the hash algorithm is SHA1. If - * AUTH_METHOD_GENERIC_DIGITAL_SIGN is used, the signature algorihtm and hash algorithm are - * extracted from authentication data. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - * @see <a href="https://tools.ietf.org/html/rfc7427">RFC 7427, Signature Authentication in the - * Internet Key Exchange Version 2 (IKEv2)</a> - */ -public class IkeAuthDigitalSignPayload extends IkeAuthPayload { - private static final String KEY_ALGO_NAME = "RSA"; - - // Byte arrays of DER encoded identifier ASN.1 objects that indicates the algorithm used to - // generate the signature, extracted from - // <a href="https://tools.ietf.org/html/rfc7427#appendix-A"> RFC 7427. There is no need to - // understand the encoding process. They are just constants to indicate the algorithm type. - private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA1 = { - (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, - (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, - (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, - (byte) 0x05, (byte) 0x05, (byte) 0x00 - }; - private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256 = { - (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, - (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, - (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, - (byte) 0x0b, (byte) 0x05, (byte) 0x00 - }; - private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384 = { - (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, - (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, - (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, - (byte) 0x0c, (byte) 0x05, (byte) 0x00 - }; - private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512 = { - (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, - (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, - (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, - (byte) 0x0d, (byte) 0x05, (byte) 0x00 - }; - - // Length of ASN.1 object length field. - private static final int SIGNATURE_ALGO_ASN1_LEN_LEN = 1; - - // Currently we only support RSA for signature algorithm. - @Retention(RetentionPolicy.SOURCE) - @StringDef({ - SIGNATURE_ALGO_RSA_SHA1, - SIGNATURE_ALGO_RSA_SHA2_256, - SIGNATURE_ALGO_RSA_SHA2_384, - SIGNATURE_ALGO_RSA_SHA2_512 - }) - @VisibleForTesting - @interface SignatureAlgo {} - - @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA1 = "SHA1withRSA"; - @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA2_256 = "SHA256withRSA"; - @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA2_384 = "SHA384withRSA"; - @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA2_512 = "SHA512withRSA"; - - public final String signatureAlgoAndHash; - public final byte[] signature; - - protected IkeAuthDigitalSignPayload( - boolean critical, @AuthMethod int authMethod, byte[] authData) - throws IkeProtocolException { - super(critical, authMethod); - switch (authMethod) { - case AUTH_METHOD_RSA_DIGITAL_SIGN: - signatureAlgoAndHash = SIGNATURE_ALGO_RSA_SHA1; - signature = authData; - break; - case AUTH_METHOD_GENERIC_DIGITAL_SIGN: - ByteBuffer inputBuffer = ByteBuffer.wrap(authData); - - // Get signature algorithm. - int signAlgoLen = Byte.toUnsignedInt(inputBuffer.get()); - byte[] signAlgoBytes = new byte[signAlgoLen]; - inputBuffer.get(signAlgoBytes); - signatureAlgoAndHash = bytesToJavaStandardSignAlgoName(signAlgoBytes); - - // Get signature. - signature = new byte[authData.length - SIGNATURE_ALGO_ASN1_LEN_LEN - signAlgoLen]; - inputBuffer.get(signature); - break; - default: - throw new IllegalArgumentException("Unrecognized authentication method."); - } - } - - /** - * Construct IkeAuthDigitalSignPayload for an outbound IKE packet. - * - * <p>Since IKE library is always a client, outbound IkeAuthDigitalSignPayload always signs IKE - * initiator's SignedOctets, which is concatenation of the IKE_INIT request message, the Nonce - * of IKE responder and the signed ID-Initiator payload body. - * - * <p>Caller MUST validate that the signatureAlgoName is supported by IKE library. - * - * @param signatureAlgoName the name of the algorithm requested. See the Signature section in - * the <a href= "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature"> Java - * Cryptography Architecture Standard Algorithm Name Documentation</a> for information about - * standard algorithm names. - * @param privateKey the private key of the identity whose signature is going to be generated. - * @param ikeInitBytes IKE_INIT request for calculating IKE initiator's SignedOctets. - * @param nonce nonce of IKE responder for calculating IKE initiator's SignedOctets. - * @param idPayloadBodyBytes ID-Initiator payload body for calculating IKE initiator's - * SignedOctets. - * @param ikePrf the negotiated PRF. - * @param prfKeyBytes the negotiated PRF initiator key. - */ - public IkeAuthDigitalSignPayload( - String signatureAlgoName, - PrivateKey privateKey, - byte[] ikeInitBytes, - byte[] nonce, - byte[] idPayloadBodyBytes, - IkeMacPrf ikePrf, - byte[] prfKeyBytes) { - super(false, IkeAuthPayload.AUTH_METHOD_GENERIC_DIGITAL_SIGN); - byte[] dataToSignBytes = - getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes); - - try { - Signature signGen = - Signature.getInstance(signatureAlgoName, IkeMessage.getSecurityProvider()); - signGen.initSign(privateKey); - signGen.update(dataToSignBytes); - - signature = signGen.sign(); - signatureAlgoAndHash = signatureAlgoName; - } catch (SignatureException | InvalidKeyException e) { - throw new IllegalArgumentException("Signature generation failed", e); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Security Provider does not support " - + KEY_ALGO_NAME - + " or " - + signatureAlgoName); - } - } - - private String bytesToJavaStandardSignAlgoName(byte[] signAlgoBytes) - throws AuthenticationFailedException { - if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA1, signAlgoBytes)) { - return SIGNATURE_ALGO_RSA_SHA1; - } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256, signAlgoBytes)) { - return SIGNATURE_ALGO_RSA_SHA2_256; - } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384, signAlgoBytes)) { - return SIGNATURE_ALGO_RSA_SHA2_384; - } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512, signAlgoBytes)) { - return SIGNATURE_ALGO_RSA_SHA2_512; - } else { - throw new AuthenticationFailedException( - "Unrecognized ASN.1 objects for Signature algorithm and Hash"); - } - } - - /** - * Verify received signature in an inbound IKE packet. - * - * <p>Since IKE library is always a client, inbound IkeAuthDigitalSignPayload always signs IKE - * responder's SignedOctets, which is concatenation of the IKE_INIT response message, the Nonce - * of IKE initiator and the signed ID-Responder payload body. - * - * @param certificate received end certificate to verify the signature. - * @param ikeInitBytes IKE_INIT response for calculating IKE responder's SignedOctets. - * @param nonce nonce of IKE initiator for calculating IKE responder's SignedOctets. - * @param idPayloadBodyBytes ID-Responder payload body for calculating IKE responder's - * SignedOctets. - * @param ikePrf the negotiated PRF. - * @param prfKeyBytes the negotiated PRF responder key. - * @throws AuthenticationFailedException if received signature verification failed. - */ - public void verifyInboundSignature( - X509Certificate certificate, - byte[] ikeInitBytes, - byte[] nonce, - byte[] idPayloadBodyBytes, - IkeMacPrf ikePrf, - byte[] prfKeyBytes) - throws AuthenticationFailedException { - byte[] dataToSignBytes = - getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes); - - try { - Signature signValidator = - Signature.getInstance(signatureAlgoAndHash, IkeMessage.getSecurityProvider()); - signValidator.initVerify(certificate); - signValidator.update(dataToSignBytes); - - if (!signValidator.verify(signature)) { - throw new AuthenticationFailedException("Signature verification failed."); - } - } catch (SignatureException | InvalidKeyException e) { - throw new AuthenticationFailedException(e); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Security Provider does not support " + signatureAlgoAndHash); - } - } - - // TODO: Add methods for generating signature. - - @Override - protected void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer) { - // TODO: Implement it. - throw new UnsupportedOperationException( - "It is not supported to encode a " + getTypeString()); - } - - @Override - protected int getAuthDataLength() { - // TODO: Implement it. - throw new UnsupportedOperationException( - "It is not supported to get payload length of " + getTypeString()); - } - - @Override - public String getTypeString() { - return "Auth(Digital Sign)"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java deleted file mode 100644 index 9227dda9..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.ProviderException; -import java.security.cert.CertificateException; -import java.security.cert.TrustAnchor; -import java.security.cert.X509CRL; -import java.security.cert.X509Certificate; -import java.util.List; -import java.util.Set; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -/** - * IkeCertPayload is an abstract class that represents the common information for all Certificate - * Payload carrying different types of certifciate-related data and static methods related to - * certificate validation. - * - * <p>Certificate Payload is only sent in IKE_AUTH exchange. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.6">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public abstract class IkeCertPayload extends IkePayload { - // Length of certificate encoding type field in octets. - private static final int CERT_ENCODING_LEN = 1; - - private static final String KEY_STORE_TYPE_PKCS12 = "PKCS12"; - private static final String CERT_PATH_ALGO_PKIX = "PKIX"; - private static final String CERT_AUTH_TYPE_RSA = "RSA"; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - CERTIFICATE_ENCODING_X509_CERT_SIGNATURE, - CERTIFICATE_ENCODING_CRL, - CERTIFICATE_ENCODING_X509_CERT_HASH_URL, - }) - public @interface CertificateEncoding {} - - public static final int CERTIFICATE_ENCODING_X509_CERT_SIGNATURE = 4; - public static final int CERTIFICATE_ENCODING_CRL = 7; - public static final int CERTIFICATE_ENCODING_X509_CERT_HASH_URL = 12; - - @CertificateEncoding public final int certEncodingType; - - protected IkeCertPayload(@CertificateEncoding int encodingType) { - this(false /*critical*/, encodingType); - } - - protected IkeCertPayload(boolean critical, @CertificateEncoding int encodingType) { - super(PAYLOAD_TYPE_CERT, critical); - certEncodingType = encodingType; - } - - protected static IkeCertPayload getIkeCertPayload(boolean critical, byte[] payloadBody) - throws IkeProtocolException { - ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); - - int certEncodingType = Byte.toUnsignedInt(inputBuffer.get()); - byte[] certData = new byte[payloadBody.length - CERT_ENCODING_LEN]; - inputBuffer.get(certData); - switch (certEncodingType) { - case CERTIFICATE_ENCODING_X509_CERT_SIGNATURE: - return new IkeCertX509CertPayload(critical, certData); - // TODO: Support decoding CRL and "Hash and URL". - case CERTIFICATE_ENCODING_CRL: - throw new AuthenticationFailedException( - "CERTIFICATE_ENCODING_CRL decoding is unsupported."); - case CERTIFICATE_ENCODING_X509_CERT_HASH_URL: - throw new AuthenticationFailedException( - "CERTIFICATE_ENCODING_X509_CERT_HASH_URL decoding is unsupported"); - default: - throw new AuthenticationFailedException("Unrecognized certificate encoding type."); - } - } - - /** - * Validate an end certificate against the received chain and trust anchors. - * - * <p>Validation is done by checking if there is a valid certificate path from end certificate - * to provided trust anchors. - * - * <p>TrustManager implementation used in this method MUST conforms RFC 4158 and RFC 5280. As - * indicated in RFC 4158, Key Identifiers(KIDs) are not required to match during certification - * path validation and cannot be used to eliminate certificates. - * - * <p>Validation will fail if any certficate in the certificate chain is using RSA public key - * whose RSA modulus is smaller than 1024 bits. - * - * @param endCert the end certificate that will be used to verify AUTH payload - * @param certList all the received certificates (include the end certificate) - * @param crlList the certificate revocation lists - * @param trustAnchorSet the certificate authority set to validate the end certificate - * @throws AuthenticationFailedException if there is no valid certificate path - */ - public static void validateCertificates( - X509Certificate endCert, - List<X509Certificate> certList, - @Nullable List<X509CRL> crlList, - Set<TrustAnchor> trustAnchorSet) - throws AuthenticationFailedException { - try { - // TODO: b/122676944 Support CRL checking - - // Create a new keyStore with all trusted anchors - KeyStore keyStore = - KeyStore.getInstance(KEY_STORE_TYPE_PKCS12, IkeMessage.getSecurityProvider()); - keyStore.load(null); - for (TrustAnchor t : trustAnchorSet) { - X509Certificate trustedCert = t.getTrustedCert(); - String alias = - trustedCert.getSubjectX500Principal().getName() + trustedCert.hashCode(); - keyStore.setCertificateEntry(alias, trustedCert); - } - - // Build X509TrustManager with all keystore - TrustManagerFactory tmFactory = - TrustManagerFactory.getInstance( - CERT_PATH_ALGO_PKIX, IkeMessage.getTrustManagerProvider()); - tmFactory.init(keyStore); - - X509TrustManager trustManager = null; - for (TrustManager tm : tmFactory.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - trustManager = (X509TrustManager) tm; - } - } - if (trustManager == null) { - throw new ProviderException( - "X509TrustManager is not supported by " - + IkeMessage.getTrustManagerProvider().getName()); - } - - // Build and validate certificate path - trustManager.checkServerTrusted( - certList.toArray(new X509Certificate[certList.size()]), CERT_AUTH_TYPE_RSA); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException("Algorithm is not supported by the provider", e); - } catch (IOException | KeyStoreException e) { - throw new IllegalStateException(e); - } catch (CertificateException e) { - throw new AuthenticationFailedException(e); - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java deleted file mode 100644 index ddd1b3ed..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java +++ /dev/null @@ -1,767 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import android.annotation.IntDef; -import android.net.LinkAddress; -import android.net.ipsec.ike.IkeManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.util.LinkedList; -import java.util.List; - -/** - * This class represents Configuration payload. - * - * <p>Configuration payload is used to exchange configuration information between IKE peers. - * - * <p>Configuration type should be consistent with the IKE message direction (e.g. a request Config - * Payload should be in a request IKE message). IKE library will ignore Config Payload with - * inconsistent type or with unrecognized type. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.6">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class IkeConfigPayload extends IkePayload { - private static final int CONFIG_HEADER_RESERVED_LEN = 3; - private static final int CONFIG_HEADER_LEN = 4; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - CONFIG_ATTR_INTERNAL_IP4_ADDRESS, - CONFIG_ATTR_INTERNAL_IP4_NETMASK, - CONFIG_ATTR_INTERNAL_IP4_DNS, - CONFIG_ATTR_INTERNAL_IP4_DHCP, - CONFIG_ATTR_APPLICATION_VERSION, - CONFIG_ATTR_INTERNAL_IP6_ADDRESS, - CONFIG_ATTR_INTERNAL_IP6_DNS, - CONFIG_ATTR_INTERNAL_IP4_SUBNET, - CONFIG_ATTR_SUPPORTED_ATTRIBUTES, - CONFIG_ATTR_INTERNAL_IP6_SUBNET - }) - public @interface ConfigAttr {} - - public static final int CONFIG_ATTR_INTERNAL_IP4_ADDRESS = 1; - public static final int CONFIG_ATTR_INTERNAL_IP4_NETMASK = 2; - public static final int CONFIG_ATTR_INTERNAL_IP4_DNS = 3; - public static final int CONFIG_ATTR_INTERNAL_IP4_DHCP = 6; - public static final int CONFIG_ATTR_APPLICATION_VERSION = 7; - public static final int CONFIG_ATTR_INTERNAL_IP6_ADDRESS = 8; - public static final int CONFIG_ATTR_INTERNAL_IP6_DNS = 10; - public static final int CONFIG_ATTR_INTERNAL_IP4_SUBNET = 13; - public static final int CONFIG_ATTR_SUPPORTED_ATTRIBUTES = 14; - public static final int CONFIG_ATTR_INTERNAL_IP6_SUBNET = 15; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({CONFIG_TYPE_REQUEST, CONFIG_TYPE_REPLY}) - public @interface ConfigType {} - - // We don't support CONFIG_TYPE_SET and CONFIG_TYPE_ACK - public static final int CONFIG_TYPE_REQUEST = 1; - public static final int CONFIG_TYPE_REPLY = 2; - - @ConfigType public final int configType; - public final List<ConfigAttribute> recognizedAttributeList; - - /** Build an IkeConfigPayload from a decoded inbound IKE packet. */ - IkeConfigPayload(boolean critical, byte[] payloadBody) throws InvalidSyntaxException { - super(PAYLOAD_TYPE_CP, critical); - - ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); - configType = Byte.toUnsignedInt(inputBuffer.get()); - inputBuffer.get(new byte[CONFIG_HEADER_RESERVED_LEN]); - - recognizedAttributeList = ConfigAttribute.decodeAttributeFrom(inputBuffer); - - // For an inbound Config Payload, IKE library is only able to handle a Config Reply or IKE - // Session attribute requests in a Config Request. For interoperability, netmask validation - // will be skipped for Config(Request) and config payloads with unsupported config types. - if (configType == CONFIG_TYPE_REPLY) { - validateNetmaskInReply(); - } - } - - /** Build an IkeConfigPayload instance for an outbound IKE packet. */ - public IkeConfigPayload(boolean isReply, List<ConfigAttribute> attributeList) { - super(PAYLOAD_TYPE_CP, false); - this.configType = isReply ? CONFIG_TYPE_REPLY : CONFIG_TYPE_REQUEST; - this.recognizedAttributeList = attributeList; - } - - private void validateNetmaskInReply() throws InvalidSyntaxException { - boolean hasIpv4Address = false; - int numNetmask = 0; - - for (ConfigAttribute attr : recognizedAttributeList) { - if (attr.isEmptyValue()) { - IkeManager.getIkeLog() - .d( - "IkeConfigPayload", - "Found empty attribute in a Config Payload reply " - + attr.attributeType); - } - switch (attr.attributeType) { - case CONFIG_ATTR_INTERNAL_IP4_ADDRESS: - if (!attr.isEmptyValue()) hasIpv4Address = true; - break; - case CONFIG_ATTR_INTERNAL_IP4_NETMASK: - if (!attr.isEmptyValue()) numNetmask++; - break; - default: - continue; - } - } - - if (!hasIpv4Address && numNetmask > 0) { - throw new InvalidSyntaxException( - "Found INTERNAL_IP4_NETMASK attribute but no INTERNAL_IP4_ADDRESS attribute"); - } - - if (numNetmask > 1) { - throw new InvalidSyntaxException("Found more than one INTERNAL_IP4_NETMASK"); - } - } - - // TODO: Create ConfigAttribute subclasses for each attribute. - - /** This class represents common information of all Configuration Attributes. */ - public abstract static class ConfigAttribute { - private static final int ATTRIBUTE_TYPE_MASK = 0x7fff; - - private static final int ATTRIBUTE_HEADER_LEN = 4; - private static final int IPV4_PREFIX_LEN_MAX = 32; - - protected static final int VALUE_LEN_NOT_INCLUDED = 0; - - protected static final int IPV4_ADDRESS_LEN = 4; - protected static final int IPV6_ADDRESS_LEN = 16; - protected static final int PREFIX_LEN_LEN = 1; - - public final int attributeType; - - protected ConfigAttribute(int attributeType) { - this.attributeType = attributeType; - } - - protected ConfigAttribute(int attributeType, int len) throws InvalidSyntaxException { - this(attributeType); - - if (!isLengthValid(len)) { - throw new InvalidSyntaxException("Invalid configuration length"); - } - } - - /** - * Package private method to decode ConfigAttribute list from an inbound packet - * - * <p>NegativeArraySizeException and BufferUnderflowException will be caught in {@link - * IkeMessage} - */ - static List<ConfigAttribute> decodeAttributeFrom(ByteBuffer inputBuffer) - throws InvalidSyntaxException { - List<ConfigAttribute> configList = new LinkedList(); - - while (inputBuffer.hasRemaining()) { - int attributeType = Short.toUnsignedInt(inputBuffer.getShort()); - int length = Short.toUnsignedInt(inputBuffer.getShort()); - byte[] value = new byte[length]; - inputBuffer.get(value); - - switch (attributeType) { - case CONFIG_ATTR_INTERNAL_IP4_ADDRESS: - configList.add(new ConfigAttributeIpv4Address(value)); - break; - case CONFIG_ATTR_INTERNAL_IP4_NETMASK: - configList.add(new ConfigAttributeIpv4Netmask(value)); - break; - case CONFIG_ATTR_INTERNAL_IP4_DNS: - configList.add(new ConfigAttributeIpv4Dns(value)); - break; - case CONFIG_ATTR_INTERNAL_IP4_DHCP: - configList.add(new ConfigAttributeIpv4Dhcp(value)); - break; - case CONFIG_ATTR_INTERNAL_IP6_ADDRESS: - configList.add(new ConfigAttributeIpv6Address(value)); - break; - case CONFIG_ATTR_INTERNAL_IP6_DNS: - configList.add(new ConfigAttributeIpv6Dns(value)); - break; - case CONFIG_ATTR_INTERNAL_IP4_SUBNET: - configList.add(new ConfigAttributeIpv4Subnet(value)); - break; - case CONFIG_ATTR_INTERNAL_IP6_SUBNET: - configList.add(new ConfigAttributeIpv6Subnet(value)); - break; - default: - IkeManager.getIkeLog() - .i( - "IkeConfigPayload", - "Unrecognized attribute type: " + attributeType); - } - - // TODO: Support App version and supported attribute list - } - - return configList; - } - - /** Encode attribute to ByteBuffer. */ - public void encodeAttributeToByteBuffer(ByteBuffer buffer) { - buffer.putShort((short) (attributeType & ATTRIBUTE_TYPE_MASK)) - .putShort((short) getValueLength()); - encodeValueToByteBuffer(buffer); - } - - /** Get attribute length. */ - public int getAttributeLen() { - return ATTRIBUTE_HEADER_LEN + getValueLength(); - } - - /** Returns if this attribute value is empty. */ - public boolean isEmptyValue() { - return getValueLength() == VALUE_LEN_NOT_INCLUDED; - } - - protected static int netmaskToPrefixLen(Inet4Address address) { - byte[] bytes = address.getAddress(); - - int netmaskInt = ByteBuffer.wrap(bytes).getInt(); - int leftmostBitMask = 0x80000000; - - int prefixLen = 0; - while ((netmaskInt & leftmostBitMask) == leftmostBitMask) { - prefixLen++; - netmaskInt <<= 1; - } - - if (netmaskInt != 0) { - throw new IllegalArgumentException("Invalid netmask address"); - } - - return prefixLen; - } - - protected static byte[] prefixToNetmaskBytes(int prefixLen) { - if (prefixLen > IPV4_PREFIX_LEN_MAX || prefixLen < 0) { - throw new IllegalArgumentException("Invalid IPv4 prefix length."); - } - - int netmaskInt = (int) (((long) 0xffffffff) << (IPV4_PREFIX_LEN_MAX - prefixLen)); - byte[] netmask = new byte[IPV4_ADDRESS_LEN]; - - ByteBuffer buffer = ByteBuffer.allocate(IPV4_ADDRESS_LEN); - buffer.putInt(netmaskInt); - return buffer.array(); - } - - protected abstract void encodeValueToByteBuffer(ByteBuffer buffer); - - protected abstract int getValueLength(); - - protected abstract boolean isLengthValid(int length); - } - - /** - * This class represents common information of all Configuration Attributes whoses value is one - * IPv4 address or empty. - */ - abstract static class ConfigAttrIpv4AddressBase extends ConfigAttribute { - public final Inet4Address address; - - protected ConfigAttrIpv4AddressBase(int attributeType, Inet4Address address) { - super(attributeType); - this.address = address; - } - - protected ConfigAttrIpv4AddressBase(int attributeType) { - super(attributeType); - this.address = null; - } - - protected ConfigAttrIpv4AddressBase(int attributeType, byte[] value) - throws InvalidSyntaxException { - super(attributeType, value.length); - - if (value.length == VALUE_LEN_NOT_INCLUDED) { - address = null; - return; - } - - try { - address = (Inet4Address) Inet4Address.getByAddress(value); - } catch (UnknownHostException e) { - throw new InvalidSyntaxException("Invalid attribute value", e); - } - } - - @Override - protected void encodeValueToByteBuffer(ByteBuffer buffer) { - if (address == null) { - buffer.put(new byte[0]); - return; - } - - buffer.put(address.getAddress()); - } - - @Override - protected int getValueLength() { - return address == null ? 0 : IPV4_ADDRESS_LEN; - } - - @Override - protected boolean isLengthValid(int length) { - return length == IPV4_ADDRESS_LEN || length == VALUE_LEN_NOT_INCLUDED; - } - } - - /** This class represents Configuration Attribute for IPv4 internal address. */ - public static class ConfigAttributeIpv4Address extends ConfigAttrIpv4AddressBase { - /** Construct an instance with specified address for an outbound packet. */ - public ConfigAttributeIpv4Address(Inet4Address ipv4Address) { - super(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, ipv4Address); - } - - /** - * Construct an instance without a specified address for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv4Address() { - super(CONFIG_ATTR_INTERNAL_IP4_ADDRESS); - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - ConfigAttributeIpv4Address(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, value); - } - } - - /** - * This class represents Configuration Attribute for IPv4 netmask. - * - * <p>Non-empty values for this attribute in a CFG_REQUEST do not make sense and thus MUST NOT - * be included - */ - public static class ConfigAttributeIpv4Netmask extends ConfigAttrIpv4AddressBase { - /** - * Construct an instance without a specified netmask for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv4Netmask() { - super(CONFIG_ATTR_INTERNAL_IP4_NETMASK); - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - public ConfigAttributeIpv4Netmask(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP4_NETMASK, value); - - if (address == null) return; - try { - netmaskToPrefixLen(address); - } catch (IllegalArgumentException e) { - throw new InvalidSyntaxException("Invalid attribute value", e); - } - } - - /** Convert netmask to prefix length. */ - public int getPrefixLen() { - return netmaskToPrefixLen(address); - } - } - - /** This class represents Configuration Attribute for IPv4 DHCP server. */ - public static class ConfigAttributeIpv4Dhcp extends ConfigAttrIpv4AddressBase { - /** Construct an instance with specified DHCP server address for an outbound packet. */ - public ConfigAttributeIpv4Dhcp(Inet4Address ipv4Address) { - super(CONFIG_ATTR_INTERNAL_IP4_DHCP, ipv4Address); - } - - /** - * Construct an instance without a specified DHCP server address for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv4Dhcp() { - super(CONFIG_ATTR_INTERNAL_IP4_DHCP); - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - ConfigAttributeIpv4Dhcp(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP4_DHCP, value); - } - } - - /** - * This class represents Configuration Attribute for IPv4 DNS. - * - * <p>There is no use case to create a DNS request for a specfic DNS server address. As an IKE - * client, we will only support building an empty DNS attribute for an outbound IKE packet. - */ - public static class ConfigAttributeIpv4Dns extends ConfigAttrIpv4AddressBase { - /** Construct an instance with specified DNS server address for an outbound packet. */ - public ConfigAttributeIpv4Dns(Inet4Address ipv4Address) { - super(CONFIG_ATTR_INTERNAL_IP4_DNS, ipv4Address); - } - - /** - * Construct an instance without a specified DNS server address for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv4Dns() { - super(CONFIG_ATTR_INTERNAL_IP4_DNS); - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - ConfigAttributeIpv4Dns(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP4_DNS, value); - } - } - - /** This class represents Configuration Attribute for IPv4 subnets. */ - public static class ConfigAttributeIpv4Subnet extends ConfigAttribute { - private static final int VALUE_LEN = 2 * IPV4_ADDRESS_LEN; - - public final LinkAddress linkAddress; - - /** Construct an instance with specified subnet for an outbound packet. */ - public ConfigAttributeIpv4Subnet(LinkAddress ipv4LinkAddress) { - super(CONFIG_ATTR_INTERNAL_IP4_SUBNET); - - if (!ipv4LinkAddress.isIpv4()) { - throw new IllegalArgumentException("Input LinkAddress is not IPv4"); - } - - this.linkAddress = ipv4LinkAddress; - } - - /** - * Construct an instance without a specified subnet for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv4Subnet() { - super(CONFIG_ATTR_INTERNAL_IP4_SUBNET); - this.linkAddress = null; - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - ConfigAttributeIpv4Subnet(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP4_SUBNET, value.length); - - if (value.length == VALUE_LEN_NOT_INCLUDED) { - linkAddress = null; - return; - } - - try { - ByteBuffer inputBuffer = ByteBuffer.wrap(value); - byte[] ipBytes = new byte[IPV4_ADDRESS_LEN]; - inputBuffer.get(ipBytes); - byte[] netmaskBytes = new byte[IPV4_ADDRESS_LEN]; - inputBuffer.get(netmaskBytes); - - InetAddress address = InetAddress.getByAddress(ipBytes); - InetAddress netmask = InetAddress.getByAddress(netmaskBytes); - validateInet4AddressTypeOrThrow(address); - validateInet4AddressTypeOrThrow(netmask); - - linkAddress = new LinkAddress(address, netmaskToPrefixLen((Inet4Address) netmask)); - } catch (UnknownHostException | IllegalArgumentException e) { - throw new InvalidSyntaxException("Invalid attribute value", e); - } - } - - private void validateInet4AddressTypeOrThrow(InetAddress address) { - if (!(address instanceof Inet4Address)) { - throw new IllegalArgumentException("Input InetAddress is not IPv4"); - } - } - - @Override - protected void encodeValueToByteBuffer(ByteBuffer buffer) { - if (linkAddress == null) { - buffer.put(new byte[VALUE_LEN_NOT_INCLUDED]); - return; - } - byte[] netmaskBytes = prefixToNetmaskBytes(linkAddress.getPrefixLength()); - buffer.put(linkAddress.getAddress().getAddress()).put(netmaskBytes); - } - - @Override - protected int getValueLength() { - return linkAddress == null ? 0 : VALUE_LEN; - } - - @Override - protected boolean isLengthValid(int length) { - return length == VALUE_LEN || length == VALUE_LEN_NOT_INCLUDED; - } - } - - /** - * This class represents common information of all Configuration Attributes whoses value is an - * IPv6 address range. - * - * <p>These attributes contains an IPv6 address and a prefix length. - */ - abstract static class ConfigAttrIpv6AddrRangeBase extends ConfigAttribute { - private static final int VALUE_LEN = IPV6_ADDRESS_LEN + PREFIX_LEN_LEN; - - public final LinkAddress linkAddress; - - protected ConfigAttrIpv6AddrRangeBase(int attributeType, LinkAddress ipv6LinkAddress) { - super(attributeType); - - validateIpv6LinkAddressTypeOrThrow(ipv6LinkAddress); - linkAddress = ipv6LinkAddress; - } - - protected ConfigAttrIpv6AddrRangeBase(int attributeType) { - super(attributeType); - linkAddress = null; - } - - protected ConfigAttrIpv6AddrRangeBase(int attributeType, byte[] value) - throws InvalidSyntaxException { - super(attributeType, value.length); - - if (value.length == VALUE_LEN_NOT_INCLUDED) { - linkAddress = null; - return; - } - - try { - ByteBuffer inputBuffer = ByteBuffer.wrap(value); - byte[] ip6AddrBytes = new byte[IPV6_ADDRESS_LEN]; - inputBuffer.get(ip6AddrBytes); - InetAddress address = InetAddress.getByAddress(ip6AddrBytes); - - int prefixLen = Byte.toUnsignedInt(inputBuffer.get()); - - linkAddress = new LinkAddress(address, prefixLen); - validateIpv6LinkAddressTypeOrThrow(linkAddress); - } catch (UnknownHostException | IllegalArgumentException e) { - throw new InvalidSyntaxException("Invalid attribute value", e); - } - } - - private void validateIpv6LinkAddressTypeOrThrow(LinkAddress address) { - if (!address.isIpv6()) { - throw new IllegalArgumentException("Input LinkAddress is not IPv6"); - } - } - - @Override - protected void encodeValueToByteBuffer(ByteBuffer buffer) { - if (linkAddress == null) { - buffer.put(new byte[VALUE_LEN_NOT_INCLUDED]); - return; - } - - buffer.put(linkAddress.getAddress().getAddress()) - .put((byte) linkAddress.getPrefixLength()); - } - - @Override - protected int getValueLength() { - return linkAddress == null ? VALUE_LEN_NOT_INCLUDED : VALUE_LEN; - } - - @Override - protected boolean isLengthValid(int length) { - return length == VALUE_LEN || length == VALUE_LEN_NOT_INCLUDED; - } - } - - /** This class represents Configuration Attribute for IPv6 internal addresses. */ - public static class ConfigAttributeIpv6Address extends ConfigAttrIpv6AddrRangeBase { - /** Construct an instance with specified address for an outbound packet. */ - public ConfigAttributeIpv6Address(LinkAddress ipv6LinkAddress) { - super(CONFIG_ATTR_INTERNAL_IP6_ADDRESS, ipv6LinkAddress); - } - - /** - * Construct an instance without a specified address for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv6Address() { - super(CONFIG_ATTR_INTERNAL_IP6_ADDRESS); - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - ConfigAttributeIpv6Address(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP6_ADDRESS, value); - } - } - - /** This class represents Configuration Attribute for IPv6 subnets. */ - public static class ConfigAttributeIpv6Subnet extends ConfigAttrIpv6AddrRangeBase { - /** Construct an instance with specified subnet for an outbound packet. */ - public ConfigAttributeIpv6Subnet(LinkAddress ipv6LinkAddress) { - super(CONFIG_ATTR_INTERNAL_IP6_SUBNET, ipv6LinkAddress); - } - - /** - * Construct an instance without a specified subnet for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv6Subnet() { - super(CONFIG_ATTR_INTERNAL_IP6_SUBNET); - } - - /** Construct an instance with a decoded inbound packet. */ - @VisibleForTesting - ConfigAttributeIpv6Subnet(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP6_SUBNET, value); - } - } - - /** - * This class represents Configuration Attribute for IPv6 DNS. - * - * <p>There is no use case to create a DNS request for a specfic DNS server address. As an IKE - * client, we will only support building an empty DNS attribute for an outbound IKE packet. - */ - public static class ConfigAttributeIpv6Dns extends ConfigAttribute { - public final Inet6Address address; - - /** Construct an instance with specified DNS server address for an outbound packet. */ - public ConfigAttributeIpv6Dns(Inet6Address ipv6Address) { - super(CONFIG_ATTR_INTERNAL_IP6_DNS); - address = ipv6Address; - } - - /** - * Construct an instance without a specified DNS server address for an outbound packet. - * - * <p>It must be only used in a configuration request. - */ - public ConfigAttributeIpv6Dns() { - super(CONFIG_ATTR_INTERNAL_IP6_DNS); - this.address = null; - } - - protected ConfigAttributeIpv6Dns(byte[] value) throws InvalidSyntaxException { - super(CONFIG_ATTR_INTERNAL_IP6_DNS, value.length); - - if (value.length == VALUE_LEN_NOT_INCLUDED) { - address = null; - return; - } - - try { - InetAddress netAddress = InetAddress.getByAddress(value); - - if (!(netAddress instanceof Inet6Address)) { - throw new InvalidSyntaxException("Invalid IPv6 address."); - } - address = (Inet6Address) netAddress; - } catch (UnknownHostException e) { - throw new InvalidSyntaxException("Invalid attribute value", e); - } - } - - @Override - protected void encodeValueToByteBuffer(ByteBuffer buffer) { - if (address == null) { - buffer.put(new byte[0]); - return; - } - - buffer.put(address.getAddress()); - } - - @Override - protected int getValueLength() { - return address == null ? 0 : IPV6_ADDRESS_LEN; - } - - @Override - protected boolean isLengthValid(int length) { - return length == IPV6_ADDRESS_LEN || length == VALUE_LEN_NOT_INCLUDED; - } - } - - /** - * Encode Configuration payload to ByteBUffer. - * - * @param nextPayload type of payload that follows this payload. - * @param byteBuffer destination ByteBuffer that stores encoded payload. - */ - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer.put((byte) configType).put(new byte[CONFIG_HEADER_RESERVED_LEN]); - - for (ConfigAttribute attr : recognizedAttributeList) { - attr.encodeAttributeToByteBuffer(byteBuffer); - } - } - - /** - * Get entire payload length. - * - * @return entire payload length. - */ - @Override - protected int getPayloadLength() { - int len = GENERIC_HEADER_LENGTH + CONFIG_HEADER_LEN; - - for (ConfigAttribute attr : recognizedAttributeList) { - len += attr.getAttributeLen(); - } - - return len; - } - - /** - * Return the payload type as a String. - * - * @return the payload type as a String. - */ - @Override - public String getTypeString() { - switch (configType) { - case CONFIG_TYPE_REQUEST: - return "CP(Req)"; - case CONFIG_TYPE_REPLY: - return "CP(Reply)"; - default: - return "CP(" + configType + ")"; - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeEapPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeEapPayload.java deleted file mode 100644 index 5dc14971..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeEapPayload.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import com.android.internal.net.eap.message.EapMessage; - -import java.nio.ByteBuffer; - -/** - * IkeEapPayload represents an EAP payload. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public final class IkeEapPayload extends IkePayload { - public final byte[] eapMessage; - - /** - * Construct an instance of IkeEapPayload from a decoded inbound IKE packet. - * - * <p>Any syntax errors contained in the eapMessage will be handled in {@link EapMessage}. - * - * @param isCritical indicates if this payload is critical. Ignored in supported payload as - * instructed by the RFC 7296. - * @param eapMessage byte-array encoded EapMessage - */ - IkeEapPayload(boolean isCritical, byte[] eapMessage) { - super(PAYLOAD_TYPE_EAP, isCritical); - - this.eapMessage = eapMessage; - } - - /** - * Construct an instance of IkeEapPayload for an outbound IKE EAP message. - * - * <p>This eapMessage is constructed in the IKE session and is guaranteed to have valid syntax. - * - * @param eapMessage byte-array encoded EapMessage - */ - public IkeEapPayload(byte[] eapMessage) { - super(PAYLOAD_TYPE_EAP, false); - - this.eapMessage = eapMessage; - } - - /** - * Encode EAP Payload to ByteBuffer. - * - * @param nextPayload type of payload that follows this payload. - * @param byteBuffer destination ByteBuffer that stores encoded payload. - */ - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer.put(eapMessage); - } - - /** - * Get entire payload length. - * - * @return entire payload length. - */ - @Override - protected int getPayloadLength() { - return GENERIC_HEADER_LENGTH + eapMessage.length; - } - - /** - * Return the payload type as a String. - * - * @return the payload type as a String. - */ - @Override - public String getTypeString() { - return "EAP"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeEncryptedPayloadBody.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeEncryptedPayloadBody.java deleted file mode 100644 index 77cc9f4e..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeEncryptedPayloadBody.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeCombinedModeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeNormalModeCipher; - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.SecureRandom; -import java.util.Arrays; - -import javax.crypto.AEADBadTagException; -import javax.crypto.IllegalBlockSizeException; - -/** - * IkeEncryptedPayloadBody is a package private class that represents an IKE payload substructure - * that contains initialization vector, encrypted content, padding, pad length and integrity - * checksum. - * - * <p>Both an Encrypted Payload (IkeSkPayload) and an EncryptedFragmentPayload (IkeSkfPayload) - * consists of an IkeEncryptedPayloadBody instance. - * - * <p>When using normal cipher with separate integrity algorithm, data to authenticate includes - * bytes from beginning of IKE header to the pad length, which are concatenation of IKE header, - * current payload header, iv and encrypted and padded data. - * - * <p>When using AEAD, additional authentication data(also known as) associated data is required. It - * MUST include bytes from beginning of IKE header to the last octet of the Payload Header of the - * Encrypted Payload. Note fragment number and total fragments are also included if Encrypted - * Payload is SKF. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#page-105">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - * @see <a href="https://tools.ietf.org/html/rfc7383#page-6">RFC 7383, Internet Key Exchange - * Protocol Version 2 (IKEv2) Message Fragmentation</a> - */ -final class IkeEncryptedPayloadBody { - // Length of pad length field. - private static final int PAD_LEN_LEN = 1; - - private final byte[] mUnencryptedData; - private final byte[] mEncryptedAndPaddedData; - private final byte[] mIv; - private final byte[] mIntegrityChecksum; - - /** - * Package private constructor for constructing an instance of IkeEncryptedPayloadBody from - * decrypting an incoming packet. - */ - IkeEncryptedPayloadBody( - byte[] message, - int encryptedBodyOffset, - IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException { - ByteBuffer inputBuffer = ByteBuffer.wrap(message); - - // Skip IKE header and generic payload header (and SKF header) - inputBuffer.get(new byte[encryptedBodyOffset]); - - // Extract bytes for authentication and decryption. - int expectedIvLen = decryptCipher.getIvLen(); - mIv = new byte[expectedIvLen]; - - int checksumLen = getChecksum(integrityMac, decryptCipher); - int encryptedDataLen = message.length - (encryptedBodyOffset + expectedIvLen + checksumLen); - // IkeMessage will catch exception if encryptedDataLen is negative. - mEncryptedAndPaddedData = new byte[encryptedDataLen]; - - mIntegrityChecksum = new byte[checksumLen]; - inputBuffer.get(mIv).get(mEncryptedAndPaddedData).get(mIntegrityChecksum); - - if (decryptCipher.isAead()) { - byte[] dataToAuthenticate = Arrays.copyOfRange(message, 0, encryptedBodyOffset); - mUnencryptedData = - combinedModeDecrypt( - (IkeCombinedModeCipher) decryptCipher, - mEncryptedAndPaddedData, - mIntegrityChecksum, - dataToAuthenticate, - decryptionKey, - mIv); - } else { - byte[] dataToAuthenticate = - Arrays.copyOfRange(message, 0, message.length - checksumLen); - - validateInboundChecksumOrThrow( - dataToAuthenticate, integrityMac, integrityKey, mIntegrityChecksum); - mUnencryptedData = - normalModeDecrypt( - mEncryptedAndPaddedData, - (IkeNormalModeCipher) decryptCipher, - decryptionKey, - mIv); - } - } - - /** - * Package private constructor for constructing an instance of IkeEncryptedPayloadBody for - * building an outbound packet. - */ - IkeEncryptedPayloadBody( - IkeHeader ikeHeader, - @IkePayload.PayloadType int firstPayloadType, - byte[] skfHeaderBytes, - byte[] unencryptedPayloads, - IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - byte[] integrityKey, - byte[] encryptionKey) { - this( - ikeHeader, - firstPayloadType, - skfHeaderBytes, - unencryptedPayloads, - integrityMac, - encryptCipher, - integrityKey, - encryptionKey, - encryptCipher.generateIv(), - calculatePadding(unencryptedPayloads.length, encryptCipher.getBlockSize())); - } - - /** Package private constructor only for testing. */ - @VisibleForTesting - IkeEncryptedPayloadBody( - IkeHeader ikeHeader, - @IkePayload.PayloadType int firstPayloadType, - byte[] skfHeaderBytes, - byte[] unencryptedPayloads, - IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - byte[] integrityKey, - byte[] encryptionKey, - byte[] iv, - byte[] padding) { - mUnencryptedData = unencryptedPayloads; - - mIv = iv; - if (encryptCipher.isAead()) { - byte[] paddedDataWithChecksum = - combinedModeEncrypt( - (IkeCombinedModeCipher) encryptCipher, - ikeHeader, - firstPayloadType, - skfHeaderBytes, - unencryptedPayloads, - encryptionKey, - iv, - padding); - - int checkSumLen = ((IkeCombinedModeCipher) encryptCipher).getChecksumLen(); - mIntegrityChecksum = new byte[checkSumLen]; - mEncryptedAndPaddedData = new byte[paddedDataWithChecksum.length - checkSumLen]; - - ByteBuffer buffer = ByteBuffer.wrap(paddedDataWithChecksum); - buffer.get(mEncryptedAndPaddedData); - buffer.get(mIntegrityChecksum); - } else { - // Encrypt data - mEncryptedAndPaddedData = - normalModeEncrypt( - unencryptedPayloads, - (IkeNormalModeCipher) encryptCipher, - encryptionKey, - iv, - padding); - // Calculate checksum - mIntegrityChecksum = - generateOutboundChecksum( - ikeHeader, - firstPayloadType, - skfHeaderBytes, - integrityMac, - iv, - mEncryptedAndPaddedData, - integrityKey); - } - } - - private int getChecksum(IkeMacIntegrity integrityMac, IkeCipher decryptCipher) { - if (decryptCipher.isAead()) { - return ((IkeCombinedModeCipher) decryptCipher).getChecksumLen(); - } else { - return integrityMac.getChecksumLen(); - } - } - - /** Package private for testing */ - @VisibleForTesting - static byte[] generateOutboundChecksum( - IkeHeader ikeHeader, - @IkePayload.PayloadType int firstPayloadType, - byte[] skfHeaderBytes, - IkeMacIntegrity integrityMac, - byte[] iv, - byte[] encryptedAndPaddedData, - byte[] integrityKey) { - // Length from encrypted payload header to the Pad Length field - int encryptedPayloadHeaderToPadLen = - IkePayload.GENERIC_HEADER_LENGTH - + skfHeaderBytes.length - + iv.length - + encryptedAndPaddedData.length; - - // Calculate length of authentication data and allocate ByteBuffer. - int dataToAuthenticateLength = IkeHeader.IKE_HEADER_LENGTH + encryptedPayloadHeaderToPadLen; - ByteBuffer authenticatedSectionBuffer = ByteBuffer.allocate(dataToAuthenticateLength); - - // Build data to authenticate. - int encryptedPayloadLength = encryptedPayloadHeaderToPadLen + integrityMac.getChecksumLen(); - ikeHeader.encodeToByteBuffer(authenticatedSectionBuffer, encryptedPayloadLength); - IkePayload.encodePayloadHeaderToByteBuffer( - firstPayloadType, encryptedPayloadLength, authenticatedSectionBuffer); - authenticatedSectionBuffer.put(skfHeaderBytes).put(iv).put(encryptedAndPaddedData); - - // Calculate checksum - return integrityMac.generateChecksum(integrityKey, authenticatedSectionBuffer.array()); - } - - /** Package private for testing */ - @VisibleForTesting - static void validateInboundChecksumOrThrow( - byte[] dataToAuthenticate, - IkeMacIntegrity integrityMac, - byte[] integrityKey, - byte[] integrityChecksum) - throws GeneralSecurityException { - // TODO: Make it package private and add test. - int checkSumLen = integrityChecksum.length; - byte[] calculatedChecksum = integrityMac.generateChecksum(integrityKey, dataToAuthenticate); - - if (!Arrays.equals(integrityChecksum, calculatedChecksum)) { - throw new GeneralSecurityException("Message authentication failed."); - } - } - - /** Package private for testing */ - @VisibleForTesting - static byte[] normalModeEncrypt( - byte[] dataToEncrypt, - IkeNormalModeCipher encryptCipher, - byte[] encryptionKey, - byte[] iv, - byte[] padding) { - byte[] paddedData = getPaddedData(dataToEncrypt, padding); - - // Encrypt data. - return encryptCipher.encrypt(paddedData, encryptionKey, iv); - } - - /** Package private for testing */ - @VisibleForTesting - static byte[] normalModeDecrypt( - byte[] encryptedData, - IkeNormalModeCipher decryptCipher, - byte[] decryptionKey, - byte[] iv) - throws IllegalBlockSizeException { - byte[] paddedPlaintext = decryptCipher.decrypt(encryptedData, decryptionKey, iv); - - return stripPadding(paddedPlaintext); - } - - /** Package private for testing */ - @VisibleForTesting - static byte[] combinedModeEncrypt( - IkeCombinedModeCipher encryptCipher, - IkeHeader ikeHeader, - @IkePayload.PayloadType int firstPayloadType, - byte[] skfHeaderBytes, - byte[] dataToEncrypt, - byte[] encryptionKey, - byte[] iv, - byte[] padding) { - int dataToAuthenticateLength = - IkeHeader.IKE_HEADER_LENGTH - + IkePayload.GENERIC_HEADER_LENGTH - + skfHeaderBytes.length; - ByteBuffer authenticatedSectionBuffer = ByteBuffer.allocate(dataToAuthenticateLength); - - byte[] paddedData = getPaddedData(dataToEncrypt, padding); - int encryptedPayloadLength = - IkePayload.GENERIC_HEADER_LENGTH - + skfHeaderBytes.length - + iv.length - + paddedData.length - + encryptCipher.getChecksumLen(); - ikeHeader.encodeToByteBuffer(authenticatedSectionBuffer, encryptedPayloadLength); - IkePayload.encodePayloadHeaderToByteBuffer( - firstPayloadType, encryptedPayloadLength, authenticatedSectionBuffer); - authenticatedSectionBuffer.put(skfHeaderBytes); - - return encryptCipher.encrypt( - paddedData, authenticatedSectionBuffer.array(), encryptionKey, iv); - } - - /** Package private for testing */ - @VisibleForTesting - static byte[] combinedModeDecrypt( - IkeCombinedModeCipher decryptCipher, - byte[] encryptedData, - byte[] checksum, - byte[] dataToAuthenticate, - byte[] decryptionKey, - byte[] iv) - throws AEADBadTagException { - ByteBuffer dataWithChecksumBuffer = - ByteBuffer.allocate(encryptedData.length + checksum.length); - dataWithChecksumBuffer.put(encryptedData); - dataWithChecksumBuffer.put(checksum); - dataWithChecksumBuffer.rewind(); - - byte[] paddedPlaintext = - decryptCipher.decrypt( - dataWithChecksumBuffer.array(), dataToAuthenticate, decryptionKey, iv); - - return stripPadding(paddedPlaintext); - } - - /** Package private for testing */ - @VisibleForTesting - static byte[] calculatePadding(int dataToEncryptLength, int blockSize) { - // Sum of dataToEncryptLength, PAD_LEN_LEN and padLength should be aligned with block size. - int unpaddedLen = dataToEncryptLength + PAD_LEN_LEN; - int padLength = (unpaddedLen + blockSize - 1) / blockSize * blockSize - unpaddedLen; - byte[] padding = new byte[padLength]; - - // According to RFC 7296, "Padding MAY contain any value". - new SecureRandom().nextBytes(padding); - - return padding; - } - - private static byte[] getPaddedData(byte[] data, byte[] padding) { - int padLength = padding.length; - int paddedDataLength = data.length + padLength + PAD_LEN_LEN; - ByteBuffer padBuffer = ByteBuffer.allocate(paddedDataLength); - padBuffer.put(data).put(padding).put((byte) padLength); - - return padBuffer.array(); - } - - private static byte[] stripPadding(byte[] paddedPlaintext) { - // Remove padding. Pad length value is the last byte of the padded unencrypted data. - int padLength = Byte.toUnsignedInt(paddedPlaintext[paddedPlaintext.length - 1]); - int decryptedDataLen = paddedPlaintext.length - padLength - PAD_LEN_LEN; - - return Arrays.copyOfRange(paddedPlaintext, 0, decryptedDataLen); - } - - /** Package private */ - byte[] getUnencryptedData() { - return mUnencryptedData; - } - - /** Package private */ - int getLength() { - return (mIv.length + mEncryptedAndPaddedData.length + mIntegrityChecksum.length); - } - - /** Package private */ - byte[] encode() { - ByteBuffer buffer = ByteBuffer.allocate(getLength()); - buffer.put(mIv).put(mEncryptedAndPaddedData).put(mIntegrityChecksum); - return buffer.array(); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeInformationalPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeInformationalPayload.java deleted file mode 100644 index 606c38f8..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeInformationalPayload.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -/** - * IkeInformationalPayload abstracts all Payloads sent in INFORMATIONAL exchanges. - * - * <p>This class is a non-RFC payload for implementation simplicity. - */ -public abstract class IkeInformationalPayload extends IkePayload { - IkeInformationalPayload(int payloadType, boolean isCritical) { - super(payloadType, isCritical); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java deleted file mode 100644 index 27fb9651..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java +++ /dev/null @@ -1,981 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import static android.net.ipsec.ike.IkeManager.getIkeLog; - -import static com.android.internal.net.ipsec.ike.message.IkePayload.PayloadType; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.net.ipsec.ike.exceptions.IkeException; -import android.net.ipsec.ike.exceptions.IkeInternalException; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.util.Pair; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecord; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.exceptions.InvalidMessageIdException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; -import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.Provider; -import java.security.Security; -import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -/** - * IkeMessage represents an IKE message. - * - * <p>It contains all attributes and provides methods for encoding, decoding, encrypting and - * decrypting. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class IkeMessage { - private static final String TAG = "IkeMessage"; - - private static IIkeMessageHelper sIkeMessageHelper = new IkeMessageHelper(); - - // Currently use Bouncy Castle as crypto security provider - static final Provider SECURITY_PROVIDER = new BouncyCastleProvider(); - - // TODO: b/142070035 Use Conscrypt as default security provider instead of BC - - // Currently use HarmonyJSSE as TrustManager provider - static final Provider TRUST_MANAGER_PROVIDER = Security.getProvider("HarmonyJSSE"); - - // Payload types in this set may be included multiple times within an IKE message. All other - // payload types can be included at most once. - private static final Set<Integer> REPEATABLE_PAYLOAD_TYPES = new HashSet<>(); - - static { - REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_CERT); - REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_CERT_REQUEST); - REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_NOTIFY); - REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_DELETE); - REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_VENDOR); - } - - public final IkeHeader ikeHeader; - public final List<IkePayload> ikePayloadList; - /** - * Conctruct an instance of IkeMessage. It is called by decode or for building outbound message. - * - * @param header the header of this IKE message - * @param payloadList the list of decoded IKE payloads in this IKE message - */ - public IkeMessage(IkeHeader header, List<IkePayload> payloadList) { - ikeHeader = header; - ikePayloadList = payloadList; - } - - /** - * Get security provider for IKE library - * - * <p>Use BouncyCastleProvider as the default security provider. - * - * @return the security provider of IKE library. - */ - public static Provider getSecurityProvider() { - // TODO: Move this getter out of IKE message package since not only this package uses it. - return SECURITY_PROVIDER; - } - - /** - * Get security provider for X509TrustManager to do certificate validation. - * - * <p>Use JSSEProvdier as the default security provider. - * - * @return the provider for X509TrustManager - */ - public static Provider getTrustManagerProvider() { - return TRUST_MANAGER_PROVIDER; - } - - /** - * Decode unencrypted IKE message body and create an instance of IkeMessage. - * - * <p>This method catches all RuntimeException during decoding incoming IKE packet. - * - * @param expectedMsgId the expected message ID to validate against. - * @param header the IKE header that is decoded but not validated. - * @param inputPacket the byte array contains the whole IKE message. - * @return the decoding result. - */ - public static DecodeResult decode(int expectedMsgId, IkeHeader header, byte[] inputPacket) { - return sIkeMessageHelper.decode(expectedMsgId, header, inputPacket); - } - - /** - * Decrypt and decode encrypted IKE message body and create an instance of IkeMessage. - * - * @param expectedMsgId the expected message ID to validate against. - * @param integrityMac the negotiated integrity algorithm. - * @param decryptCipher the negotiated encryption algorithm. - * @param ikeSaRecord ikeSaRecord where this packet is sent on. - * @param ikeHeader header of IKE packet. - * @param packet IKE packet as a byte array. - * @param collectedFragments previously received IKE fragments. - * @return the decoding result. - */ - public static DecodeResult decode( - int expectedMsgId, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - IkeSaRecord ikeSaRecord, - IkeHeader ikeHeader, - byte[] packet, - DecodeResultPartial collectedFragments) { - return sIkeMessageHelper.decode( - expectedMsgId, - integrityMac, - decryptCipher, - ikeSaRecord, - ikeHeader, - packet, - collectedFragments); - } - - private static List<IkePayload> decodePayloadList( - @PayloadType int firstPayloadType, boolean isResp, byte[] unencryptedPayloads) - throws IkeProtocolException { - ByteBuffer inputBuffer = ByteBuffer.wrap(unencryptedPayloads); - int currentPayloadType = firstPayloadType; - // For supported payload - List<IkePayload> supportedPayloadList = new LinkedList<>(); - // For unsupported critical payload - List<Integer> unsupportedCriticalPayloadList = new LinkedList<>(); - - // For marking the existence of supported payloads in this message. - HashSet<Integer> supportedTypesFoundSet = new HashSet<>(); - - StringBuilder logPayloadsSb = new StringBuilder(); - logPayloadsSb.append("Decoded payloads [ "); - - while (currentPayloadType != IkePayload.PAYLOAD_TYPE_NO_NEXT) { - Pair<IkePayload, Integer> pair = - IkePayloadFactory.getIkePayload(currentPayloadType, isResp, inputBuffer); - IkePayload payload = pair.first; - logPayloadsSb.append(payload.getTypeString()).append(" "); - - if (!(payload instanceof IkeUnsupportedPayload)) { - int type = payload.payloadType; - if (!supportedTypesFoundSet.add(type) && !REPEATABLE_PAYLOAD_TYPES.contains(type)) { - throw new InvalidSyntaxException( - "It is not allowed to have multiple payloads with payload type: " - + type); - } - - supportedPayloadList.add(payload); - } else if (payload.isCritical) { - unsupportedCriticalPayloadList.add(payload.payloadType); - } - // Simply ignore unsupported uncritical payload. - - currentPayloadType = pair.second; - } - - logPayloadsSb.append("]"); - getIkeLog().d("IkeMessage", logPayloadsSb.toString()); - - if (inputBuffer.remaining() > 0) { - throw new InvalidSyntaxException( - "Malformed IKE Payload: Unexpected bytes at the end of packet."); - } - - if (unsupportedCriticalPayloadList.size() > 0) { - throw new UnsupportedCriticalPayloadException(unsupportedCriticalPayloadList); - } - - // TODO: Verify that for all status notification payloads, only - // NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP and NOTIFY_TYPE_IPCOMP_SUPPORTED can be included - // multiple times in a request message. There is not a clear number restriction for - // error notification payloads. - - return supportedPayloadList; - } - - /** - * Encode unencrypted IKE message. - * - * @return encoded IKE message in byte array. - */ - public byte[] encode() { - return sIkeMessageHelper.encode(this); - } - - /** - * Encrypt and encode packet. - * - * @param integrityMac the negotiated integrity algorithm. - * @param encryptCipher the negotiated encryption algortihm. - * @param ikeSaRecord the ikeSaRecord where this packet is sent on. - * @param supportFragment if IKE fragmentation is supported - * @param fragSize the maximum size of IKE fragment - * @return encoded IKE message in byte array. - */ - public byte[][] encryptAndEncode( - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - IkeSaRecord ikeSaRecord, - boolean supportFragment, - int fragSize) { - return sIkeMessageHelper.encryptAndEncode( - integrityMac, encryptCipher, ikeSaRecord, this, supportFragment, fragSize); - } - - /** - * Encode all payloads to a byte array. - * - * @return byte array contains all encoded payloads - */ - private byte[] encodePayloads() { - StringBuilder logPayloadsSb = new StringBuilder(); - logPayloadsSb.append("Generating payloads [ "); - - int payloadLengthSum = 0; - for (IkePayload payload : ikePayloadList) { - payloadLengthSum += payload.getPayloadLength(); - logPayloadsSb.append(payload.getTypeString()).append(" "); - } - logPayloadsSb.append("]"); - getIkeLog().d("IkeMessage", logPayloadsSb.toString()); - - if (ikePayloadList.isEmpty()) return new byte[0]; - - ByteBuffer byteBuffer = ByteBuffer.allocate(payloadLengthSum); - for (int i = 0; i < ikePayloadList.size() - 1; i++) { - ikePayloadList - .get(i) - .encodeToByteBuffer(ikePayloadList.get(i + 1).payloadType, byteBuffer); - } - ikePayloadList - .get(ikePayloadList.size() - 1) - .encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, byteBuffer); - - return byteBuffer.array(); - } - - /** Package */ - @VisibleForTesting - byte[] attachEncodedHeader(byte[] encodedIkeBody) { - ByteBuffer outputBuffer = - ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH + encodedIkeBody.length); - ikeHeader.encodeToByteBuffer(outputBuffer, encodedIkeBody.length); - outputBuffer.put(encodedIkeBody); - return outputBuffer.array(); - } - - /** - * Obtain all payloads with input payload type. - * - * <p>This method can be only applied to the payload types that can be included multiple times - * within an IKE message. - * - * @param payloadType the payloadType to look for. - * @param payloadClass the class of the desired payloads. - * @return a list of IkePayloads with the payloadType. - */ - public <T extends IkePayload> List<T> getPayloadListForType( - @IkePayload.PayloadType int payloadType, Class<T> payloadClass) { - // STOPSHIP: b/130190639 Notify user the error and close IKE session. - if (!REPEATABLE_PAYLOAD_TYPES.contains(payloadType)) { - throw new IllegalArgumentException( - "Received unexpected payloadType: " - + payloadType - + " that can be included at most once within an IKE message."); - } - - return IkePayload.getPayloadListForTypeInProvidedList( - payloadType, payloadClass, ikePayloadList); - } - - /** - * Obtain the payload with the input payload type. - * - * <p>This method can be only applied to the payload type that can be included at most once - * within an IKE message. - * - * @param payloadType the payloadType to look for. - * @param payloadClass the class of the desired payload. - * @return the IkePayload with the payloadType. - */ - public <T extends IkePayload> T getPayloadForType( - @IkePayload.PayloadType int payloadType, Class<T> payloadClass) { - // STOPSHIP: b/130190639 Notify user the error and close IKE session. - if (REPEATABLE_PAYLOAD_TYPES.contains(payloadType)) { - throw new IllegalArgumentException( - "Received unexpected payloadType: " - + payloadType - + " that may be included multiple times within an IKE message."); - } - - return IkePayload.getPayloadForTypeInProvidedList( - payloadType, payloadClass, ikePayloadList); - } - - /** - * Checks if this Request IkeMessage was a DPD message - * - * <p>An IKE message is a DPD request iff the message was encrypted (has a SK payload) and there - * were no payloads within the SK payload (or outside the SK payload). - */ - public boolean isDpdRequest() { - return !ikeHeader.isResponseMsg - && ikeHeader.exchangeType == IkeHeader.EXCHANGE_TYPE_INFORMATIONAL - && ikePayloadList.isEmpty() - && ikeHeader.nextPayloadType == IkePayload.PAYLOAD_TYPE_SK; - } - - /** - * IIkeMessageHelper provides interface for decoding, encoding and processing IKE packet. - * - * <p>IkeMessageHelper exists so that the interface is injectable for testing. - */ - @VisibleForTesting - public interface IIkeMessageHelper { - /** - * Encode IKE message. - * - * @param ikeMessage message need to be encoded. - * @return encoded IKE message in byte array. - */ - byte[] encode(IkeMessage ikeMessage); - - /** - * Encrypt and encode IKE message. - * - * @param integrityMac the negotiated integrity algorithm. - * @param encryptCipher the negotiated encryption algortihm. - * @param ikeSaRecord the ikeSaRecord where this packet is sent on. - * @param ikeMessage message need to be encoded. * @param supportFragment if IKE - * fragmentation is supported. - * @param fragSize the maximum size of IKE fragment. - * @return encoded IKE message in byte array. - */ - byte[][] encryptAndEncode( - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - IkeSaRecord ikeSaRecord, - IkeMessage ikeMessage, - boolean supportFragment, - int fragSize); - - // TODO: Return DecodeResult when decoding unencrypted message - /** - * Decode unencrypted packet. - * - * @param expectedMsgId the expected message ID to validate against. - * @param ikeHeader header of IKE packet. - * @param packet IKE packet as a byte array. - * @return the decoding result. - */ - DecodeResult decode(int expectedMsgId, IkeHeader ikeHeader, byte[] packet); - - /** - * Decrypt and decode packet. - * - * @param expectedMsgId the expected message ID to validate against. - * @param integrityMac the negotiated integrity algorithm. - * @param decryptCipher the negotiated encryption algorithm. - * @param ikeSaRecord ikeSaRecord where this packet is sent on. - * @param ikeHeader header of IKE packet. - * @param packet IKE packet as a byte array. - * @param collectedFragments previously received IKE fragments. - * @return the decoding result. - */ - DecodeResult decode( - int expectedMsgId, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - IkeSaRecord ikeSaRecord, - IkeHeader ikeHeader, - byte[] packet, - DecodeResultPartial collectedFragments); - } - - /** IkeMessageHelper provides methods for decoding, encoding and processing IKE packet. */ - public static final class IkeMessageHelper implements IIkeMessageHelper { - @Override - public byte[] encode(IkeMessage ikeMessage) { - getIkeLog().d("IkeMessage", "Generating " + ikeMessage.ikeHeader.getBasicInfoString()); - - byte[] encodedIkeBody = ikeMessage.encodePayloads(); - byte[] packet = ikeMessage.attachEncodedHeader(encodedIkeBody); - getIkeLog().d("IkeMessage", "Build a complete IKE message: " + getIkeLog().pii(packet)); - return packet; - } - - @Override - public byte[][] encryptAndEncode( - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - IkeSaRecord ikeSaRecord, - IkeMessage ikeMessage, - boolean supportFragment, - int fragSize) { - getIkeLog().d("IkeMessage", "Generating " + ikeMessage.ikeHeader.getBasicInfoString()); - - return encryptAndEncode( - ikeMessage.ikeHeader, - ikeMessage.ikePayloadList.isEmpty() - ? IkePayload.PAYLOAD_TYPE_NO_NEXT - : ikeMessage.ikePayloadList.get(0).payloadType, - ikeMessage.encodePayloads(), - integrityMac, - encryptCipher, - ikeSaRecord.getOutboundIntegrityKey(), - ikeSaRecord.getOutboundEncryptionKey(), - supportFragment, - fragSize); - } - - @VisibleForTesting - byte[][] encryptAndEncode( - IkeHeader ikeHeader, - @PayloadType int firstInnerPayload, - byte[] unencryptedPayloads, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - byte[] integrityKey, - byte[] encryptionKey, - boolean supportFragment, - int fragSize) { - - IkeSkPayload skPayload = - new IkeSkPayload( - ikeHeader, - firstInnerPayload, - unencryptedPayloads, - integrityMac, - encryptCipher, - integrityKey, - encryptionKey); - int msgLen = IkeHeader.IKE_HEADER_LENGTH + skPayload.getPayloadLength(); - - // Build complete IKE message - if (!supportFragment || msgLen <= fragSize) { - byte[][] packetList = new byte[1][]; - packetList[0] = encodeHeaderAndBody(ikeHeader, skPayload, firstInnerPayload); - - getIkeLog() - .d( - "IkeMessage", - "Build a complete IKE message: " + getIkeLog().pii(packetList[0])); - return packetList; - } - - // Build IKE fragments - int dataLenPerPacket = - fragSize - - IkeHeader.IKE_HEADER_LENGTH - - IkePayload.GENERIC_HEADER_LENGTH - - IkeSkfPayload.SKF_HEADER_LEN - - encryptCipher.getIvLen() - - integrityMac.getChecksumLen() - - encryptCipher.getBlockSize(); - - // Caller of this method MUST validate fragSize is valid. - if (dataLenPerPacket <= 0) { - throw new IllegalArgumentException( - "Max fragment size is too small for an IKE fragment."); - } - - int totalFragments = - (unencryptedPayloads.length + dataLenPerPacket - 1) / dataLenPerPacket; - IkeHeader skfHeader = ikeHeader.makeSkfHeaderFromSkHeader(); - byte[][] packetList = new byte[totalFragments][]; - - ByteBuffer unencryptedDataBuffer = ByteBuffer.wrap(unencryptedPayloads); - for (int i = 0; i < totalFragments; i++) { - byte[] unencryptedData = - new byte[Math.min(dataLenPerPacket, unencryptedDataBuffer.remaining())]; - unencryptedDataBuffer.get(unencryptedData); - - int fragNum = i + 1; // 1-based - - IkeSkfPayload skfPayload = - new IkeSkfPayload( - ikeHeader, - firstInnerPayload, - unencryptedData, - integrityMac, - encryptCipher, - integrityKey, - encryptionKey, - fragNum, - totalFragments); - - packetList[i] = - encodeHeaderAndBody( - skfHeader, - skfPayload, - i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT); - getIkeLog() - .d( - "IkeMessage", - "Build an IKE fragment (" - + (i + 1) - + "/" - + totalFragments - + "): " - + getIkeLog().pii(packetList[0])); - } - - return packetList; - } - - private byte[] encodeHeaderAndBody( - IkeHeader ikeHeader, IkeSkPayload skPayload, @PayloadType int firstInnerPayload) { - ByteBuffer outputBuffer = - ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH + skPayload.getPayloadLength()); - ikeHeader.encodeToByteBuffer(outputBuffer, skPayload.getPayloadLength()); - skPayload.encodeToByteBuffer(firstInnerPayload, outputBuffer); - return outputBuffer.array(); - } - - @Override - public DecodeResult decode(int expectedMsgId, IkeHeader header, byte[] inputPacket) { - try { - if (header.messageId != expectedMsgId) { - throw new InvalidMessageIdException(header.messageId); - } - - header.validateMajorVersion(); - header.validateInboundHeader(inputPacket.length); - - byte[] unencryptedPayloads = - Arrays.copyOfRange( - inputPacket, IkeHeader.IKE_HEADER_LENGTH, inputPacket.length); - List<IkePayload> supportedPayloadList = - decodePayloadList( - header.nextPayloadType, header.isResponseMsg, unencryptedPayloads); - return new DecodeResultOk( - new IkeMessage(header, supportedPayloadList), inputPacket); - } catch (NegativeArraySizeException | BufferUnderflowException e) { - // Invalid length error when parsing payload bodies. - return new DecodeResultUnprotectedError( - new InvalidSyntaxException("Malformed IKE Payload")); - } catch (IkeProtocolException e) { - return new DecodeResultUnprotectedError(e); - } - } - - @Override - public DecodeResult decode( - int expectedMsgId, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - IkeSaRecord ikeSaRecord, - IkeHeader ikeHeader, - byte[] packet, - DecodeResultPartial collectedFragments) { - return decode( - expectedMsgId, - ikeHeader, - packet, - integrityMac, - decryptCipher, - ikeSaRecord.getInboundIntegrityKey(), - ikeSaRecord.getInboundDecryptionKey(), - collectedFragments); - } - - private DecodeResult decode( - int expectedMsgId, - IkeHeader header, - byte[] inputPacket, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey, - DecodeResultPartial collectedFragments) { - if (header.nextPayloadType != IkePayload.PAYLOAD_TYPE_SK - && header.nextPayloadType != IkePayload.PAYLOAD_TYPE_SKF) { - // TODO: b/123372339 Handle message containing unprotected payloads. - throw new UnsupportedOperationException("Message contains unprotected payloads"); - } - - // Decrypt message and do authentication - Pair<IkeSkPayload, Integer> pair; - try { - pair = - decryptAndAuthenticate( - expectedMsgId, - header, - inputPacket, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - } catch (IkeException e) { - if (collectedFragments == null) { - return new DecodeResultUnprotectedError(e); - } else { - getIkeLog() - .i( - TAG, - "Message authentication or decryption failed on received" - + " message. Discard it ", - e); - return collectedFragments; - } - } - - // Handle IKE fragment - boolean isFragment = (header.nextPayloadType == IkePayload.PAYLOAD_TYPE_SKF); - boolean fragReassemblyStarted = (collectedFragments != null); - - if (isFragment) { - getIkeLog() - .d( - TAG, - "Received an IKE fragment (" - + ((IkeSkfPayload) pair.first).fragmentNum - + "/" - + ((IkeSkfPayload) pair.first).totalFragments - + ")"); - } - - // IKE fragment reassembly has started but a complete message was received. - if (!isFragment && fragReassemblyStarted) { - getIkeLog() - .w( - TAG, - "Received a complete IKE message while doing IKE fragment" - + " reassembly. Discard the newly received message."); - return collectedFragments; - } - - byte[] firstPacket = inputPacket; - byte[] decryptedBytes = pair.first.getUnencryptedData(); - int firstPayloadType = pair.second; - - // Received an IKE fragment - if (isFragment) { - validateFragmentHeader(header, inputPacket.length, collectedFragments); - - // Add the recently received fragment to the reassembly queue. - DecodeResultPartial DecodeResultPartial = - processIkeFragment( - header, - inputPacket, - (IkeSkfPayload) (pair.first), - pair.second, - collectedFragments); - - if (!DecodeResultPartial.isAllFragmentsReceived()) return DecodeResultPartial; - - firstPayloadType = DecodeResultPartial.firstPayloadType; - decryptedBytes = DecodeResultPartial.reassembleAllFrags(); - firstPacket = DecodeResultPartial.firstFragBytes; - } - - // Received or has reassembled a complete IKE message. Check if there is protocol error. - try { - // TODO: Log IKE header information and payload types - - List<IkePayload> supportedPayloadList = - decodePayloadList(firstPayloadType, header.isResponseMsg, decryptedBytes); - - header.validateInboundHeader(inputPacket.length); - return new DecodeResultOk( - new IkeMessage(header, supportedPayloadList), firstPacket); - } catch (NegativeArraySizeException | BufferUnderflowException e) { - // Invalid length error when parsing payload bodies. - return new DecodeResultProtectedError( - new InvalidSyntaxException("Malformed IKE Payload", e), firstPacket); - } catch (IkeProtocolException e) { - return new DecodeResultProtectedError(e, firstPacket); - } - } - - private Pair<IkeSkPayload, Integer> decryptAndAuthenticate( - int expectedMsgId, - IkeHeader header, - byte[] inputPacket, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeException { - - try { - if (header.messageId != expectedMsgId) { - throw new InvalidMessageIdException(header.messageId); - } - - header.validateMajorVersion(); - - boolean isSkf = header.nextPayloadType == IkePayload.PAYLOAD_TYPE_SKF; - return IkePayloadFactory.getIkeSkPayload( - isSkf, - inputPacket, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - } catch (NegativeArraySizeException | BufferUnderflowException e) { - throw new InvalidSyntaxException("Malformed IKE Payload", e); - } catch (GeneralSecurityException e) { - throw new IkeInternalException(e); - } - } - - private void validateFragmentHeader( - IkeHeader fragIkeHeader, int packetLen, DecodeResultPartial collectedFragments) { - try { - fragIkeHeader.validateInboundHeader(packetLen); - } catch (IkeProtocolException e) { - getIkeLog() - .e( - TAG, - "Received an IKE fragment with invalid header. Will be handled when" - + " reassembly is done.", - e); - } - - if (collectedFragments == null) return; - if (fragIkeHeader.exchangeType != collectedFragments.ikeHeader.exchangeType) { - getIkeLog() - .e( - TAG, - "Received an IKE fragment with different exchange type from" - + " previously collected fragments. Ignore it."); - } - } - - private DecodeResultPartial processIkeFragment( - IkeHeader header, - byte[] inputPacket, - IkeSkfPayload skf, - int nextPayloadType, - @Nullable DecodeResultPartial collectedFragments) { - if (collectedFragments == null) { - return new DecodeResultPartial( - header, inputPacket, skf, nextPayloadType, collectedFragments); - } - - if (skf.totalFragments > collectedFragments.collectedFragsList.length) { - getIkeLog() - .i( - TAG, - "Received IKE fragment has larger total fragments number. Discard" - + " all previously collected fragments"); - return new DecodeResultPartial( - header, inputPacket, skf, nextPayloadType, null /*collectedFragments*/); - } - - if (skf.totalFragments < collectedFragments.collectedFragsList.length) { - getIkeLog() - .i( - TAG, - "Received IKE fragment has smaller total fragments number. Discard" - + " it."); - return collectedFragments; - } - - if (collectedFragments.collectedFragsList[skf.fragmentNum - 1] != null) { - getIkeLog().i(TAG, "Received IKE fragment is a replay."); - return collectedFragments; - } - - return new DecodeResultPartial( - header, inputPacket, skf, nextPayloadType, collectedFragments); - } - } - - /** Status to describe the result of decoding an inbound IKE message. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - DECODE_STATUS_OK, - DECODE_STATUS_PARTIAL, - DECODE_STATUS_PROTECTED_ERROR, - DECODE_STATUS_UNPROTECTED_ERROR, - }) - public @interface DecodeStatus {} - - /** - * Represents a message that has been successfully (decrypted and) decoded or reassembled from - * IKE fragments - */ - public static final int DECODE_STATUS_OK = 0; - /** Represents that reassembly process of IKE fragments has started but has not finished */ - public static final int DECODE_STATUS_PARTIAL = 1; - /** Represents a crypto protected message with correct message ID but has parsing error. */ - public static final int DECODE_STATUS_PROTECTED_ERROR = 2; - /** - * Represents an unencrypted message with parsing error, an encrypted message with - * authentication or decryption error, or any message with wrong message ID. - */ - public static final int DECODE_STATUS_UNPROTECTED_ERROR = 3; - - /** This class represents common decoding result of an IKE message. */ - public abstract static class DecodeResult { - public final int status; - - /** Construct an instance of DecodeResult. */ - protected DecodeResult(int status) { - this.status = status; - } - } - - /** This class represents an IKE message has been successfully (decrypted and) decoded. */ - public static class DecodeResultOk extends DecodeResult { - public final IkeMessage ikeMessage; - public final byte[] firstPacket; - - public DecodeResultOk(IkeMessage ikeMessage, byte[] firstPacket) { - super(DECODE_STATUS_OK); - this.ikeMessage = ikeMessage; - this.firstPacket = firstPacket; - } - } - - /** - * This class represents IKE fragments are being reassembled to build a complete IKE message. - * - * <p>All IKE fragments should have the same IKE headers, except for the message length. This - * class only stores the IKE header of the first arrived IKE fragment to represent the IKE - * header of the complete IKE message. In this way we can verify all subsequent fragments' - * headers against it. - * - * <p>The first payload type is only stored in the first fragment, as indicated in RFC 7383. So - * this class only stores the next payload type field taken from the first fragment. - */ - public static class DecodeResultPartial extends DecodeResult { - public final int firstPayloadType; - public final byte[] firstFragBytes; - public final IkeHeader ikeHeader; - public final byte[][] collectedFragsList; - - /** - * Construct an instance of DecodeResultPartial with collected fragments and the newly - * received fragment. - * - * <p>The newly received fragment has been validated against collected fragments during - * decoding that all fragments have the same total fragments number and the newly received - * fragment is not a replay. - */ - public DecodeResultPartial( - IkeHeader ikeHeader, - byte[] inputPacket, - IkeSkfPayload skfPayload, - int nextPayloadType, - @Nullable DecodeResultPartial collectedFragments) { - super(DECODE_STATUS_PARTIAL); - - boolean isFirstFragment = 1 == skfPayload.fragmentNum; - if (collectedFragments == null) { - // First arrived IKE fragment - this.ikeHeader = ikeHeader; - this.firstPayloadType = - isFirstFragment ? nextPayloadType : IkePayload.PAYLOAD_TYPE_NO_NEXT; - this.firstFragBytes = isFirstFragment ? inputPacket : null; - this.collectedFragsList = new byte[skfPayload.totalFragments][]; - } else { - this.ikeHeader = collectedFragments.ikeHeader; - this.firstPayloadType = - isFirstFragment ? nextPayloadType : collectedFragments.firstPayloadType; - this.firstFragBytes = - isFirstFragment ? inputPacket : collectedFragments.firstFragBytes; - this.collectedFragsList = collectedFragments.collectedFragsList; - } - - this.collectedFragsList[skfPayload.fragmentNum - 1] = skfPayload.getUnencryptedData(); - } - - /** Return if all IKE fragments have been collected */ - public boolean isAllFragmentsReceived() { - for (byte[] frag : collectedFragsList) { - if (frag == null) return false; - } - return true; - } - - /** Reassemble all IKE fragments and return the unencrypted message body in byte array. */ - public byte[] reassembleAllFrags() { - if (!isAllFragmentsReceived()) { - throw new IllegalStateException("Not all fragments have been received"); - } - - int len = 0; - for (byte[] frag : collectedFragsList) { - len += frag.length; - } - - ByteBuffer buffer = ByteBuffer.allocate(len); - for (byte[] frag : collectedFragsList) { - buffer.put(frag); - } - - return buffer.array(); - } - } - - /** - * This class represents common information of error cases in decrypting and decoding message. - */ - public abstract static class DecodeResultError extends DecodeResult { - public final IkeException ikeException; - - protected DecodeResultError(int status, IkeException ikeException) { - super(status); - this.ikeException = ikeException; - } - } - /** - * This class represents that decoding errors have been found after the IKE message is - * authenticated and decrypted. - */ - public static class DecodeResultProtectedError extends DecodeResultError { - public final byte[] firstPacket; - - public DecodeResultProtectedError(IkeException ikeException, byte[] firstPacket) { - super(DECODE_STATUS_PROTECTED_ERROR, ikeException); - this.firstPacket = firstPacket; - } - } - /** This class represents errors have been found during message authentication or decryption. */ - public static class DecodeResultUnprotectedError extends DecodeResultError { - public DecodeResultUnprotectedError(IkeException ikeException) { - super(DECODE_STATUS_UNPROTECTED_ERROR, ikeException); - } - } - - /** - * For setting mocked IIkeMessageHelper for testing - * - * @param helper the mocked IIkeMessageHelper - */ - public static void setIkeMessageHelper(IIkeMessageHelper helper) { - sIkeMessageHelper = helper; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java deleted file mode 100644 index ce9529ee..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_IKE_SPI; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MAJOR_VERSION; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MESSAGE_ID; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SELECTORS; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; - -import android.annotation.IntDef; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.util.ArraySet; -import android.util.SparseArray; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidMajorVersionException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidMessageIdException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.exceptions.TemporaryFailureException; -import com.android.internal.net.ipsec.ike.exceptions.TsUnacceptableException; -import com.android.internal.net.ipsec.ike.exceptions.UnrecognizedIkeProtocolException; -import com.android.internal.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.ProviderException; -import java.util.Set; - -/** - * IkeNotifyPayload represents a Notify Payload. - * - * <p>As instructed by RFC 7296, for IKE SA concerned Notify Payload, Protocol ID and SPI Size must - * be zero. Unrecognized notify message type must be ignored but should be logged. - * - * <p>Notification types that smaller or equal than ERROR_NOTIFY_TYPE_MAX are error types. The rest - * of them are status types. - * - * <p>Critical bit for this payload must be ignored in received packet and must not be set in - * outbound packet. - * - * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol - * Version 2 (IKEv2)</a> - */ -public final class IkeNotifyPayload extends IkeInformationalPayload { - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, - NOTIFY_TYPE_IPCOMP_SUPPORTED, - NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, - NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, - NOTIFY_TYPE_USE_TRANSPORT_MODE, - NOTIFY_TYPE_REKEY_SA, - NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED - }) - public @interface NotifyType {} - - /** - * Indicates that the responder has narrowed the proposed Traffic Selectors but other Traffic - * Selectors would also have been acceptable. Only allowed in the response for negotiating a - * Child SA. - */ - public static final int NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE = 16386; - /** - * Indicates a willingness by its sender to use IPComp on this Child SA. Only allowed in the - * request/response for negotiating a Child SA. - */ - public static final int NOTIFY_TYPE_IPCOMP_SUPPORTED = 16387; - /** - * Used for detecting if the IKE initiator is behind a NAT. Only allowed in the request/response - * of IKE_SA_INIT exchange. - */ - public static final int NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP = 16388; - /** - * Used for detecting if the IKE responder is behind a NAT. Only allowed in the request/response - * of IKE_SA_INIT exchange. - */ - public static final int NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP = 16389; - /** - * Indicates a willingness by its sender to use transport mode rather than tunnel mode on this - * Child SA. Only allowed in the request/response for negotiating a Child SA. - */ - public static final int NOTIFY_TYPE_USE_TRANSPORT_MODE = 16391; - /** - * Used for rekeying a Child SA or an IKE SA. Only allowed in the request/response of - * CREATE_CHILD_SA exchange. - */ - public static final int NOTIFY_TYPE_REKEY_SA = 16393; - /** - * Indicates that the sender will not accept packets that contain TFC padding over the Child SA - * being negotiated. Only allowed in the request/response for negotiating a Child SA. - */ - public static final int NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED = 16394; - /** Indicates that the sender supports IKE fragmentation. */ - public static final int NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED = 16430; - - private static final int NOTIFY_HEADER_LEN = 4; - private static final int ERROR_NOTIFY_TYPE_MAX = 16383; - - private static final String NAT_DETECTION_DIGEST_ALGORITHM = "SHA-1"; - - private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA; - private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA; - - private static final SparseArray<String> NOTIFY_TYPE_TO_STRING; - - static { - VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA = new ArraySet<>(); - VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_INVALID_SELECTORS); - VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_CHILD_SA_NOT_FOUND); - VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(NOTIFY_TYPE_REKEY_SA); - } - - static { - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA = new ArraySet<>(); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add( - IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add( - IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE); - - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_IPCOMP_SUPPORTED); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_USE_TRANSPORT_MODE); - VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED); - } - - static { - NOTIFY_TYPE_TO_STRING = new SparseArray<>(); - NOTIFY_TYPE_TO_STRING.put( - ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_IKE_SPI, "Invalid IKE SPI"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MAJOR_VERSION, "Invalid major version"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MESSAGE_ID, "Invalid message ID"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_PROPOSAL_CHOSEN, "No proposal chosen"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_KE_PAYLOAD, "Invalid KE payload"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_SINGLE_PAIR_REQUIRED, "Single pair required"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_ADDITIONAL_SAS, "No additional SAs"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, "Internal address failure"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_FAILED_CP_REQUIRED, "Failed CP required"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TEMPORARY_FAILURE, "Temporary failure"); - NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_CHILD_SA_NOT_FOUND, "Child SA not found"); - - NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible"); - NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_IPCOMP_SUPPORTED, "IPCOMP supported"); - NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, "NAT detection source IP"); - NOTIFY_TYPE_TO_STRING.put( - NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP"); - NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode"); - NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA"); - NOTIFY_TYPE_TO_STRING.put( - NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TCP Padding not supported"); - NOTIFY_TYPE_TO_STRING.put( - NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported"); - } - - public final int protocolId; - public final byte spiSize; - public final int notifyType; - public final int spi; - public final byte[] notifyData; - - /** - * Construct an instance of IkeNotifyPayload in the context of IkePayloadFactory - * - * @param critical indicates if this payload is critical. Ignored in supported payload as - * instructed by the RFC 7296. - * @param payloadBody payload body in byte array - * @throws IkeProtocolException if there is any error - */ - IkeNotifyPayload(boolean isCritical, byte[] payloadBody) throws IkeProtocolException { - super(PAYLOAD_TYPE_NOTIFY, isCritical); - - ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); - - protocolId = Byte.toUnsignedInt(inputBuffer.get()); - spiSize = inputBuffer.get(); - notifyType = Short.toUnsignedInt(inputBuffer.getShort()); - - // Validate syntax of spiSize, protocolId and notifyType. - // Reference: <https://tools.ietf.org/html/rfc7296#page-100> - if (spiSize == SPI_LEN_IPSEC) { - // For message concerning existing Child SA - validateNotifyPayloadForExistingChildSa(); - spi = inputBuffer.getInt(); - - } else if (spiSize == SPI_LEN_NOT_INCLUDED) { - // For message concerning IKE SA or for new Child SA that to be negotiated. - validateNotifyPayloadForIkeAndNewChild(); - spi = SPI_NOT_INCLUDED; - - } else { - throw new InvalidSyntaxException("Invalid SPI Size: " + spiSize); - } - - notifyData = new byte[payloadBody.length - NOTIFY_HEADER_LEN - spiSize]; - inputBuffer.get(notifyData); - } - - private void validateNotifyPayloadForExistingChildSa() throws InvalidSyntaxException { - if (protocolId != PROTOCOL_ID_AH && protocolId != PROTOCOL_ID_ESP) { - throw new InvalidSyntaxException( - "Expected Procotol ID AH(2) or ESP(3): Protocol ID is " + protocolId); - } - - if (!VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.contains(notifyType)) { - throw new InvalidSyntaxException( - "Expected Notify Type for existing Child SA: Notify Type is " + notifyType); - } - } - - private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException { - if (protocolId != PROTOCOL_ID_UNSET) { - throw new InvalidSyntaxException( - "Expected Procotol ID unset: Protocol ID is " + protocolId); - } - - if (notifyType == ERROR_TYPE_INVALID_SELECTORS - || notifyType == ERROR_TYPE_CHILD_SA_NOT_FOUND) { - throw new InvalidSyntaxException( - "Expected Notify Type concerning IKE SA or new Child SA under negotiation" - + ": Notify Type is " - + notifyType); - } - } - - /** - * Generate NAT DETECTION notification data. - * - * <p>This method calculates NAT DETECTION notification data which is a SHA-1 digest of the IKE - * initiator's SPI, IKE responder's SPI, IP address and port. Source address and port should be - * used for generating NAT_DETECTION_SOURCE_IP data. Destination address and port should be used - * for generating NAT_DETECTION_DESTINATION_IP data. Here "source" and "destination" mean the - * direction of this IKE message. - * - * @param initiatorIkeSpi the SPI of IKE initiator - * @param responderIkeSpi the SPI of IKE responder - * @param ipAddress the IP address - * @param port the port - * @return the generated NAT DETECTION notification data as a byte array. - */ - public static byte[] generateNatDetectionData( - long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port) { - byte[] rawIpAddr = ipAddress.getAddress(); - - ByteBuffer byteBuffer = - ByteBuffer.allocate(2 * SPI_LEN_IKE + rawIpAddr.length + IP_PORT_LEN); - byteBuffer - .putLong(initiatorIkeSpi) - .putLong(responderIkeSpi) - .put(rawIpAddr) - .putShort((short) port); - - try { - MessageDigest natDetectionDataDigest = - MessageDigest.getInstance( - NAT_DETECTION_DIGEST_ALGORITHM, IkeMessage.getSecurityProvider()); - return natDetectionDataDigest.digest(byteBuffer.array()); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain algorithm :" + NAT_DETECTION_DIGEST_ALGORITHM, e); - } - } - - /** - * Encode Notify payload to ByteBuffer. - * - * @param nextPayload type of payload that follows this payload. - * @param byteBuffer destination ByteBuffer that stores encoded payload. - */ - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) notifyType); - if (spiSize == SPI_LEN_IPSEC) { - byteBuffer.putInt(spi); - } - byteBuffer.put(notifyData); - } - - /** - * Get entire payload length. - * - * @return entire payload length. - */ - @Override - protected int getPayloadLength() { - return GENERIC_HEADER_LENGTH + NOTIFY_HEADER_LEN + spiSize + notifyData.length; - } - - protected IkeNotifyPayload( - @ProtocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData) { - super(PAYLOAD_TYPE_NOTIFY, false); - this.protocolId = protocolId; - this.spiSize = spiSize; - this.spi = spi; - this.notifyType = notifyType; - this.notifyData = notifyData; - } - - /** - * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be - * negotiated with associated notification data. - * - * @param notifyType the notify type concerning IKE SA - * @param notifytData status or error data transmitted. Values for this field are notify type - * specific. - */ - public IkeNotifyPayload(int notifyType, byte[] notifyData) { - this(PROTOCOL_ID_UNSET, SPI_LEN_NOT_INCLUDED, SPI_NOT_INCLUDED, notifyType, notifyData); - try { - validateNotifyPayloadForIkeAndNewChild(); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException(e); - } - } - - /** - * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be - * negotiated without additional notification data. - * - * @param notifyType the notify type concerning IKE SA - */ - public IkeNotifyPayload(int notifyType) { - this(notifyType, new byte[0]); - } - - /** - * Construct IkeNotifyPayload concerning existing Child SA - * - * @param notifyType the notify type concerning Child SA - * @param notifytData status or error data transmitted. Values for this field are notify type - * specific. - */ - public IkeNotifyPayload( - @ProtocolId int protocolId, int spi, int notifyType, byte[] notifyData) { - this(protocolId, SPI_LEN_IPSEC, spi, notifyType, notifyData); - try { - validateNotifyPayloadForExistingChildSa(); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException(e); - } - } - - /** - * Indicates if this is an error notification payload. - * - * @return if this is an error notification payload. - */ - public boolean isErrorNotify() { - return notifyType <= ERROR_NOTIFY_TYPE_MAX; - } - - /** - * Indicates if this is an notification for a new Child SA negotiation. - * - * <p>This notification may provide additional configuration information for negotiating a new - * Child SA or is an error notification of the Child SA negotiation failure. - * - * @return if this is an notification for a new Child SA negotiation. - */ - public boolean isNewChildSaNotify() { - return VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.contains(notifyType); - } - - /** - * Validate error data and build IkeProtocolException for this error notification. - * - * @return the IkeProtocolException that represents this error. - * @throws InvalidSyntaxException if error data has invalid size. - */ - public IkeProtocolException validateAndBuildIkeException() throws InvalidSyntaxException { - if (!isErrorNotify()) { - throw new IllegalArgumentException( - "Do not support building IkeException for a non-error notificaton. Notify" - + " type: " - + notifyType); - } - - try { - switch (notifyType) { - case ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD: - return new UnsupportedCriticalPayloadException(notifyData); - case ERROR_TYPE_INVALID_MAJOR_VERSION: - return new InvalidMajorVersionException(notifyData); - case ERROR_TYPE_INVALID_SYNTAX: - return new InvalidSyntaxException(notifyData); - case ERROR_TYPE_INVALID_MESSAGE_ID: - return new InvalidMessageIdException(notifyData); - case ERROR_TYPE_NO_PROPOSAL_CHOSEN: - return new NoValidProposalChosenException(notifyData); - case ERROR_TYPE_INVALID_KE_PAYLOAD: - return new InvalidKeException(notifyData); - case ERROR_TYPE_AUTHENTICATION_FAILED: - return new AuthenticationFailedException(notifyData); - case ERROR_TYPE_TS_UNACCEPTABLE: - return new TsUnacceptableException(notifyData); - case ERROR_TYPE_TEMPORARY_FAILURE: - return new TemporaryFailureException(notifyData); - default: - return new UnrecognizedIkeProtocolException(notifyType, notifyData); - } - } catch (IllegalArgumentException e) { - // Notification data length is invalid. - throw new InvalidSyntaxException(e); - } - } - - /** - * Return the payload type as a String. - * - * @return the payload type as a String. - */ - @Override - public String getTypeString() { - String notifyTypeString = NOTIFY_TYPE_TO_STRING.get(notifyType); - - if (notifyTypeString == null) { - return "Notify(" + notifyType + ")"; - } - return "Notify(" + notifyTypeString + ")"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeSkPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeSkPayload.java deleted file mode 100644 index 0c2d1fff..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeSkPayload.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import android.annotation.Nullable; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; - -/** - * IkeSkPayload represents the common information of an Encrypted and Authenticated Payload and an - * Encrypted and Authenticated Fragment Payload. - * - * <p>It contains other payloads in encrypted form. It is must be the last payload in the message. - * It should be the only payload in this implementation. - * - * <p>Critical bit must be ignored when doing decoding. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#page-105">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public class IkeSkPayload extends IkePayload { - - protected final IkeEncryptedPayloadBody mIkeEncryptedPayloadBody; - - /** - * Construct an instance of IkeSkPayload from decrypting an incoming packet. - * - * @param critical indicates if it is a critical payload. - * @param message the byte array contains the whole IKE message. - * @param integrityMac the negotiated integrity algorithm. - * @param decryptCipher the negotiated encryption algorithm. - * @param integrityKey the negotiated integrity algorithm key. - * @param decryptionKey the negotiated decryption key. - */ - @VisibleForTesting - IkeSkPayload( - boolean critical, - byte[] message, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException { - - this( - false /*isSkf*/, - critical, - IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH, - message, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - } - - /** Construct an instance of IkeSkPayload for testing.*/ - @VisibleForTesting - IkeSkPayload(boolean isSkf, IkeEncryptedPayloadBody encryptedPayloadBody) { - super(isSkf ? PAYLOAD_TYPE_SKF : PAYLOAD_TYPE_SK, false/*critical*/); - mIkeEncryptedPayloadBody = encryptedPayloadBody; - } - - /** Construct an instance of IkeSkPayload from decrypting an incoming packet. */ - protected IkeSkPayload( - boolean isSkf, - boolean critical, - int encryptedBodyOffset, - byte[] message, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException { - super(isSkf ? PAYLOAD_TYPE_SKF : PAYLOAD_TYPE_SK, critical); - - // TODO: Support constructing IkeEncryptedPayloadBody using AEAD. - - mIkeEncryptedPayloadBody = - new IkeEncryptedPayloadBody( - message, - encryptedBodyOffset, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - } - - /** - * Construct an instance of IkeSkPayload for building outbound packet. - * - * @param ikeHeader the IKE header. - * @param firstPayloadType the type of first payload nested in SkPayload. - * @param unencryptedPayloads the encoded payload list to protect. - * @param integrityMac the negotiated integrity algorithm. - * @param encryptCipher the negotiated encryption algorithm. - * @param integrityKey the negotiated integrity algorithm key. - * @param encryptionKey the negotiated encryption key. - */ - @VisibleForTesting - IkeSkPayload( - IkeHeader ikeHeader, - @PayloadType int firstPayloadType, - byte[] unencryptedPayloads, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - byte[] integrityKey, - byte[] encryptionKey) { - - this( - ikeHeader, - firstPayloadType, - new byte[0] /*skfHeaderBytes*/, - unencryptedPayloads, - integrityMac, - encryptCipher, - integrityKey, - encryptionKey); - } - - /** Construct an instance of IkeSkPayload for building outbound packet. */ - @VisibleForTesting - protected IkeSkPayload( - IkeHeader ikeHeader, - @PayloadType int firstPayloadType, - byte[] skfHeaderBytes, - byte[] unencryptedPayloads, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - byte[] integrityKey, - byte[] encryptionKey) { - super(skfHeaderBytes.length == 0 ? PAYLOAD_TYPE_SK : PAYLOAD_TYPE_SKF, false); - - // TODO: Support constructing IkeEncryptedPayloadBody using AEAD. - - mIkeEncryptedPayloadBody = - new IkeEncryptedPayloadBody( - ikeHeader, - firstPayloadType, - skfHeaderBytes, - unencryptedPayloads, - integrityMac, - encryptCipher, - integrityKey, - encryptionKey); - } - - /** - * Return unencrypted data. - * - * @return unencrypted data in a byte array. - */ - public byte[] getUnencryptedData() { - return mIkeEncryptedPayloadBody.getUnencryptedData(); - } - - /** - * Encode this payload to a ByteBuffer. - * - * @param nextPayload type of payload that follows this payload. - * @param byteBuffer destination ByteBuffer that stores encoded payload. - */ - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer.put(mIkeEncryptedPayloadBody.encode()); - } - - /** - * Get entire payload length. - * - * @return entire payload length. - */ - @Override - protected int getPayloadLength() { - return GENERIC_HEADER_LENGTH + mIkeEncryptedPayloadBody.getLength(); - } - - /** - * Return the payload type as a String. - * - * @return the payload type as a String. - */ - @Override - public String getTypeString() { - return "SK"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeSkfPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeSkfPayload.java deleted file mode 100644 index 6faea123..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeSkfPayload.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import android.annotation.Nullable; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; - -/** - * IkeSkfPayload represents an Encrypted and Authenticated Fragment Payload. - * - * @see <a href="https://tools.ietf.org/html/rfc7383">RFC 7383, Internet Key Exchange Protocol - * Version 2 (IKEv2) Message Fragmentation</a> - */ -public final class IkeSkfPayload extends IkeSkPayload { - public static final int SKF_HEADER_LEN = 4; - - /** Current Fragment message number, starting from 1 */ - public final int fragmentNum; - /** Number of Fragment messages into which the original message was divided */ - public final int totalFragments; - - /** - * Construct an instance of IkeSkfPayload by authenticating and decrypting an incoming packet. - * - * <p>SKF Payload with invalid fragmentNum or invalid totalFragments, or cannot be authenticated - * or decrypted MUST be discarded - * - * @param critical indicates if it is a critical payload. - * @param message the byte array contains the whole IKE message. - * @param integrityMac the negotiated integrity algorithm. - * @param decryptCipher the negotiated encryption algorithm. - * @param integrityKey the negotiated integrity algorithm key. - * @param decryptionKey the negotiated decryption key. - */ - IkeSkfPayload( - boolean critical, - byte[] message, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher decryptCipher, - byte[] integrityKey, - byte[] decryptionKey) - throws IkeProtocolException, GeneralSecurityException { - super( - true /*isSkf*/, - critical, - IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH + SKF_HEADER_LEN, - message, - integrityMac, - decryptCipher, - integrityKey, - decryptionKey); - - // TODO: Support constructing IkeEncryptedPayloadBody using AEAD. - - ByteBuffer inputBuffer = ByteBuffer.wrap(message); - inputBuffer.get(new byte[IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH]); - - fragmentNum = Short.toUnsignedInt(inputBuffer.getShort()); - totalFragments = Short.toUnsignedInt(inputBuffer.getShort()); - - if (fragmentNum < 1 || totalFragments < 1 || fragmentNum > totalFragments) { - throw new InvalidSyntaxException( - "Received invalid Fragment Number or Total Fragments Number. Fragment Number: " - + fragmentNum - + " Total Fragments: " - + totalFragments); - } - } - - /** - * Construct an instance of IkeSkfPayload for building outbound packet. - * - * @param ikeHeader the IKE header. - * @param firstPayloadType the type of first payload nested in SkPayload. - * @param unencryptedPayloads the encoded payload list to protect. - * @param integrityMac the negotiated integrity algorithm. - * @param encryptCipher the negotiated encryption algorithm. - * @param integrityKey the negotiated integrity algorithm key. - * @param encryptionKey the negotiated encryption key. - */ - IkeSkfPayload( - IkeHeader ikeHeader, - @PayloadType int firstPayloadType, - byte[] unencryptedPayloads, - @Nullable IkeMacIntegrity integrityMac, - IkeCipher encryptCipher, - byte[] integrityKey, - byte[] encryptionKey, - int fragNum, - int totalFrags) { - super( - ikeHeader, - firstPayloadType, - encodeSkfHeader(fragNum, totalFrags), - unencryptedPayloads, - integrityMac, - encryptCipher, - integrityKey, - encryptionKey); - fragmentNum = fragNum; - totalFragments = totalFrags; - } - - /** Construct an instance of IkeSkfPayload for testing. */ - @VisibleForTesting - IkeSkfPayload(IkeEncryptedPayloadBody encryptedPayloadBody, int fragNum, int totalFrags) { - super(true /*isSkf*/, encryptedPayloadBody); - fragmentNum = fragNum; - totalFragments = totalFrags; - } - - @VisibleForTesting - static byte[] encodeSkfHeader(int fragNum, int totalFrags) { - ByteBuffer buffer = ByteBuffer.allocate(SKF_HEADER_LEN); - buffer.putShort((short) fragNum).putShort((short) totalFrags); - return buffer.array(); - } - - /** - * Encode this payload to a ByteBuffer. - * - * @param nextPayload type of payload that follows this payload. - * @param byteBuffer destination ByteBuffer that stores encoded payload. - */ - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - byteBuffer - .putShort((short) fragmentNum) - .putShort((short) totalFragments) - .put(mIkeEncryptedPayloadBody.encode()); - } - - /** - * Get entire payload length. - * - * @return entire payload length. - */ - @Override - protected int getPayloadLength() { - return GENERIC_HEADER_LENGTH + SKF_HEADER_LEN + mIkeEncryptedPayloadBody.getLength(); - } - - /** - * Return the payload type as a String. - * - * @return the payload type as a String. - */ - @Override - public String getTypeString() { - return "SKF"; - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeTsPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeTsPayload.java deleted file mode 100644 index 207bdc36..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeTsPayload.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import android.net.ipsec.ike.IkeTrafficSelector; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; - -import java.nio.ByteBuffer; - -/** - * IkeTsPayload represents an Traffic Selector Initiator Payload or an Traffic Selector Responder - * Payload. - * - * <p>Traffic Selector Initiator Payload and Traffic Selector Responder Payload have same format but - * different payload types. They describe the address ranges and port ranges of Child SA initiator - * and Child SA responder. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.13">RFC 7296, Internet Key Exchange - * Protocol Version 2 (IKEv2)</a> - */ -public final class IkeTsPayload extends IkePayload { - // Length of Traffic Selector Payload header. - private static final int TS_HEADER_LEN = 4; - // Length of reserved field in octets. - private static final int TS_HEADER_RESERVED_LEN = 3; - - /** Number of Traffic Selectors */ - public final int numTs; - /** Array of Traffic Selectors */ - public final IkeTrafficSelector[] trafficSelectors; - - IkeTsPayload(boolean critical, byte[] payloadBody, boolean isInitiator) - throws IkeProtocolException { - super((isInitiator ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER), critical); - - ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); - numTs = Byte.toUnsignedInt(inputBuffer.get()); - if (numTs == 0) { - throw new InvalidSyntaxException("Cannot find Traffic Selector in TS payload."); - } - - // Skip RESERVED byte - inputBuffer.get(new byte[TS_HEADER_RESERVED_LEN]); - - // Decode Traffic Selectors - byte[] tsBytes = new byte[inputBuffer.remaining()]; - inputBuffer.get(tsBytes); - trafficSelectors = IkeTrafficSelector.decodeIkeTrafficSelectors(numTs, tsBytes); - } - - /** - * Construct an instance of IkeTsPayload for building an outbound IKE message. - * - * @param isInitiator indicates if this payload is for a Child SA initiator or responder. - * @param ikeTrafficSelectors the array of included traffic selectors. - */ - public IkeTsPayload(boolean isInitiator, IkeTrafficSelector[] ikeTrafficSelectors) { - super((isInitiator ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER), false); - - if (ikeTrafficSelectors == null || ikeTrafficSelectors.length == 0) { - throw new IllegalArgumentException( - "TS Payload requires at least one Traffic Selector."); - } - - numTs = ikeTrafficSelectors.length; - trafficSelectors = ikeTrafficSelectors; - } - - /** - * Check if this TS payload contains the all TS in the provided TS payload. - * - * <p>A TS response cannot be narrower than a TS request. When doing rekey, the newly negotiated - * TS cannot be narrower than old negotiated TS. - * - * <p>This method will be used to (1) validate that an inbound response is subset of a locally - * generated request; and (2) validate that an inbound rekey request/response is superset of - * current negotiated TS. - * - * @param tsPayload the other TS payload to validate - * @return true if current TS Payload contains all TS in the input tsPayload - */ - public boolean contains(IkeTsPayload tsPayload) { - subTsLoop: - for (IkeTrafficSelector subTs : tsPayload.trafficSelectors) { - for (IkeTrafficSelector superTs : this.trafficSelectors) { - if (superTs.contains(subTs)) { - continue subTsLoop; - } - } - return false; - } - return true; - } - - /** - * Encode Traffic Selector Payload to ByteBuffer. - * - * @param nextPayload type of payload that follows this payload. - * @param byteBuffer destination ByteBuffer that stores encoded payload. - */ - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); - - byteBuffer.put((byte) numTs).put(new byte[TS_HEADER_RESERVED_LEN]); - for (IkeTrafficSelector ts : trafficSelectors) { - ts.encodeToByteBuffer(byteBuffer); - } - } - - /** - * Get entire payload length. - * - * @return entire payload length. - */ - @Override - protected int getPayloadLength() { - int len = GENERIC_HEADER_LENGTH + TS_HEADER_LEN; - for (IkeTrafficSelector ts : trafficSelectors) { - len += ts.selectorLength; - } - - return len; - } - - /** - * Return the payload type as a String. - * - * @return the payload type as a String. - */ - @Override - public String getTypeString() { - switch (payloadType) { - case PAYLOAD_TYPE_TS_INITIATOR: - return "TSi"; - case PAYLOAD_TYPE_TS_RESPONDER: - return "TSr"; - default: - // Won't reach here. - throw new IllegalArgumentException( - "Invalid Payload Type for Traffic Selector Payload."); - } - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/utils/FdEventsReader.java b/src/java/com/android/internal/net/ipsec/ike/utils/FdEventsReader.java deleted file mode 100644 index 65f9cedc..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/utils/FdEventsReader.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.utils; - -import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; -import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.util.SocketUtils; -import android.os.Handler; -import android.os.Looper; -import android.os.MessageQueue; -import android.system.ErrnoException; -import android.system.OsConstants; - -import java.io.FileDescriptor; -import java.io.IOException; - -/** - * This class encapsulates the mechanics of registering a file descriptor - * with a thread's Looper and handling read events (and errors). - * - * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override - * onStop() and onStart(). - * - * Subclasses can expect a call life-cycle like the following: - * - * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all - * goes well. Implementations may override onStart() for additional initialization. - * - * [2] yield, waiting for read event or error notification: - * - * [a] readPacket() && handlePacket() - * - * [b] if (no error): - * goto 2 - * else: - * goto 3 - * - * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never - * started). Implementations may override onStop() for additional cleanup. - * - * The packet receive buffer is recycled on every read call, so subclasses - * should make any copies they would like inside their handlePacket() - * implementation. - * - * All public methods MUST only be called from the same thread with which - * the Handler constructor argument is associated. - * - * <p> This code is an exact copy of {@link FdEventsReader} in - * frameworks/base/packages/NetworkStack/src/android/net/util/FdEventsReader.java, except the class - * name is changed to avoid confusion. - * - * FIXME: b/130058477 Find a way to share the code between network stack and code outside. - * - * @param <BufferType> the type of the buffer used to read data. - * @hide - */ -public abstract class FdEventsReader<BufferType> { - private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; - private static final int UNREGISTER_THIS_FD = 0; - - @NonNull - private final Handler mHandler; - @NonNull - private final MessageQueue mQueue; - @NonNull - private final BufferType mBuffer; - @Nullable - private FileDescriptor mFd; - private long mPacketsReceived; - - protected static void closeFd(FileDescriptor fd) { - try { - SocketUtils.closeSocket(fd); - } catch (IOException ignored) { - } - } - - protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) { - mHandler = h; - mQueue = mHandler.getLooper().getQueue(); - mBuffer = buffer; - } - - /** Start this FdEventsReader. */ - public void start() { - if (onCorrectThread()) { - createAndRegisterFd(); - } else { - mHandler.post(() -> { - logError("start() called from off-thread", null); - createAndRegisterFd(); - }); - } - } - - /** Stop this FdEventsReader and destroy the file descriptor. */ - public void stop() { - if (onCorrectThread()) { - unregisterAndDestroyFd(); - } else { - mHandler.post(() -> { - logError("stop() called from off-thread", null); - unregisterAndDestroyFd(); - }); - } - } - - @NonNull - public Handler getHandler() { - return mHandler; - } - - protected abstract int recvBufSize(@NonNull BufferType buffer); - - /** Returns the size of the receive buffer. */ - public int recvBufSize() { - return recvBufSize(mBuffer); - } - - /** - * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}. - * - * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0. - */ - public final long numPacketsReceived() { - return mPacketsReceived; - } - - /** - * Subclasses MUST create the listening socket here, including setting all desired socket - * options, interface or address/port binding, etc. The socket MUST be created nonblocking. - */ - @Nullable - protected abstract FileDescriptor createFd(); - - /** - * Implementations MUST return the bytes read or throw an Exception. - * - * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or - * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer - * contents and respectively wait for further input or retry the read immediately. For all other - * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this - * method. - */ - protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer) - throws Exception; - - /** - * Called by the main loop for every packet. Any desired copies of - * |recvbuf| should be made in here, as the underlying byte array is - * reused across all reads. - */ - protected void handlePacket(@NonNull BufferType recvbuf, int length) {} - - /** - * Called by the main loop to log errors. In some cases |e| may be null. - */ - protected void logError(@NonNull String msg, @Nullable Exception e) {} - - /** - * Called by start(), if successful, just prior to returning. - */ - protected void onStart() {} - - /** - * Called by stop() just prior to returning. - */ - protected void onStop() {} - - private void createAndRegisterFd() { - if (mFd != null) return; - - try { - mFd = createFd(); - } catch (Exception e) { - logError("Failed to create socket: ", e); - closeFd(mFd); - mFd = null; - } - - if (mFd == null) return; - - mQueue.addOnFileDescriptorEventListener( - mFd, - FD_EVENTS, - (fd, events) -> { - // Always call handleInput() so read/recvfrom are given - // a proper chance to encounter a meaningful errno and - // perhaps log a useful error message. - if (!isRunning() || !handleInput()) { - unregisterAndDestroyFd(); - return UNREGISTER_THIS_FD; - } - return FD_EVENTS; - }); - onStart(); - } - - private boolean isRunning() { - return (mFd != null) && mFd.valid(); - } - - // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. - private boolean handleInput() { - while (isRunning()) { - final int bytesRead; - - try { - bytesRead = readPacket(mFd, mBuffer); - if (bytesRead < 1) { - if (isRunning()) logError("Socket closed, exiting", null); - break; - } - mPacketsReceived++; - } catch (ErrnoException e) { - if (e.errno == OsConstants.EAGAIN) { - // We've read everything there is to read this time around. - return true; - } else if (e.errno == OsConstants.EINTR) { - continue; - } else { - if (isRunning()) logError("readPacket error: ", e); - break; - } - } catch (Exception e) { - if (isRunning()) logError("readPacket error: ", e); - break; - } - - try { - handlePacket(mBuffer, bytesRead); - } catch (Exception e) { - logError("handlePacket error: ", e); - break; - } - } - - return false; - } - - private void unregisterAndDestroyFd() { - if (mFd == null) return; - - mQueue.removeOnFileDescriptorEventListener(mFd); - closeFd(mFd); - mFd = null; - onStop(); - } - - private boolean onCorrectThread() { - return (mHandler.getLooper() == Looper.myLooper()); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/utils/PacketReader.java b/src/java/com/android/internal/net/ipsec/ike/utils/PacketReader.java deleted file mode 100644 index cd6b98d2..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/utils/PacketReader.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.utils; - -import static java.lang.Math.max; - -import android.os.Handler; -import android.system.Os; - -import java.io.FileDescriptor; - -/** - * Specialization of {@link FdEventsReader} that reads packets into a byte array. - * - * TODO: rename this class to something more correctly descriptive (something - * like [or less horrible than] IkeFdReadEventsHandler?). - * - * <p> This code is a exact copy of {@link PacketReader} in - * frameworks/base/packages/NetworkStack/src/android/net/util/PacketReader.java, except the class - * name is changed to avoid confusion. - * - * FIXME: b/130058477 Find a way to share the code between network stack and code outside. - * - * @hide - */ -public abstract class PacketReader extends FdEventsReader<byte[]> { - - public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024; - - protected PacketReader(Handler h) { - this(h, DEFAULT_RECV_BUF_SIZE); - } - - protected PacketReader(Handler h, int recvBufSize) { - super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]); - } - - @Override - protected final int recvBufSize(byte[] buffer) { - return buffer.length; - } - - /** - * Subclasses MAY override this to change the default read() implementation - * in favour of, say, recvfrom(). - * - * Implementations MUST return the bytes read or throw an Exception. - */ - @Override - protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception { - return Os.read(fd, packetBuffer, 0, packetBuffer.length); - } -} diff --git a/src/java/com/android/internal/net/ipsec/ike/utils/Retransmitter.java b/src/java/com/android/internal/net/ipsec/ike/utils/Retransmitter.java deleted file mode 100644 index 778d6859..00000000 --- a/src/java/com/android/internal/net/ipsec/ike/utils/Retransmitter.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.utils; - -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_RETRANSMIT; - -import android.os.Handler; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.ipsec.ike.message.IkeMessage; - -/** - * Retransmitter represents a class that will send a message and trigger delayed retransmissions - * - * <p>The Retransmitter class will queue retransmission signals on the provided handler. The owner - * of this retransmitter instance is expected to wait for the signal, and call retransmit() on the - * instance of this class. - */ -public abstract class Retransmitter { - private static IBackoffTimeoutCalculator sBackoffTimeoutCalculator = - new BackoffTimeoutCalculator(); - - /* - * Retransmit parameters - * - * (Re)transmission count | Relative timeout | Absolute timeout - * -------------------------+-------------------+------------------ - * 0 | 500ms | 500ms - * 1 | 1s | 1.5s - * 2 | 2s | 3.5s - * 3 | 4s | 7.5s - * 4 | 8s | 15.5s - * 5 | 16s | 31.5s - * - * TODO: Add retransmitter configurability - */ - static final double RETRANSMIT_BACKOFF_FACTOR = 2.0; - static final long RETRANSMIT_TIMEOUT_MS = 500L; - static final int RETRANSMIT_MAX_ATTEMPTS = 5; - - private final Handler mHandler; - private final IkeMessage mRetransmitMsg; - private int mRetransmitCount = 0; - - public Retransmitter(Handler handler, IkeMessage msg) { - mHandler = handler; - mRetransmitMsg = msg; - } - - /** - * Triggers a (re)transmission. Will enqueue a future retransmission signal on the given handler - */ - public void retransmit() { - if (mRetransmitMsg == null) { - return; - } - - // If the failed iteration is beyond the max attempts, clean up and shut down. - if (mRetransmitCount > RETRANSMIT_MAX_ATTEMPTS) { - handleRetransmissionFailure(); - return; - } - - send(mRetransmitMsg); - - long timeout = sBackoffTimeoutCalculator.getExponentialBackoffTimeout(mRetransmitCount++); - mHandler.sendMessageDelayed(mHandler.obtainMessage(CMD_RETRANSMIT, this), timeout); - } - - /** Cancels any future retransmissions */ - public void stopRetransmitting() { - mHandler.removeMessages(CMD_RETRANSMIT, this); - } - - /** Retrieves the message this retransmitter is tracking */ - public IkeMessage getMessage() { - return mRetransmitMsg; - } - - /** - * Implementation-provided sender - * - * <p>For Retransmitter-internal use only. - * - * @param msg the message to be sent - */ - protected abstract void send(IkeMessage msg); - - /** - * Callback for implementations to be informed that we have reached the max retransmissions. - * - * <p>For Retransmitter-internal use only. - */ - protected abstract void handleRetransmissionFailure(); - - /** - * IBackoffTimeoutCalculator provides interface for calculating retransmission backoff timeout. - * - * <p>IBackoffTimeoutCalculator exists so that the interface is injectable for testing. - */ - @VisibleForTesting - public interface IBackoffTimeoutCalculator { - /** Calculate retransmission backoff timeout */ - long getExponentialBackoffTimeout(int retransmitCount); - } - - private static final class BackoffTimeoutCalculator implements IBackoffTimeoutCalculator { - @Override - public long getExponentialBackoffTimeout(int retransmitCount) { - double expBackoffFactor = Math.pow(RETRANSMIT_BACKOFF_FACTOR, retransmitCount); - return (long) (RETRANSMIT_TIMEOUT_MS * expBackoffFactor); - } - } - - /** Sets IBackoffTimeoutCalculator */ - @VisibleForTesting - public static void setBackoffTimeoutCalculator(IBackoffTimeoutCalculator calculator) { - sBackoffTimeoutCalculator = calculator; - } - - /** Resets BackoffTimeoutCalculator of retransmitter */ - @VisibleForTesting - public static void resetBackoffTimeoutCalculator() { - sBackoffTimeoutCalculator = new BackoffTimeoutCalculator(); - } -} diff --git a/src/java/com/android/internal/net/utils/Log.java b/src/java/com/android/internal/net/utils/Log.java deleted file mode 100644 index 55ea0910..00000000 --- a/src/java/com/android/internal/net/utils/Log.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.utils; - -import android.os.Build; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.Locale; -import java.util.Objects; - -/** - * Manages logging for all IKE packages. Wraps Android's Log class to prevent leakage of PII. - */ -public class Log { - private final String mTAG; - private final boolean mIsEngBuild; - private final boolean mLogSensitive; - - /** - * Constructs a Log instance configured with the given tag and logSensitive flag - * - * @param tag the String tag to be used for this Log's logging - * @param logSensitive boolean flag marking whether sensitive data (PII) should be logged - */ - public Log(String tag, boolean logSensitive) { - this(tag, Build.IS_ENG, logSensitive); - } - - @VisibleForTesting - Log(String tag, boolean isEngBuild, boolean logSensitive) { - this.mTAG = tag; - this.mIsEngBuild = isEngBuild; - this.mLogSensitive = logSensitive; - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#VERBOSE} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - */ - public void v(String prefix, String msg) { - if (isLoggable(android.util.Log.VERBOSE)) { - android.util.Log.v(mTAG, prefix + ": " + msg); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#VERBOSE} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - * @param tr an Exception to log - */ - public void v(String prefix, String msg, Throwable tr) { - if (isLoggable(android.util.Log.VERBOSE)) { - android.util.Log.v(mTAG, prefix + ": " + msg, tr); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#DEBUG} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - */ - public void d(String prefix, String msg) { - if (isLoggable(android.util.Log.DEBUG)) { - android.util.Log.d(mTAG, prefix + ": " + msg); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#DEBUG} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - * @param tr an Exception to log - */ - public void d(String prefix, String msg, Throwable tr) { - if (isLoggable(android.util.Log.DEBUG)) { - android.util.Log.d(mTAG, prefix + ": " + msg, tr); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#INFO} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - */ - public void i(String prefix, String msg) { - if (isLoggable(android.util.Log.INFO)) { - android.util.Log.i(mTAG, prefix + ": " + msg); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#INFO} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - * @param tr an Exception to log - */ - public void i(String prefix, String msg, Throwable tr) { - if (isLoggable(android.util.Log.INFO)) { - android.util.Log.i(mTAG, prefix + ": " + msg, tr); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#WARN} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - */ - public void w(String prefix, String msg) { - if (isLoggable(android.util.Log.WARN)) { - android.util.Log.w(mTAG, prefix + ": " + msg); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#WARN} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - * @param tr an Exception to log - */ - public void w(String prefix, String msg, Throwable tr) { - if (isLoggable(android.util.Log.WARN)) { - android.util.Log.w(mTAG, prefix + ": " + msg, tr); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#ERROR} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - */ - public void e(String prefix, String msg) { - if (isLoggable(android.util.Log.ERROR)) { - android.util.Log.e(mTAG, prefix + ": " + msg); - } - } - - /** - * Logs the given prefix and msg Strings. - * - * <p>Note: Logging is only done if this instance's logging level is {@link - * android.util.Log#ERROR} or higher. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - * @param tr an Exception to log - */ - public void e(String prefix, String msg, Throwable tr) { - if (isLoggable(android.util.Log.ERROR)) { - android.util.Log.e(mTAG, prefix + ": " + msg, tr); - } - } - - /** - * What a Terrible Failure: Report a condition that should never happen. - * The error will always be logged at level ASSERT with the call stack. - * Depending on system configuration, a report may be added to the - * {@link android.os.DropBoxManager} and/or the process may be terminated - * immediately with an error dialog. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - */ - public void wtf(String prefix, String msg) { - android.util.Log.wtf(mTAG, prefix + ": " + msg); - - } - - /** - * What a Terrible Failure: Report a condition that should never happen. - * The error will always be logged at level ASSERT with the call stack. - * Depending on system configuration, a report may be added to the - * {@link android.os.DropBoxManager} and/or the process may be terminated - * immediately with an error dialog. - * - * @param prefix the String prefix to be used for this log entry - * @param msg the String msg to be logged - * @param tr an Exception to log - */ - public void wtf(String prefix, String msg, Throwable tr) { - android.util.Log.wtf(mTAG, prefix + ": " + msg, tr); - } - - /** - * Returns a String-formatted version of the given PII. - * - * <p>Depending on the logging configurations and build-type, the returned PII may be - * obfuscated. - * - * @param pii the PII to be formatted - * @return the String-formatted version of the PII - */ - public String pii(Object pii) { - if (!mIsEngBuild || !mLogSensitive) { - return String.valueOf(Objects.hashCode(pii)); - } else { - if (pii instanceof byte[]) { - return byteArrayToHexString((byte[]) pii); - } - return String.valueOf(pii); - } - } - - /** - * Checks whether the given logging level (defined in {@link android.util.Log}) is loggable for - * this Log. - * - * @param level the logging level to be checked for being loggable - * @return true iff level is at the configured logging level or higher - */ - private boolean isLoggable(int level) { - return android.util.Log.isLoggable(mTAG, level); - } - - /** - * Returns the hex-String representation of the given byte[]. - * - * @param data the byte[] to be represented - * @return the String representation of data - */ - public static String byteArrayToHexString(byte[] data) { - if (data == null || data.length == 0) { - return ""; - } - - StringBuilder sb = new StringBuilder(); - for (byte b : data) { - sb.append(String.format(Locale.US, "%02X", b)); - } - return sb.toString(); - } -} diff --git a/src/java/com/android/internal/net/utils/SimpleStateMachine.java b/src/java/com/android/internal/net/utils/SimpleStateMachine.java deleted file mode 100644 index a2fffddf..00000000 --- a/src/java/com/android/internal/net/utils/SimpleStateMachine.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.utils; - -/** - * SimpleStateMachine provides a minimal, synchronous state machine framework. - * - * <p>This state machine is of limited functionality, but sufficient for implementation of simple - * protocols. Due to the limited functionality, it is also easy to read and maintain. - * - * SimpleStateMachine defaults to the null state. Implementers should immediately transition - * to their default state when instantiated. - * - * @param <T> The input message type. - * @param <R> The result type. For SimpleStateMachines without a return value, use {@link - * java.lang.Void} - */ -public abstract class SimpleStateMachine<T, R> { - protected final SimpleState mNullState = - new SimpleState() { - public R process(T msg) { - throw new IllegalStateException("Process called on null state"); - } - }; - - protected SimpleState mState = mNullState; - - // Non-static to allow for compiler verification of T, R from SimpleStateMachine - protected abstract class SimpleState { - public abstract R process(T msg); - } - - /** - * Processes the given message based on the current {@link SimpleState} - * - * @param msg The message to be processed by the current state - * @return The result of the processing by the current state - */ - public R process(T msg) { - return mState.process(msg); - } - - /** - * Transitions to a new state - * - * @param newState The {@link SimpleState} that the {@link SimpleStateMachine} should - * transition to - * @throws IllegalArgumentException if newState is null - */ - protected void transitionTo(SimpleState newState) { - if (newState == null) { - throw new IllegalArgumentException("SimpleState value must be non-null."); - } - - mState = newState; - } - - /** - * Transitions to a new state, and lets the new state process the given message - * - * @param newState The {@link SimpleState} that the {@link SimpleStateMachine} should transition - * to. This state will immediately be requested to process the given message. - * @param msg The message that should be processed by the new state - * @return The result of the processing by the new state - * @throws IllegalArgumentException if newState is null - */ - protected R transitionAndProcess(SimpleState newState, T msg) { - transitionTo(newState); - return mState.process(msg); - } -} diff --git a/tests/Android.mk b/tests/Android.mk new file mode 100644 index 00000000..caa27f66 --- /dev/null +++ b/tests/Android.mk @@ -0,0 +1,18 @@ +# Copyright (C) 2018 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file diff --git a/tests/iketests/Android.bp b/tests/iketests/Android.bp deleted file mode 100644 index c5263536..00000000 --- a/tests/iketests/Android.bp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2018 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. - -android_test { - name: "FrameworksIkeTests", - - srcs: ["src/java/**/*.java"], - - platform_apis: false, - certificate: "platform", - test_suites: ["device-tests"], - - libs: ["android.test.runner"], - - static_libs: [ - "ike", - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-inline-minus-junit4", - "services.core", - ], - - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - "libmultiplejvmtiagentsinterferenceagent", - ], -} diff --git a/tests/iketests/Android.mk b/tests/iketests/Android.mk new file mode 100644 index 00000000..0da49b47 --- /dev/null +++ b/tests/iketests/Android.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2018 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src/java) + +LOCAL_PACKAGE_NAME := FrameworksIkeTests +LOCAL_PRIVATE_PLATFORM_APIS := false +LOCAL_CERTIFICATE := platform +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_MODULE_TAGS := tests + +LOCAL_JAVA_LIBRARIES := android.test.runner + +LOCAL_STATIC_JAVA_LIBRARIES := ike \ + androidx.test.rules \ + frameworks-base-testutils \ + mockito-target-minus-junit4 \ + NetworkStackBase + +include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/tests/iketests/AndroidManifest.xml b/tests/iketests/AndroidManifest.xml index 7260e621..d69cbc8f 100644 --- a/tests/iketests/AndroidManifest.xml +++ b/tests/iketests/AndroidManifest.xml @@ -20,14 +20,7 @@ <uses-permission android:name="android.permission.INTERNET"/> - <!-- - 'debuggable=true' is required to properly load mockito jvmti dependencies, - otherwise it gives the following error at runtime: - - Openjdkjvmti plugin was loaded on a non-debuggable Runtime. - Plugin was loaded too late to change runtime state to DEBUGGABLE. - --> - <application android:label="FrameworksIkeTests" android:debuggable="true"> + <application android:label="FrameworksIkeTests"> <uses-library android:name="android.test.runner" /> </application> diff --git a/tests/iketests/assets/key/end-cert-key-a.key b/tests/iketests/assets/key/end-cert-key-a.key deleted file mode 100644 index a506a350..00000000 --- a/tests/iketests/assets/key/end-cert-key-a.key +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAphl0fstit/XcelkX -2iKoosGsZ2U5rU+mYWR/9RJIY+qR3OYrBeqqtdvjPOu2fNjHEtsO//dCRvdxVWdx -20ADPQIDAQABAkApsawXg/Bk4zeUErc1D4wrRtiDH9rJkXvfaL3iA9PeGIU2j2ci -WwqbJY6HhGSJiNAKMVHRMRAtoaBeX80DOqH1AiEA0pWj6kItG2zTww09sqc6ymTc -1aknFaGuXZY+RO0MTF8CIQDJ68uKQ8LV6labUnnmLPUnIYfVPY8XTNpvkwazeTuV -4wIgDy60u637vI9zEQwCV8AQ0AjHlyvz4m5euOadJLEGgvcCIQCc9BpsySsjmFnl -tgBm+L8+wYOSL52QYP7SB5kH3M6CPQIgQQIVYRKf44G9agh09utnaiw11FCwRr0M -EKsdiVmkw+o= ------END PRIVATE KEY-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/end-cert-a.pem b/tests/iketests/assets/pem/end-cert-a.pem deleted file mode 100644 index 2e872952..00000000 --- a/tests/iketests/assets/pem/end-cert-a.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDRzCCAi+gAwIBAgIIZSciRUaEUakwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxHDAaBgNVBAMTE2NhLnRlc3QuYW5kcm9p -ZC5uZXQwHhcNMTkwNzE2MTcxODMxWhcNMjQwNzE0MTcxODMxWjBBMQswCQYDVQQG -EwJVUzEQMA4GA1UEChMHQW5kcm9pZDEgMB4GA1UEAxMXc2VydmVyLnRlc3QuYW5k -cm9pZC5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpU5M+c3Qg -Sej5NeCboB5T6R0XaODqo/hpZFkjTXt5ku2lvsioLU0xC38K9Cym7kPU0kGMAl1p -tatMZ2Uxde/sDiLyFwYgx//TniDNnxdDXYYxcZNbfV4ERcuPmTexq9t86MneVkxn -hJ9dEBJcr2goFaFIebCUlj3DF827/JQhWgV54M9trPOGOyoRy5HvH+IxOOt8PXaL -vySQZxo4bC6m+qeQQZCgZAwvGagFF9KjVFyKt9ZAVp97wQi7yo+Bzm5I54C4EUbT -XnTRITQXqFKOUXVGYPChwgZTEz/2s6Wh1CR0LjNFTaDMlsUJkUbGn27iZc90nd5w -6WAXYQgsmXnTAgMBAAGjRzBFMB8GA1UdIwQYMBaAFGYUzuvZUaVJl8mcxejuFiUN -GcTfMCIGA1UdEQQbMBmCF3NlcnZlci50ZXN0LmFuZHJvaWQubmV0MA0GCSqGSIb3 -DQEBCwUAA4IBAQByajAzcLrMc2gjDSzTd+5/VTgLhoJfJul3FgsUzZHa9EiRUChV -O94ZCLWWoZxeB0iejaUqrLz/xCJqeC3wbNP7LejiW2qgUAoJdOvNtDGiVx2P7wid -iXS4y49+IYP+T1BVWNNrI+zcAycN2uiQlEKR5KQ3cNXVHZoiVOroheHzi8ezSeYM -j5bhJ2GbpOw9/4PkaBonnQNs9sljkyZ2keYrir1xzf4PI9gieXniJcNuAjYNaAAA -oaHKXah9NggbAVEXEZjLoKtQQqWFz9wNE8AXsIdoD4gOeBuwNQSyn+FmDJdI/mpA -enbz3qbTVurltTHySye0+nhlP7XTifyEanXM ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/end-cert-b.pem b/tests/iketests/assets/pem/end-cert-b.pem deleted file mode 100644 index f25d3524..00000000 --- a/tests/iketests/assets/pem/end-cert-b.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDWDCCAkCgAwIBAgIIRs9N2RKvOUYwDQYJKoZIhvcNAQELBQAwQTELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIDAeBgNVBAMTF3R3by5jYS50ZXN0LmFu -ZHJvaWQubmV0MB4XDTE5MDkzMDE4NDg1MVoXDTI0MDkyODE4NDg1MVowQTELMAkG -A1UEBhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIDAeBgNVBAMTF3NlcnZlci50ZXN0 -LmFuZHJvaWQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxmhy -posM/dhFPvmHpiqk+bJR2yfw5AeWhspnjuIJB1X3/TFCRTmLLsQ8VGRQnSKYlAYJ -2r5XpgYQ09r4DYAbHwL2oSYtktMzqax22JlR73czZH4D3UTtKk7CdLtc1NPFXYFm -lJ9uE/TD1pXvXwj9vdYp8tVuls2Rv+hBNtgM4nT1FqyMpp1sr5t2LIdx+WpDR4PC -8C7HExeuw4wOBY6mWp4uErWqDFBfQNI3dzwpySRtnuMVKSX5Qcj6Z+bqKmtAgAnZ -qdoLegn4sBbELDFW1QYNqp9QgdJO9P9R2lI8LZvKcd2yB8zJ2+JK1Efh9ErzhqFn -Rc1BzbsBxKJBbppZXQIDAQABo1QwUjAfBgNVHSMEGDAWgBRypK7W5FhP8MtsugM1 -TPfyca8IpDAaBgNVHREEEzARgg9pa2UuYW5kcm9pZC5uZXQwEwYDVR0lBAwwCgYI -KwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBADJu4bfbDO/PUSjTMuH1u6x9iTdx -PKVkzFHqeiAsEKccyenuFKrwkkoIF+gieJeKKDj6lKFDP4uPOYIuNxs9td8G52+1 -5XKX2v9heaw6uFU3AlMmoAHKwIiM+U6eweuG+rVG2doTbMW2OOrEfJ5mgQtky7tx -EIPUL9gUpAKqvsC7pJ7nrakm6TBkhYaTtDYOvdD97LyH9/5h32WKn9zU2H4dog+4 -87K6icdjBpd4ViPXbOBuOLvEsnMDmbSC3/12hv59swAf865SZN10B7ScYbg/yS9V -x2YtMxPMNOOqC71Z/JE5mc80Un0nd9eJFxPueWqeH/4cGA6gL7ZtAeor0BE= ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/end-cert-small.pem b/tests/iketests/assets/pem/end-cert-small.pem deleted file mode 100644 index b21aa0df..00000000 --- a/tests/iketests/assets/pem/end-cert-small.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBtjCCAWCgAwIBAgIIC0mN0a99ZR0wDQYJKoZIhvcNAQELBQAwQzELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIjAgBgNVBAMTGXNtYWxsLmNhLnRlc3Qu -YW5kcm9pZC5uZXQwHhcNMTkwNzE2MjIyMzM0WhcNMjQwNzE0MjIyMzM0WjBHMQsw -CQYDVQQGEwJVUzEQMA4GA1UEChMHQW5kcm9pZDEmMCQGA1UEAxMdc21hbGwuc2Vy -dmVyLnRlc3QuYW5kcm9pZC5uZXQwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAphl0 -fstit/XcelkX2iKoosGsZ2U5rU+mYWR/9RJIY+qR3OYrBeqqtdvjPOu2fNjHEtsO -//dCRvdxVWdx20ADPQIDAQABozQwMjAfBgNVHSMEGDAWgBRuPvsaYu/KSLILNs2l -qzN0Q3bo8jAPBgNVHREECDAGhwTAqCuKMA0GCSqGSIb3DQEBCwUAA0EA1HWQseq+ -kfL5YaYN7Klb3WiPPg8Vxj4dMNYiQTSH7AG7Gt1Yc6NqBLhmMpa+1T+gwlDdvkD4 -RPIxjfK12sbbog== ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/intermediate-ca-b-one.pem b/tests/iketests/assets/pem/intermediate-ca-b-one.pem deleted file mode 100644 index 707e575b..00000000 --- a/tests/iketests/assets/pem/intermediate-ca-b-one.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDaDCCAlCgAwIBAgIIIbjMyRn2770wDQYJKoZIhvcNAQELBQAwQjELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxITAfBgNVBAMTGHJvb3QuY2EudGVzdC5h -bmRyb2lkLm5ldDAeFw0xOTA5MzAxODQzMThaFw0yNDA5MjgxODQzMThaMEExCzAJ -BgNVBAYTAlVTMRAwDgYDVQQKEwdBbmRyb2lkMSAwHgYDVQQDExdvbmUuY2EudGVz -dC5hbmRyb2lkLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNN -sRr5Z30rAEw2jrAh/BIekbEy/MvOucAr1w0lxH71p+ybRBx5Bj7G07UGXbL659gm -meMV6nabY4HjQXNMq22POiJBZj+U+rw34br6waljBttxCmmJac1VvgqNsSspXjRy -NbiVQdFjyKSX0NOPcEkwANk15mZbOgJBaYYc8jQCY2G/p8eARVBTLJCy8LEwEU6j -XRv/4eYST79qpBFc7gQQj2FLmh9oppDIvcIVBHwtd1tBoVuehRSud1o8vQRkl/HJ -Mrwp24nO5YYhmVNSFRtBpmWMSu1KknFUwkOebINUNsKXXHebVa7cP4XIQUL8mRT3 -5X9rFJFSQJE01S3NjNMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B -Af8EBAMCAQYwHQYDVR0OBBYEFHK3FIm7g8dxEIwK9zMAO8EWhRYxMB8GA1UdIwQY -MBaAFEmfqEeF14Nj91ekIpR+sVhCEoAaMA0GCSqGSIb3DQEBCwUAA4IBAQAeMlXT -TnxZo8oz0204gKZ63RzlgDpJ7SqA3qFG+pV+TiqGfSuVkXuIdOskjxJnA9VxUzrr -LdMTCn5e0FK6wCYjZ2GT/CD7oD3vSMkzGbLGNcNJhhDHUq8BOLPkPzz/rwQFPBSb -zr6hsiVXphEt/psGoN7Eu9blPeQaIwMfWnaufAwF664S/3dmCRbNMWSam1qzzz8q -jr0cDOIMa//ZIAcM16cvoBK6pFGnUmuoJYYRtfpY5MmfCWz0sCJxENIX/lxyhd7N -FdRALA1ZP3E//Tn2vQoeFjbKaAba527RE26HgHJ9zZDo1nn8J8J/YwYRJdBWM/3S -LYebNiMtcyB5nIkj ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/intermediate-ca-b-two.pem b/tests/iketests/assets/pem/intermediate-ca-b-two.pem deleted file mode 100644 index 39808f88..00000000 --- a/tests/iketests/assets/pem/intermediate-ca-b-two.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIIKWCREnNCs+wwDQYJKoZIhvcNAQELBQAwQTELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIDAeBgNVBAMTF29uZS5jYS50ZXN0LmFu -ZHJvaWQubmV0MB4XDTE5MDkzMDE4NDQwMloXDTI0MDkyODE4NDQwMlowQTELMAkG -A1UEBhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIDAeBgNVBAMTF3R3by5jYS50ZXN0 -LmFuZHJvaWQubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLUa -RqkYl2m7lUmMnkooqO0DNNY1aN9r7mJc3ndYn5gjkpb3yLgOYPDNLcQerV6uWk/u -qKudNHed2dInGonl3oxwwv7++6oUvvtrSWLDZlRg16GsdIE1Y98DSMQWkSxevYy9 -Nh6FGTdlBFQVMpiMa8qHEkrOyKsy85yCW1sgzlpGTIBwbDAqYtwe3rgbwyHwUtfy -0EU++DBcR4ll/pDqB0OQtW5E3AOq2GH1iaGeFLKSUQ5KAbdI8y4/b8IkSDffvxcc -kXig7S54aLrNlL/ZjQ+H4Chgjj2A5wMucd81+Fb60Udej73ICL9PpMPnXQ1+BVYd -MJ/txjLNmrOJG9yEHQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB -/wQEAwIBBjAdBgNVHQ4EFgQUcqSu1uRYT/DLbLoDNUz38nGvCKQwHwYDVR0jBBgw -FoAUcrcUibuDx3EQjAr3MwA7wRaFFjEwDQYJKoZIhvcNAQELBQADggEBADY461GT -Rw0dGnD07xaGJcI0i0pV+WnGSrl1s1PAIdMYihJAqYnh10fXbFXLm2WMWVmv/pxs -FI/xDJno+pd4mCa/sIhm63ar/Nv+lFQmcpIlvSlKnhhV4SLNBeqbVhPBGTCHfrG4 -aIyCwm1KJsnkWbf03crhSskR/2CXIjX6lcAy7K3fE2u1ELpAdH0kMJR7VXkLFLUm -gqe9YCluR0weMpe2sCaOGzdVzQSmMMCzGP5cxeFR5U6K40kMOpiW11JNmQ06xI/m -YVkMNwoiV/ITT0/C/g9FxJmkO0mVSLEqxaLS/hNiQNDlroVM0rbxhzviXLI3R3AO -50VvlOQYGxWed/I= ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/self-signed-ca-a.pem b/tests/iketests/assets/pem/self-signed-ca-a.pem deleted file mode 100644 index 5135ea70..00000000 --- a/tests/iketests/assets/pem/self-signed-ca-a.pem +++ /dev/null @@ -1,20 +0,0 @@ ------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/iketests/assets/pem/self-signed-ca-b.pem b/tests/iketests/assets/pem/self-signed-ca-b.pem deleted file mode 100644 index 972fd553..00000000 --- a/tests/iketests/assets/pem/self-signed-ca-b.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDSDCCAjCgAwIBAgIITJQJ6HC1rjwwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxITAfBgNVBAMTGHJvb3QuY2EudGVzdC5h -bmRyb2lkLm5ldDAeFw0xOTA5MzAxNzU1NTJaFw0yOTA5MjcxNzU1NTJaMEIxCzAJ -BgNVBAYTAlVTMRAwDgYDVQQKEwdBbmRyb2lkMSEwHwYDVQQDExhyb290LmNhLnRl -c3QuYW5kcm9pZC5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCT -q3hGF+JvLaB1xW7KGKmaxiQ7BxX2Sn7cbp7ggoVYXsFlBUuPPv3+Vg5PfPCPhsJ8 -/7w4HyKo3uc/vHs5HpQ7rSd9blhAkfmJci2ULLq73FB8Mix4CzPwMx29RrN1X9bU -z4G0vJMczIBGxbZ0uw7n8bKcXBV7AIeax+J8lseEZ3k8iSuBkUJqGIpPFKTqByFZ -A1Lvt47xkON5SZh6c/Oe+o6291wXaCOJUSAKv6PAWZkq9HeD2fqKA/ck9dBaz1M3 -YvzQ9V/7so3/dECjAfKia388h1I6XSGNUM+d5hpxMXpAFgG42eUXHpJ10OjDvSwd -7ZSC91/kRQewUomEKBK1AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P -AQH/BAQDAgEGMB0GA1UdDgQWBBRJn6hHhdeDY/dXpCKUfrFYQhKAGjANBgkqhkiG -9w0BAQsFAAOCAQEAig/94aGfHBhZuvbbhwAK4rUNpizmR567u0ZJ+QUEKyAlo9lT -ZWYHSm7qTAZYvPEjzTQIptnAlxCHePXh3Cfwgo+r82lhG2rcdI03iRyvHWjM8gyk -BXCJTi0Q08JHHpTP6GnAqpz58qEIFkk8P766zNXdhYrGPOydF+p7MFcb1Zv1gum3 -zmRLt0XUAMfjPUv1Bl8kTKFxH5lkMBLR1E0jnoJoTTfgRPrf9CuFSoh48n7YhoBT -KV75xZY8b8+SuB0v6BvQmkpKZGoxBjuVsShyG7q1+4JTAtwhiP7BlkDvVkaBEi7t -WIMFp2r2ZDisHgastNaeYFyzHYz9g1FCCrHQ4w== ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/assets/pem/self-signed-ca-small.pem b/tests/iketests/assets/pem/self-signed-ca-small.pem deleted file mode 100644 index bb587bcc..00000000 --- a/tests/iketests/assets/pem/self-signed-ca-small.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBwDCCAWqgAwIBAgIIWKLr7BJ1wyEwDQYJKoZIhvcNAQELBQAwQzELMAkGA1UE -BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIjAgBgNVBAMTGXNtYWxsLmNhLnRlc3Qu -YW5kcm9pZC5uZXQwHhcNMTkwNzE2MjIwNjAzWhcNMjkwNzEzMjIwNjAzWjBDMQsw -CQYDVQQGEwJVUzEQMA4GA1UEChMHQW5kcm9pZDEiMCAGA1UEAxMZc21hbGwuY2Eu -dGVzdC5hbmRyb2lkLm5ldDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDY/gUvbZjF -YuslvcYduKyWeUr30dgOcC6UmAy0toNjnowtsjwp1Zqkp6+SB/vkmRatrMIDgyu9 -KXKRfy9TFUY9AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMB0GA1UdDgQWBBRuPvsaYu/KSLILNs2lqzN0Q3bo8jANBgkqhkiG9w0BAQsF -AANBAMRtcdhE8Ebew9PGNwZtfsp1KiI0ZGLE6zP9YKZYk5VZxqpr914LzEMKZpXA -BqlgNWIcp4nRbuIhLNLyvWRdW0A= ------END CERTIFICATE-----
\ No newline at end of file diff --git a/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java b/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java deleted file mode 100644 index eed32e3e..00000000 --- a/tests/iketests/src/java/android/net/eap/EapSessionConfigTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2019 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.eap; - -import static android.net.eap.EapSessionConfig.DEFAULT_IDENTITY; -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -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 org.junit.Test; - -import java.nio.charset.StandardCharsets; - -public class EapSessionConfigTest { - private static final byte[] EAP_IDENTITY = - "test@android.net".getBytes(StandardCharsets.US_ASCII); - 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 static final String USERNAME = "username"; - private static final String PASSWORD = "password"; - - @Test - public void testBuildEapSim() { - EapSessionConfig result = new EapSessionConfig.Builder() - .setEapIdentity(EAP_IDENTITY) - .setEapSimConfig(SUB_ID, APPTYPE_USIM) - .build(); - - assertArrayEquals(EAP_IDENTITY, result.eapIdentity); - - EapMethodConfig eapMethodConfig = result.eapConfigs.get(EAP_TYPE_SIM); - assertEquals(EAP_TYPE_SIM, eapMethodConfig.methodType); - EapSimConfig eapSimConfig = (EapSimConfig) eapMethodConfig; - assertEquals(SUB_ID, eapSimConfig.subId); - assertEquals(APPTYPE_USIM, eapSimConfig.apptype); - } - - @Test - public void testBuildEapAka() { - EapSessionConfig result = new EapSessionConfig.Builder() - .setEapAkaConfig(SUB_ID, APPTYPE_USIM) - .build(); - - assertArrayEquals(DEFAULT_IDENTITY, result.eapIdentity); - EapMethodConfig eapMethodConfig = result.eapConfigs.get(EAP_TYPE_AKA); - EapAkaConfig eapAkaConfig = (EapAkaConfig) eapMethodConfig; - assertEquals(SUB_ID, eapAkaConfig.subId); - assertEquals(APPTYPE_USIM, eapAkaConfig.apptype); - } - - @Test - public void testBuildEapAkaPrime() { - EapSessionConfig result = - new EapSessionConfig.Builder() - .setEapAkaPrimeConfig( - SUB_ID, APPTYPE_USIM, NETWORK_NAME, ALLOW_MISMATCHED_NETWORK_NAMES) - .build(); - - assertEquals(DEFAULT_IDENTITY, result.eapIdentity); - EapMethodConfig eapMethodConfig = result.eapConfigs.get(EAP_TYPE_AKA_PRIME); - EapAkaPrimeConfig eapAkaPrimeConfig = (EapAkaPrimeConfig) eapMethodConfig; - assertEquals(SUB_ID, eapAkaPrimeConfig.subId); - assertEquals(APPTYPE_USIM, eapAkaPrimeConfig.apptype); - assertEquals(NETWORK_NAME, eapAkaPrimeConfig.networkName); - assertTrue(eapAkaPrimeConfig.allowMismatchedNetworkNames); - } - - @Test - public void testBuildEapMsChapV2() { - EapSessionConfig result = - new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build(); - - EapMsChapV2Config config = (EapMsChapV2Config) result.eapConfigs.get(EAP_TYPE_MSCHAP_V2); - assertEquals(USERNAME, config.username); - assertEquals(PASSWORD, config.password); - } - - @Test - public void testBuildWithoutConfigs() { - try { - new EapSessionConfig.Builder().build(); - fail("build() should throw an IllegalStateException if no EAP methods are configured"); - } catch (IllegalStateException expected) { - } - } -} diff --git a/tests/iketests/src/java/android/net/ipsec/ike/ChildSessionConfigurationTest.java b/tests/iketests/src/java/android/net/ipsec/ike/ChildSessionConfigurationTest.java deleted file mode 100644 index 08d89942..00000000 --- a/tests/iketests/src/java/android/net/ipsec/ike/ChildSessionConfigurationTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; - -import android.net.LinkAddress; - -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Address; - -import libcore.net.InetAddressUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.util.LinkedList; -import java.util.List; - -public final class ChildSessionConfigurationTest { - private static final int IP4_PREFIX_LEN = 28; - private static final Inet4Address IPV4_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - private static final Inet4Address IPV4_NETMASK = - (Inet4Address) (InetAddressUtils.parseNumericAddress("255.255.255.240")); - private static final LinkAddress IPV4_LINK_ADDRESS = - new LinkAddress(IPV4_ADDRESS, IP4_PREFIX_LEN); - - private static final int IP6_PREFIX_LEN = 64; - private static final Inet6Address IPV6_ADDRESS = - (Inet6Address) (InetAddressUtils.parseNumericAddress("2001:db8::1")); - private static final LinkAddress IPV6_LINK_ADDRESS = - new LinkAddress(IPV6_ADDRESS, IP6_PREFIX_LEN); - - private List mMockInTsList; - private List mMockOutTsList; - - private ConfigAttributeIpv4Address mIpv4Attr; - private ConfigAttributeIpv4Netmask mNetmaskAttr; - private ConfigAttributeIpv6Address mIpv6Attr; - - @Before - public void setUp() throws Exception { - mMockInTsList = new LinkedList<IkeTrafficSelector>(); - mMockInTsList.add(mock(IkeTrafficSelector.class)); - - mMockOutTsList = new LinkedList<IkeTrafficSelector>(); - mMockOutTsList.add(mock(IkeTrafficSelector.class)); - mMockOutTsList.add(mock(IkeTrafficSelector.class)); - - mIpv4Attr = new ConfigAttributeIpv4Address(IPV4_ADDRESS); - mNetmaskAttr = new ConfigAttributeIpv4Netmask(IPV4_NETMASK.getAddress()); - mIpv6Attr = new ConfigAttributeIpv6Address(IPV6_LINK_ADDRESS); - } - - private void verifySessionConfigCommon(ChildSessionConfiguration sessionConfig) { - verifyTsList(mMockInTsList, sessionConfig.getInboundTrafficSelectors()); - verifyTsList(mMockOutTsList, sessionConfig.getOutboundTrafficSelectors()); - } - - private void verifyTsList( - List<IkeTrafficSelector> expectedList, List<IkeTrafficSelector> tsList) { - assertEquals(expectedList.size(), tsList.size()); - for (int i = 0; i < expectedList.size(); i++) { - assertEquals(expectedList.get(i), tsList.get(i)); - } - } - - @Test - public void testBuildWithoutConfig() { - ChildSessionConfiguration sessionConfig = - new ChildSessionConfiguration(mMockInTsList, mMockOutTsList); - - verifySessionConfigCommon(sessionConfig); - } - - @Test - public void testBuildWithNetmaskAttr() { - List<ConfigAttribute> attributeList = new LinkedList<>(); - attributeList.add(mIpv4Attr); - attributeList.add(mNetmaskAttr); - attributeList.add(mIpv6Attr); - - IkeConfigPayload configPayload = new IkeConfigPayload(true /*isReply*/, attributeList); - - ChildSessionConfiguration sessionConfig = - new ChildSessionConfiguration(mMockInTsList, mMockOutTsList, configPayload); - - verifySessionConfigCommon(sessionConfig); - - List<LinkAddress> expectedInternalAddrList = new LinkedList<>(); - expectedInternalAddrList.add(IPV4_LINK_ADDRESS); - expectedInternalAddrList.add(IPV6_LINK_ADDRESS); - - assertEquals( - expectedInternalAddrList.size(), sessionConfig.getInternalAddressList().size()); - for (int i = 0; i < expectedInternalAddrList.size(); i++) { - assertEquals( - expectedInternalAddrList.get(i), sessionConfig.getInternalAddressList().get(i)); - } - } - - @Test - public void testBuildWithoutNetmaskAttr() { - List<ConfigAttribute> attributeList = new LinkedList<>(); - attributeList.add(mIpv4Attr); - attributeList.add(mIpv6Attr); - - IkeConfigPayload configPayload = new IkeConfigPayload(true /*isReply*/, attributeList); - - ChildSessionConfiguration sessionConfig = - new ChildSessionConfiguration(mMockInTsList, mMockOutTsList, configPayload); - - verifySessionConfigCommon(sessionConfig); - - List<LinkAddress> expectedInternalAddrList = new LinkedList<>(); - expectedInternalAddrList.add(new LinkAddress(IPV4_ADDRESS, 32)); - expectedInternalAddrList.add(IPV6_LINK_ADDRESS); - - assertEquals( - expectedInternalAddrList.size(), sessionConfig.getInternalAddressList().size()); - for (int i = 0; i < expectedInternalAddrList.size(); i++) { - assertEquals( - expectedInternalAddrList.get(i), sessionConfig.getInternalAddressList().get(i)); - } - } - - @Test - public void testBuildWithConfigReq() { - List<ConfigAttribute> attributeList = new LinkedList<>(); - attributeList.add(mIpv4Attr); - attributeList.add(mIpv6Attr); - - IkeConfigPayload configPayload = new IkeConfigPayload(false /*isReply*/, attributeList); - - try { - new ChildSessionConfiguration(mMockInTsList, mMockOutTsList, configPayload); - fail("Expected to fail because provided config paylaod is not a reply."); - } catch (IllegalArgumentException expected) { - - } - } -} diff --git a/tests/iketests/src/java/android/net/ipsec/ike/ChildSessionOptionsTest.java b/tests/iketests/src/java/android/net/ipsec/ike/ChildSessionOptionsTest.java deleted file mode 100644 index 242957d6..00000000 --- a/tests/iketests/src/java/android/net/ipsec/ike/ChildSessionOptionsTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import org.junit.Test; - -public final class ChildSessionOptionsTest { - private static final int NUM_TS = 1; - - @Test - public void testBuild() throws Exception { - ChildSaProposal saProposal = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, - SaProposal.KEY_LEN_AES_128) - .build(); - ChildSessionOptions sessionOptions = - new TunnelModeChildSessionOptions.Builder().addSaProposal(saProposal).build(); - - assertArrayEquals(new SaProposal[] {saProposal}, sessionOptions.getSaProposals()); - assertEquals(NUM_TS, sessionOptions.getLocalTrafficSelectors().length); - assertEquals(NUM_TS, sessionOptions.getRemoteTrafficSelectors().length); - assertFalse(sessionOptions.isTransportMode()); - } - - @Test - public void testBuildWithoutSaProposal() throws Exception { - try { - new TunnelModeChildSessionOptions.Builder().build(); - fail("Expected to fail due to the absence of SA proposal."); - } catch (IllegalArgumentException expected) { - } - } -} diff --git a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionOptionsTest.java b/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionOptionsTest.java deleted file mode 100644 index fa077d17..00000000 --- a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionOptionsTest.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static android.net.ipsec.ike.IkeSessionOptions.IkeAuthConfig; -import static android.net.ipsec.ike.IkeSessionOptions.IkeAuthDigitalSignLocalConfig; -import static android.net.ipsec.ike.IkeSessionOptions.IkeAuthDigitalSignRemoteConfig; -import static android.net.ipsec.ike.IkeSessionOptions.IkeAuthEapConfig; -import static android.net.ipsec.ike.IkeSessionOptions.IkeAuthPskConfig; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; - -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.eap.EapSessionConfig; - -import androidx.test.InstrumentationRegistry; - -import com.android.internal.net.TestUtils; - -import libcore.net.InetAddressUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.net.Inet4Address; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.security.interfaces.DSAPrivateKey; -import java.security.interfaces.RSAPrivateKey; - -public final class IkeSessionOptionsTest { - private static final String PSK_HEX_STRING = "6A756E69706572313233"; - private static final byte[] PSK = TestUtils.hexStringToByteArray(PSK_HEX_STRING); - - private static final Inet4Address LOCAL_IPV4_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private static final Inet4Address REMOTE_IPV4_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - - private UdpEncapsulationSocket mUdpEncapSocket; - private IkeSaProposal mIkeSaProposal; - private IkeIdentification mLocalIdentification; - private IkeIdentification mRemoteIdentification; - - private X509Certificate mMockServerCaCert; - private X509Certificate mMockClientEndCert; - private PrivateKey mMockRsaPrivateKey; - - @Before - public void setUp() throws Exception { - Context context = InstrumentationRegistry.getContext(); - IpSecManager ipSecManager = (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE); - mUdpEncapSocket = ipSecManager.openUdpEncapsulationSocket(); - - mIkeSaProposal = - new IkeSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, - SaProposal.KEY_LEN_AES_128) - .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) - .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) - .build(); - mLocalIdentification = new IkeIpv4AddrIdentification(LOCAL_IPV4_ADDRESS); - mRemoteIdentification = new IkeIpv4AddrIdentification(REMOTE_IPV4_ADDRESS); - - mMockServerCaCert = mock(X509Certificate.class); - mMockClientEndCert = mock(X509Certificate.class); - mMockRsaPrivateKey = mock(RSAPrivateKey.class); - } - - @After - public void tearDown() throws Exception { - mUdpEncapSocket.close(); - } - - private void verifyIkeSessionOptionsCommon(IkeSessionOptions sessionOptions) { - assertEquals(REMOTE_IPV4_ADDRESS, sessionOptions.getServerAddress()); - assertEquals(mUdpEncapSocket, sessionOptions.getUdpEncapsulationSocket()); - assertArrayEquals(new SaProposal[] {mIkeSaProposal}, sessionOptions.getSaProposals()); - - assertEquals(mLocalIdentification, sessionOptions.getLocalIdentification()); - assertEquals(mRemoteIdentification, sessionOptions.getRemoteIdentification()); - - assertFalse(sessionOptions.isIkeFragmentationSupported()); - } - - @Test - public void testBuildWithPsk() throws Exception { - IkeSessionOptions sessionOptions = - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(mIkeSaProposal) - .setLocalIdentification(mLocalIdentification) - .setRemoteIdentification(mRemoteIdentification) - .setAuthPsk(PSK) - .build(); - - verifyIkeSessionOptionsCommon(sessionOptions); - - IkeAuthConfig localConfig = sessionOptions.getLocalAuthConfig(); - assertTrue(localConfig instanceof IkeAuthPskConfig); - assertEquals(IkeSessionOptions.IKE_AUTH_METHOD_PSK, localConfig.mAuthMethod); - assertArrayEquals(PSK, ((IkeAuthPskConfig) localConfig).mPsk); - - IkeAuthConfig remoteConfig = sessionOptions.getRemoteAuthConfig(); - assertTrue(remoteConfig instanceof IkeAuthPskConfig); - assertEquals(IkeSessionOptions.IKE_AUTH_METHOD_PSK, remoteConfig.mAuthMethod); - assertArrayEquals(PSK, ((IkeAuthPskConfig) remoteConfig).mPsk); - } - - @Test - public void testBuildWithEap() throws Exception { - EapSessionConfig eapConfig = mock(EapSessionConfig.class); - - IkeSessionOptions sessionOptions = - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(mIkeSaProposal) - .setLocalIdentification(mLocalIdentification) - .setRemoteIdentification(mRemoteIdentification) - .setAuthEap(mMockServerCaCert, eapConfig) - .build(); - - verifyIkeSessionOptionsCommon(sessionOptions); - - IkeAuthConfig localConfig = sessionOptions.getLocalAuthConfig(); - assertTrue(localConfig instanceof IkeAuthEapConfig); - assertEquals(IkeSessionOptions.IKE_AUTH_METHOD_EAP, localConfig.mAuthMethod); - assertEquals(eapConfig, ((IkeAuthEapConfig) localConfig).mEapConfig); - - IkeAuthConfig remoteConfig = sessionOptions.getRemoteAuthConfig(); - assertTrue(remoteConfig instanceof IkeAuthDigitalSignRemoteConfig); - assertEquals(IkeSessionOptions.IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, remoteConfig.mAuthMethod); - assertEquals( - mMockServerCaCert, - ((IkeAuthDigitalSignRemoteConfig) remoteConfig).mTrustAnchor.getTrustedCert()); - } - - @Test - public void testBuildWithDigitalSignatureAuth() throws Exception { - IkeSessionOptions sessionOptions = - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(mIkeSaProposal) - .setLocalIdentification(mLocalIdentification) - .setRemoteIdentification(mRemoteIdentification) - .setAuthDigitalSignature( - mMockServerCaCert, mMockClientEndCert, mMockRsaPrivateKey) - .build(); - - verifyIkeSessionOptionsCommon(sessionOptions); - - IkeAuthConfig localConfig = sessionOptions.getLocalAuthConfig(); - assertTrue(localConfig instanceof IkeAuthDigitalSignLocalConfig); - - IkeAuthDigitalSignLocalConfig localAuthConfig = (IkeAuthDigitalSignLocalConfig) localConfig; - assertEquals( - IkeSessionOptions.IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, localAuthConfig.mAuthMethod); - assertEquals(mMockClientEndCert, localAuthConfig.mEndCert); - assertTrue(localAuthConfig.mIntermediateCerts.isEmpty()); - assertEquals(mMockRsaPrivateKey, localAuthConfig.mPrivateKey); - - IkeAuthConfig remoteConfig = sessionOptions.getRemoteAuthConfig(); - assertTrue(remoteConfig instanceof IkeAuthDigitalSignRemoteConfig); - assertEquals(IkeSessionOptions.IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, remoteConfig.mAuthMethod); - assertEquals( - mMockServerCaCert, - ((IkeAuthDigitalSignRemoteConfig) remoteConfig).mTrustAnchor.getTrustedCert()); - } - - @Test - public void testBuildWithDsaDigitalSignatureAuth() throws Exception { - try { - IkeSessionOptions sessionOptions = - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(mIkeSaProposal) - .setLocalIdentification(mLocalIdentification) - .setRemoteIdentification(mRemoteIdentification) - .setAuthDigitalSignature( - mMockServerCaCert, - mMockClientEndCert, - mock(DSAPrivateKey.class)) - .build(); - fail("Expected to fail because DSA is not supported"); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildWithoutSaProposal() throws Exception { - try { - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .build(); - fail("Expected to fail due to absence of SA proposal."); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testBuildWithoutLocalId() throws Exception { - try { - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(mIkeSaProposal) - .setRemoteIdentification(mRemoteIdentification) - .setAuthPsk(PSK) - .build(); - fail("Expected to fail because local identification is not set."); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testBuildWithoutSetAuth() throws Exception { - try { - new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_IPV4_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(mIkeSaProposal) - .setLocalIdentification(mLocalIdentification) - .setRemoteIdentification(mRemoteIdentification) - .build(); - fail("Expected to fail because authentiction method is not set."); - } catch (IllegalArgumentException expected) { - } - } -} diff --git a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionTest.java b/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionTest.java deleted file mode 100644 index 8bf1281d..00000000 --- a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - -import android.content.Context; -import android.net.IpSecManager; -import android.os.Looper; -import android.os.test.TestLooper; -import android.util.Log; - -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachineTest; -import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils; - -import libcore.net.InetAddressUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.net.Inet4Address; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -public final class IkeSessionTest { - private static final int TIMEOUT_MS = 500; - - private static final Inet4Address LOCAL_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private static final Inet4Address REMOTE_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("127.0.0.1")); - - private MockIpSecTestUtils mMockIpSecTestUtils; - private IpSecManager mIpSecManager; - private Context mContext; - - private IkeSessionOptions mIkeSessionOptions; - private ChildSessionOptions mMockChildSessionOptions; - private Executor mUserCbExecutor; - private IkeSessionCallback mMockIkeSessionCb; - private ChildSessionCallback mMockChildSessionCb; - - @Before - public void setUp() throws Exception { - if (Looper.myLooper() == null) Looper.prepare(); - - mMockIpSecTestUtils = MockIpSecTestUtils.setUpMockIpSec(); - mIpSecManager = mMockIpSecTestUtils.getIpSecManager(); - mContext = mMockIpSecTestUtils.getContext(); - - mIkeSessionOptions = buildIkeSessionOptions(); - mMockChildSessionOptions = mock(ChildSessionOptions.class); - mUserCbExecutor = (r) -> r.run(); // Inline executor for testing purposes. - mMockIkeSessionCb = mock(IkeSessionCallback.class); - mMockChildSessionCb = mock(ChildSessionCallback.class); - } - - private IkeSessionOptions buildIkeSessionOptions() throws Exception { - return new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_ADDRESS) - .setUdpEncapsulationSocket(mIpSecManager.openUdpEncapsulationSocket()) - .addSaProposal(IkeSessionStateMachineTest.buildSaProposal()) - .setLocalIdentification(new IkeIpv4AddrIdentification((Inet4Address) LOCAL_ADDRESS)) - .setRemoteIdentification( - new IkeIpv4AddrIdentification((Inet4Address) REMOTE_ADDRESS)) - .setAuthPsk(new byte[0] /* psk, unused */) - .build(); - } - - @Test - public void testConstructIkeSession() throws Exception { - IkeSession ikeSession = - new IkeSession( - mContext, - mIkeSessionOptions, - mMockChildSessionOptions, - mUserCbExecutor, - mMockIkeSessionCb, - mMockChildSessionCb); - assertNotNull(ikeSession.mIkeSessionStateMachine.getHandler().getLooper()); - } - - /** - * Test that when users construct IkeSessions from different threads, these IkeSessions will - * still be running on the same IKE worker thread. - */ - @Test - public void testConstructFromDifferentThreads() throws Exception { - final int numSession = 2; - IkeSession[] sessions = new IkeSession[numSession]; - - final CountDownLatch cntLatch = new CountDownLatch(2); - - for (int i = 0; i < numSession; i++) { - int index = i; - new Thread() { - @Override - public void run() { - try { - sessions[index] = - new IkeSession( - mContext, - mIkeSessionOptions, - mMockChildSessionOptions, - mUserCbExecutor, - mMockIkeSessionCb, - mMockChildSessionCb); - cntLatch.countDown(); - } catch (Exception e) { - Log.e("IkeSessionTest", "error encountered constructing IkeSession. ", e); - } - } - }.start(); - } - - assertTrue(cntLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - - // Verify that two sessions use the same looper. - assertEquals( - sessions[0].mIkeSessionStateMachine.getHandler().getLooper(), - sessions[1].mIkeSessionStateMachine.getHandler().getLooper()); - } - - @Test - public void testOpensIkeSession() throws Exception { - TestLooper testLooper = new TestLooper(); - IkeSession ikeSession = - new IkeSession( - testLooper.getLooper(), - mContext, - mIpSecManager, - mIkeSessionOptions, - mMockChildSessionOptions, - mUserCbExecutor, - mMockIkeSessionCb, - mMockChildSessionCb); - testLooper.dispatchAll(); - - assertTrue( - ikeSession.mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeInit); - } -} diff --git a/tests/iketests/src/java/android/net/ipsec/ike/TunnelModeChildSessionOptionsTest.java b/tests/iketests/src/java/android/net/ipsec/ike/TunnelModeChildSessionOptionsTest.java deleted file mode 100644 index b0f81dc2..00000000 --- a/tests/iketests/src/java/android/net/ipsec/ike/TunnelModeChildSessionOptionsTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2019 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.ipsec.ike; - -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_INET6; - -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_ADDRESS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_DHCP; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_DNS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_NETMASK; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_SUBNET; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_ADDRESS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_DNS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_SUBNET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import android.util.SparseArray; - -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; - -import libcore.net.InetAddressUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.net.Inet4Address; -import java.net.Inet6Address; - -public final class TunnelModeChildSessionOptionsTest { - private static final int NUM_TS = 1; - - private static final int IP4_PREFIX_LEN = 32; - private static final int IP6_PREFIX_LEN = 64; - - private static final int INVALID_ADDR_FAMILY = 5; - - private static final Inet4Address IPV4_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - private static final Inet6Address IPV6_ADDRESS = - (Inet6Address) (InetAddressUtils.parseNumericAddress("2001:db8::1")); - - private static final Inet4Address IPV4_DNS_SERVER = - (Inet4Address) (InetAddressUtils.parseNumericAddress("8.8.8.8")); - private static final Inet6Address IPV6_DNS_SERVER = - (Inet6Address) (InetAddressUtils.parseNumericAddress("2001:4860:4860::8888")); - - private static final Inet4Address IPV4_DHCP_SERVER = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private ChildSaProposal mSaProposal; - - @Before - public void setup() { - mSaProposal = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, - SaProposal.KEY_LEN_AES_128) - .build(); - } - - private void verifyCommon(TunnelModeChildSessionOptions childOptions) { - assertArrayEquals(new SaProposal[] {mSaProposal}, childOptions.getSaProposals()); - assertEquals(NUM_TS, childOptions.getLocalTrafficSelectors().length); - assertEquals(NUM_TS, childOptions.getRemoteTrafficSelectors().length); - assertFalse(childOptions.isTransportMode()); - } - - private void verifyAttrTypes( - SparseArray expectedAttrCntMap, TunnelModeChildSessionOptions childOptions) { - ConfigAttribute[] configAttributes = childOptions.getConfigurationRequests(); - - SparseArray<Integer> atrrCntMap = expectedAttrCntMap.clone(); - - for (int i = 0; i < configAttributes.length; i++) { - int attType = configAttributes[i].attributeType; - assertNotNull(atrrCntMap.get(attType)); - - atrrCntMap.put(attType, atrrCntMap.get(attType) - 1); - if (atrrCntMap.get(attType) == 0) atrrCntMap.remove(attType); - } - - assertEquals(0, atrrCntMap.size()); - } - - @Test - public void testBuildChildSessionOptionsWithoutConfigReq() { - TunnelModeChildSessionOptions childOptions = - new TunnelModeChildSessionOptions.Builder().addSaProposal(mSaProposal).build(); - - verifyCommon(childOptions); - assertEquals(0, childOptions.getConfigurationRequests().length); - } - - @Test - public void testBuildChildSessionOptionsWithAddressReq() { - TunnelModeChildSessionOptions childOptions = - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalAddressRequest(AF_INET, 1) - .addInternalAddressRequest(AF_INET6, 2) - .addInternalAddressRequest(IPV4_ADDRESS, IP4_PREFIX_LEN) - .addInternalAddressRequest(IPV6_ADDRESS, IP6_PREFIX_LEN) - .build(); - - verifyCommon(childOptions); - - SparseArray<Integer> expectedAttrCntMap = new SparseArray<>(); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, 2); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP6_ADDRESS, 3); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP4_NETMASK, 1); - - verifyAttrTypes(expectedAttrCntMap, childOptions); - } - - @Test - public void testBuildChildSessionOptionsWithInvalidAddressReq() { - try { - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalAddressRequest(IPV4_ADDRESS, 31) - .build(); - fail("Expected to fail due to invalid IPv4 prefix length."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildChildSessionOptionsWithDnsServerReq() { - TunnelModeChildSessionOptions childOptions = - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalDnsServerRequest(AF_INET, 1) - .addInternalDnsServerRequest(AF_INET6, 1) - .addInternalDnsServerRequest(IPV4_DNS_SERVER) - .addInternalDnsServerRequest(IPV6_DNS_SERVER) - .build(); - - verifyCommon(childOptions); - - SparseArray<Integer> expectedAttrCntMap = new SparseArray<>(); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP4_DNS, 2); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP6_DNS, 2); - - verifyAttrTypes(expectedAttrCntMap, childOptions); - } - - @Test - public void testBuildChildSessionOptionsWithSubnetReq() { - TunnelModeChildSessionOptions childOptions = - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalSubnetRequest(AF_INET, 1) - .addInternalSubnetRequest(AF_INET6, 1) - .build(); - - verifyCommon(childOptions); - - SparseArray<Integer> expectedAttrCntMap = new SparseArray<>(); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP4_SUBNET, 1); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP6_SUBNET, 1); - - verifyAttrTypes(expectedAttrCntMap, childOptions); - } - - @Test - public void testBuildChildSessionOptionsWithDhcpServerReq() { - TunnelModeChildSessionOptions childOptions = - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalDhcpServerRequest(AF_INET, 3) - .addInternalDhcpServerRequest(IPV4_DHCP_SERVER) - .build(); - - verifyCommon(childOptions); - - SparseArray<Integer> expectedAttrCntMap = new SparseArray<>(); - expectedAttrCntMap.put(CONFIG_ATTR_INTERNAL_IP4_DHCP, 4); - - verifyAttrTypes(expectedAttrCntMap, childOptions); - } - - @Test - public void testBuildChildSessionOptionsWithDhcp6SeverReq() { - try { - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalDhcpServerRequest(AF_INET6, 3) - .build(); - fail("Expected to fail because DHCP6 is not supported."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildChildSessionOptionsWithInvalidDhcpReq() { - try { - new TunnelModeChildSessionOptions.Builder() - .addSaProposal(mSaProposal) - .addInternalDhcpServerRequest(INVALID_ADDR_FAMILY, 3) - .build(); - fail("Expected to fail due to invalid address family value"); - } catch (IllegalArgumentException expected) { - - } - } -} - diff --git a/tests/iketests/src/java/android/net/ipsec/ike/exceptions/IkeProtocolExceptionTest.java b/tests/iketests/src/java/android/net/ipsec/ike/exceptions/IkeProtocolExceptionTest.java deleted file mode 100644 index 8c3b16da..00000000 --- a/tests/iketests/src/java/android/net/ipsec/ike/exceptions/IkeProtocolExceptionTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2018 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.ipsec.ike.exceptions; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; - -import org.junit.Test; - -import java.util.LinkedList; -import java.util.List; - -public final class IkeProtocolExceptionTest { - @Test - public void buildNotifyPayloadWithData() throws Exception { - List<Integer> unsupportedTypes = new LinkedList<>(); - unsupportedTypes.add(55); // 0x37 in hex - unsupportedTypes.add(56); - unsupportedTypes.add(57); - UnsupportedCriticalPayloadException exception = - new UnsupportedCriticalPayloadException(unsupportedTypes); - - IkeNotifyPayload payload = exception.buildNotifyPayload(); - assertEquals(ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, payload.notifyType); - assertArrayEquals(new byte[] {(byte) 0x37}, payload.notifyData); - } - - @Test - public void buildNotifyPayloadWithoutData() throws Exception { - NoValidProposalChosenException exception = - new NoValidProposalChosenException("IkeProtocolExceptionTest"); - - IkeNotifyPayload payload = exception.buildNotifyPayload(); - assertEquals(ERROR_TYPE_NO_PROPOSAL_CHOSEN, payload.notifyType); - assertArrayEquals(new byte[0], payload.notifyData); - } -} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/ChildSessionStateMachineTest.java b/tests/iketests/src/java/com/android/ike/ikev2/ChildSessionStateMachineTest.java new file mode 100644 index 00000000..1d760b7e --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/ChildSessionStateMachineTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.test.TestLooper; + +import com.android.ike.ikev2.IkeSessionStateMachine.IChildSessionCallback; +import com.android.ike.ikev2.SaRecord.ChildSaRecord; +import com.android.ike.ikev2.SaRecord.ISaRecordHelper; +import com.android.ike.ikev2.SaRecord.SaRecordHelper; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.message.IkePayload; +import com.android.ike.ikev2.message.TestUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; + +public final class ChildSessionStateMachineTest { + private static final String IKE_AUTH_REQ_SA_PAYLOAD = + "2c00002c00000028010304032ad4c0a20300000c0100000c800e0080" + + "03000008030000020000000805000000"; + private static final String IKE_AUTH_RESP_SA_PAYLOAD = + "2c00002c0000002801030403cae7019f0300000c0100000c800e0080" + + "03000008030000020000000805000000"; + + private static final String CURRENT_CHILD_SA_SPI_IN = "2ad4c0a2"; + private static final String CURRENT_CHILD_SA_SPI_OUT = "cae7019f"; + + private TestLooper mLooper; + private ChildSessionStateMachine mChildSessionStateMachine; + + private List<IkePayload> mAuthReqSaNegoPayloads = new LinkedList<>(); + private List<IkePayload> mAuthRespSaNegoPayloads = new LinkedList<>(); + + private ChildSaRecord mSpyCurrentChildSaRecord; + + private ISaRecordHelper mMockSaRecordHelper; + private IChildSessionCallback mMockChildSessionCallback; + private ChildSessionOptions mChildSessionOptions; + + public ChildSessionStateMachineTest() { + mMockSaRecordHelper = mock(SaRecord.ISaRecordHelper.class); + mMockChildSessionCallback = mock(IChildSessionCallback.class); + + mChildSessionOptions = new ChildSessionOptions(); + } + + @Before + public void setup() throws Exception { + // Setup thread and looper + mLooper = new TestLooper(); + mChildSessionStateMachine = + new ChildSessionStateMachine( + "ChildSessionStateMachine", mLooper.getLooper(), mChildSessionOptions); + mChildSessionStateMachine.setDbg(true); + SaRecord.setSaRecordHelper(mMockSaRecordHelper); + + setUpPayloadLists(); + setUpChildSaRecords(); + + mChildSessionStateMachine.start(); + } + + private void setUpPayloadLists() throws IkeException { + mAuthReqSaNegoPayloads.add( + TestUtils.hexStringToIkePayload( + IkePayload.PAYLOAD_TYPE_SA, false, IKE_AUTH_REQ_SA_PAYLOAD)); + mAuthRespSaNegoPayloads.add( + TestUtils.hexStringToIkePayload( + IkePayload.PAYLOAD_TYPE_SA, true, IKE_AUTH_RESP_SA_PAYLOAD)); + // TODO: Build and add Traffic Selector Payloads to two payload lists. + } + + private void setUpChildSaRecords() { + mSpyCurrentChildSaRecord = + spy(makeDummyChildSaRecord(CURRENT_CHILD_SA_SPI_IN, CURRENT_CHILD_SA_SPI_OUT)); + } + + private ChildSaRecord makeDummyChildSaRecord(String inboundSpiHex, String outboundSpiHex) { + byte[] spiInBytes = TestUtils.hexStringToByteArray(CURRENT_CHILD_SA_SPI_IN); + int spiIn = ByteBuffer.wrap(spiInBytes).getInt(); + + byte[] spiOutBytes = TestUtils.hexStringToByteArray(CURRENT_CHILD_SA_SPI_OUT); + int spiOut = ByteBuffer.wrap(spiOutBytes).getInt(); + + return new ChildSaRecord(spiIn, spiOut, null, null); + } + + @After + public void tearDown() { + mChildSessionStateMachine.quit(); + mChildSessionStateMachine.setDbg(false); + + SaRecord.setSaRecordHelper(new SaRecordHelper()); + } + + @Test + public void testCreateFirstChild() throws Exception { + when(mMockSaRecordHelper.makeChildSaRecord(any(), any())) + .thenReturn(mSpyCurrentChildSaRecord); + mChildSessionStateMachine.handleFirstChildExchange( + mAuthReqSaNegoPayloads, mAuthRespSaNegoPayloads, mMockChildSessionCallback); + + mLooper.dispatchAll(); + verify(mMockChildSessionCallback) + .onCreateChildSa(mSpyCurrentChildSaRecord.outboundSpi, mChildSessionStateMachine); + assertTrue( + mChildSessionStateMachine.getCurrentState() + instanceof ChildSessionStateMachine.Idle); + assertEquals(mSpyCurrentChildSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord); + } +} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionOptionsTest.java b/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionOptionsTest.java new file mode 100644 index 00000000..bdaf135a --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionOptionsTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.net.IpSecManager; +import android.net.IpSecManager.UdpEncapsulationSocket; + +import androidx.test.InstrumentationRegistry; + +import libcore.net.InetAddressUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.net.Inet4Address; + +public final class IkeSessionOptionsTest { + private static final Inet4Address IPV4_ADDRESS = + (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); + + private UdpEncapsulationSocket mUdpEncapSocket; + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + IpSecManager ipSecManager = (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE); + mUdpEncapSocket = ipSecManager.openUdpEncapsulationSocket(); + } + + @After + public void tearDown() throws Exception { + mUdpEncapSocket.close(); + } + + @Test + public void testBuild() throws Exception { + SaProposal saProposal = + SaProposal.Builder.newIkeSaProposalBuilder() + .addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.KEY_LEN_AES_128) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .build(); + + IkeSessionOptions sessionOptions = + new IkeSessionOptions.Builder(IPV4_ADDRESS, mUdpEncapSocket) + .addSaProposal(saProposal) + .build(); + + assertEquals(IPV4_ADDRESS, sessionOptions.getServerAddress()); + assertEquals(mUdpEncapSocket, sessionOptions.getUdpEncapsulationSocket()); + assertArrayEquals(new SaProposal[] {saProposal}, sessionOptions.getSaProposals()); + assertFalse(sessionOptions.isIkeFragmentationSupported()); + } + + @Test + public void testBuildWithoutSaProposal() throws Exception { + try { + new IkeSessionOptions.Builder(IPV4_ADDRESS, mUdpEncapSocket).build(); + fail("Expected to fail due to absence of SA proposal."); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testBuildWithChildSaProposal() throws Exception { + SaProposal saProposal = + SaProposal.Builder.newChildSaProposalBuilder(true) + .addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.KEY_LEN_AES_128) + .build(); + try { + new IkeSessionOptions.Builder(IPV4_ADDRESS, mUdpEncapSocket) + .addSaProposal(saProposal) + .build(); + fail("Expected to fail due to wrong type of SA proposal."); + } catch (IllegalArgumentException expected) { + } + } +} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java new file mode 100644 index 00000000..1dcf5b82 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.IpSecManager; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.os.Looper; +import android.os.test.TestLooper; + +import androidx.test.InstrumentationRegistry; + +import com.android.ike.ikev2.ChildSessionStateMachineFactory.ChildSessionFactoryHelper; +import com.android.ike.ikev2.ChildSessionStateMachineFactory.IChildSessionFactoryHelper; +import com.android.ike.ikev2.IkeSessionStateMachine.ReceivedIkePacket; +import com.android.ike.ikev2.SaRecord.ISaRecordHelper; +import com.android.ike.ikev2.SaRecord.IkeSaRecord; +import com.android.ike.ikev2.SaRecord.SaRecordHelper; +import com.android.ike.ikev2.message.IkeHeader; +import com.android.ike.ikev2.message.IkeMessage; +import com.android.ike.ikev2.message.IkeMessage.IIkeMessageHelper; +import com.android.ike.ikev2.message.IkeMessage.IkeMessageHelper; +import com.android.ike.ikev2.message.IkePayload; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.net.InetAddress; +import java.util.LinkedList; +import java.util.List; + +public final class IkeSessionStateMachineTest { + + private static final String SERVER_ADDRESS = "192.0.2.100"; + + private UdpEncapsulationSocket mUdpEncapSocket; + + private TestLooper mLooper; + private IkeSessionStateMachine mIkeSessionStateMachine; + + private IkeSessionOptions mIkeSessionOptions; + private ChildSessionOptions mChildSessionOptions; + + private IIkeMessageHelper mMockIkeMessageHelper; + private ISaRecordHelper mMockSaRecordHelper; + + private ChildSessionStateMachine mMockChildSessionStateMachine; + private IChildSessionFactoryHelper mMockChildSessionFactoryHelper; + + private IkeSaRecord mSpyCurrentIkeSaRecord; + private IkeSaRecord mSpyLocalInitIkeSaRecord; + private IkeSaRecord mSpyRemoteInitIkeSaRecord; + + private ArgumentCaptor<IkeMessage> mIkeMessageCaptor = + ArgumentCaptor.forClass(IkeMessage.class); + + private ReceivedIkePacket makeDummyUnencryptedReceivedIkePacket(int packetType) + throws Exception { + IkeMessage dummyIkeMessage = makeDummyIkeMessageForTest(0, 0, false, false); + byte[] dummyIkePacketBytes = new byte[0]; + + when(mMockIkeMessageHelper.decode(dummyIkeMessage.ikeHeader, dummyIkePacketBytes)) + .thenReturn(dummyIkeMessage); + when(mMockIkeMessageHelper.getMessageType(dummyIkeMessage)).thenReturn(packetType); + return new ReceivedIkePacket(dummyIkeMessage.ikeHeader, dummyIkePacketBytes); + } + + private ReceivedIkePacket makeDummyEncryptedReceivedIkePacket( + int packetType, IkeSaRecord ikeSaRecord) throws Exception { + boolean fromIkeInit = !ikeSaRecord.isLocalInit; + IkeMessage dummyIkeMessage = + makeDummyIkeMessageForTest( + ikeSaRecord.initiatorSpi, ikeSaRecord.responderSpi, fromIkeInit, true); + byte[] dummyIkePacketBytes = new byte[0]; + + when(mMockIkeMessageHelper.decode( + mIkeSessionOptions, + ikeSaRecord, + dummyIkeMessage.ikeHeader, + dummyIkePacketBytes)) + .thenReturn(dummyIkeMessage); + when(mMockIkeMessageHelper.getMessageType(dummyIkeMessage)).thenReturn(packetType); + return new ReceivedIkePacket(dummyIkeMessage.ikeHeader, dummyIkePacketBytes); + } + + private IkeMessage makeDummyIkeMessageForTest( + long initSpi, long respSpi, boolean fromikeInit, boolean isEncrypted) { + int firstPayloadType = + isEncrypted ? IkePayload.PAYLOAD_TYPE_SK : IkePayload.PAYLOAD_TYPE_NO_NEXT; + IkeHeader header = + new IkeHeader(initSpi, respSpi, firstPayloadType, 0, true, fromikeInit, 0); + return new IkeMessage(header, new LinkedList<IkePayload>()); + } + + private void verifyDecodeEncryptedMessage(IkeSaRecord record, ReceivedIkePacket rcvPacket) + throws Exception { + verify(mMockIkeMessageHelper) + .decode(mIkeSessionOptions, record, rcvPacket.ikeHeader, rcvPacket.ikePacketBytes); + } + + public IkeSessionStateMachineTest() { + mMockIkeMessageHelper = mock(IkeMessage.IIkeMessageHelper.class); + mMockSaRecordHelper = mock(SaRecord.ISaRecordHelper.class); + + mMockChildSessionStateMachine = mock(ChildSessionStateMachine.class); + mMockChildSessionFactoryHelper = mock(IChildSessionFactoryHelper.class); + + mSpyCurrentIkeSaRecord = spy(new IkeSaRecord(11, 12, true, null, null)); + mSpyLocalInitIkeSaRecord = spy(new IkeSaRecord(21, 22, true, null, null)); + mSpyRemoteInitIkeSaRecord = spy(new IkeSaRecord(31, 32, false, null, null)); + + when(mMockIkeMessageHelper.encode(any())).thenReturn(new byte[0]); + when(mMockIkeMessageHelper.encode(any(), any(), any())).thenReturn(new byte[0]); + when(mMockChildSessionFactoryHelper.makeChildSessionStateMachine(any(), any(), any())) + .thenReturn(mMockChildSessionStateMachine); + } + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + IpSecManager ipSecManager = (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE); + mUdpEncapSocket = ipSecManager.openUdpEncapsulationSocket(); + + mIkeSessionOptions = buildIkeSessionOptions(); + mChildSessionOptions = new ChildSessionOptions(); + + // Setup thread and looper + mLooper = new TestLooper(); + mIkeSessionStateMachine = + new IkeSessionStateMachine( + "IkeSessionStateMachine", + mLooper.getLooper(), + mIkeSessionOptions, + mChildSessionOptions); + mIkeSessionStateMachine.setDbg(true); + mIkeSessionStateMachine.start(); + + IkeMessage.setIkeMessageHelper(mMockIkeMessageHelper); + SaRecord.setSaRecordHelper(mMockSaRecordHelper); + ChildSessionStateMachineFactory.setChildSessionFactoryHelper( + mMockChildSessionFactoryHelper); + } + + @After + public void tearDown() throws Exception { + mIkeSessionStateMachine.quit(); + mIkeSessionStateMachine.setDbg(false); + mUdpEncapSocket.close(); + + IkeMessage.setIkeMessageHelper(new IkeMessageHelper()); + SaRecord.setSaRecordHelper(new SaRecordHelper()); + ChildSessionStateMachineFactory.setChildSessionFactoryHelper( + new ChildSessionFactoryHelper()); + } + + private IkeSessionOptions buildIkeSessionOptions() throws Exception { + SaProposal saProposal = + SaProposal.Builder.newIkeSaProposalBuilder() + .addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .build(); + + InetAddress serveAddress = InetAddress.getByName(SERVER_ADDRESS); + IkeSessionOptions sessionOptions = + new IkeSessionOptions.Builder(serveAddress, mUdpEncapSocket) + .addSaProposal(saProposal) + .build(); + return sessionOptions; + } + + private static boolean isIkePayloadExist( + List<IkePayload> payloadList, @IkePayload.PayloadType int payloadType) { + for (IkePayload payload : payloadList) { + if (payload.payloadType == payloadType) return true; + } + return false; + } + + @Test + public void testCreateIkeLocalIkeInit() throws Exception { + if (Looper.myLooper() == null) Looper.myLooper().prepare(); + // Mock IKE_INIT response. + ReceivedIkePacket dummyReceivedIkePacket = + makeDummyUnencryptedReceivedIkePacket(IkeMessage.MESSAGE_TYPE_IKE_INIT_RESP); + when(mMockSaRecordHelper.makeFirstIkeSaRecord(any(), any())) + .thenReturn(mSpyCurrentIkeSaRecord); + + mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyReceivedIkePacket); + + mLooper.dispatchAll(); + + // Validate outbound IKE INIT request + verify(mMockIkeMessageHelper).encode(mIkeMessageCaptor.capture()); + IkeMessage ikeInitReqMessage = mIkeMessageCaptor.getValue(); + + IkeHeader ikeHeader = ikeInitReqMessage.ikeHeader; + assertEquals(IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, ikeHeader.exchangeType); + assertFalse(ikeHeader.isResponseMsg); + assertTrue(ikeHeader.fromIkeInitiator); + + List<IkePayload> payloadList = ikeInitReqMessage.ikePayloadList; + assertTrue(isIkePayloadExist(payloadList, IkePayload.PAYLOAD_TYPE_SA)); + assertTrue(isIkePayloadExist(payloadList, IkePayload.PAYLOAD_TYPE_KE)); + assertTrue(isIkePayloadExist(payloadList, IkePayload.PAYLOAD_TYPE_NONCE)); + + IkeSocket ikeSocket = mIkeSessionStateMachine.mIkeSocket; + assertNotNull(ikeSocket); + assertNotEquals( + -1 /*not found*/, ikeSocket.mSpiToIkeSession.indexOfValue(mIkeSessionStateMachine)); + + verify(mMockIkeMessageHelper) + .decode(dummyReceivedIkePacket.ikeHeader, dummyReceivedIkePacket.ikePacketBytes); + verify(mMockIkeMessageHelper).getMessageType(any()); + + assertTrue( + mIkeSessionStateMachine.getCurrentState() + instanceof IkeSessionStateMachine.CreateIkeLocalIkeAuth); + } + + private void mockIkeSetup() throws Exception { + if (Looper.myLooper() == null) Looper.myLooper().prepare(); + // Mock IKE_INIT response + ReceivedIkePacket dummyIkeInitRespReceivedPacket = + makeDummyUnencryptedReceivedIkePacket(IkeMessage.MESSAGE_TYPE_IKE_INIT_RESP); + when(mMockSaRecordHelper.makeFirstIkeSaRecord(any(), any())) + .thenReturn(mSpyCurrentIkeSaRecord); + + // Mock IKE_AUTH response + ReceivedIkePacket dummyIkeAuthRespReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_IKE_AUTH_RESP, mSpyCurrentIkeSaRecord); + + mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyIkeInitRespReceivedPacket); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyIkeAuthRespReceivedPacket); + } + + @Test + public void testCreateIkeLocalIkeAuth() throws Exception { + mockIkeSetup(); + + mLooper.dispatchAll(); + verify(mMockIkeMessageHelper).decode(any(), any(), any(), any()); + verify(mMockIkeMessageHelper, times(2)).getMessageType(any()); + verify(mMockChildSessionStateMachine).handleFirstChildExchange(any(), any(), any()); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + } + + @Test + public void testRekeyIkeLocal() throws Exception { + // Mock Rekey IKE response + ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_REKEY_IKE_RESP, mSpyCurrentIkeSaRecord); + when(mMockSaRecordHelper.makeNewIkeSaRecord(eq(mSpyCurrentIkeSaRecord), any(), any())) + .thenReturn(mSpyLocalInitIkeSaRecord); + // Mock Delete old IKE response; + ReceivedIkePacket dummyDeleteIkeRespReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_DELETE_IKE_RESP, mSpyCurrentIkeSaRecord); + + mockIkeSetup(); + + // Testing creating new IKE + mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); + // Testing deleting old IKE + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRespReceivedPacket); + + mLooper.dispatchAll(); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRespReceivedPacket); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRespReceivedPacket); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + assertEquals(mIkeSessionStateMachine.mCurrentIkeSaRecord, mSpyLocalInitIkeSaRecord); + } + + @Test + public void testRekeyIkeRemote() throws Exception { + // Mock Rekey IKE request + ReceivedIkePacket dummyRekeyIkeRequestReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_REKEY_IKE_REQ, mSpyCurrentIkeSaRecord); + when(mMockSaRecordHelper.makeNewIkeSaRecord(eq(mSpyCurrentIkeSaRecord), any(), any())) + .thenReturn(mSpyRemoteInitIkeSaRecord); + + // Mock Delete IKE request + ReceivedIkePacket dummyDeleteIkeRequestReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_DELETE_IKE_REQ, mSpyCurrentIkeSaRecord); + mockIkeSetup(); + + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRequestReceivedPacket); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRequestReceivedPacket); + + mLooper.dispatchAll(); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRequestReceivedPacket); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRequestReceivedPacket); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + assertEquals(mIkeSessionStateMachine.mCurrentIkeSaRecord, mSpyRemoteInitIkeSaRecord); + } + + @Test + public void testSimulRekey() throws Exception { + // Mock Rekey IKE response + ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_REKEY_IKE_RESP, mSpyCurrentIkeSaRecord); + when(mMockSaRecordHelper.makeNewIkeSaRecord(eq(mSpyCurrentIkeSaRecord), any(), any())) + .thenReturn(mSpyLocalInitIkeSaRecord); + + // Mock Rekey IKE request + ReceivedIkePacket dummyRekeyIkeRequestReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_REKEY_IKE_REQ, mSpyCurrentIkeSaRecord); + + when(mMockSaRecordHelper.makeNewIkeSaRecord(eq(mSpyCurrentIkeSaRecord), any(), any())) + .thenReturn(mSpyRemoteInitIkeSaRecord) + .thenReturn(mSpyLocalInitIkeSaRecord); + + // Mock nonce comparison + when(mSpyLocalInitIkeSaRecord.compareTo(mSpyRemoteInitIkeSaRecord)).thenReturn(1); + + // Mock Delete old IKE response; + ReceivedIkePacket dummyDeleteIkeRespReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_DELETE_IKE_RESP, mSpyCurrentIkeSaRecord); + + // Mock Delete IKE request on remotely initiated IKE SA + ReceivedIkePacket dummyDeleteIkeRequestReceivedPacket = + makeDummyEncryptedReceivedIkePacket( + IkeMessage.MESSAGE_TYPE_DELETE_IKE_REQ, mSpyRemoteInitIkeSaRecord); + + mockIkeSetup(); + + // Testing creating new IKE + mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRequestReceivedPacket); + + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); + // Testing deleting old IKE and losing new IKE + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRespReceivedPacket); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRequestReceivedPacket); + + mLooper.dispatchAll(); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRequestReceivedPacket); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRespReceivedPacket); + verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRespReceivedPacket); + verifyDecodeEncryptedMessage( + mSpyRemoteInitIkeSaRecord, dummyDeleteIkeRequestReceivedPacket); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + assertEquals(mIkeSessionStateMachine.mCurrentIkeSaRecord, mSpyLocalInitIkeSaRecord); + } +} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java b/tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java index 1b8761f7..5f15f554 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike; +package com.android.ike.ikev2; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -39,9 +39,9 @@ import android.util.LongSparseArray; import androidx.test.InstrumentationRegistry; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.IkeSocket.PacketReceiver; -import com.android.internal.net.ipsec.ike.message.IkeHeader; +import com.android.ike.ikev2.IkeSocket.PacketReceiver; +import com.android.ike.ikev2.message.IkeHeader; +import com.android.ike.ikev2.message.TestUtils; import org.junit.After; import org.junit.Before; @@ -141,35 +141,28 @@ public final class IkeSocketTest { @Test public void testGetAndCloseIkeSocket() throws Exception { - // Must be prepared here; AndroidJUnitRunner runs tests on different threads from the - // setUp() call. Since the new Handler() call is run in getIkeSocket, the Looper must be - // prepared here. - if (Looper.myLooper() == null) Looper.prepare(); + if (Looper.myLooper() == null) Looper.myLooper().prepare(); - IkeSessionStateMachine mMockIkeSessionOne = mock(IkeSessionStateMachine.class); - IkeSessionStateMachine mMockIkeSessionTwo = mock(IkeSessionStateMachine.class); + IkeSocket ikeSocketOne = IkeSocket.getIkeSocket(mClientUdpEncapSocket); + assertEquals(1, ikeSocketOne.mRefCount); - IkeSocket ikeSocketOne = IkeSocket.getIkeSocket(mClientUdpEncapSocket, mMockIkeSessionOne); - assertEquals(1, ikeSocketOne.mAliveIkeSessions.size()); - - IkeSocket ikeSocketTwo = IkeSocket.getIkeSocket(mClientUdpEncapSocket, mMockIkeSessionTwo); + IkeSocket ikeSocketTwo = IkeSocket.getIkeSocket(mClientUdpEncapSocket); assertEquals(ikeSocketOne, ikeSocketTwo); - assertEquals(2, ikeSocketTwo.mAliveIkeSessions.size()); + assertEquals(2, ikeSocketTwo.mRefCount); - ikeSocketOne.releaseReference(mMockIkeSessionOne); - assertEquals(1, ikeSocketOne.mAliveIkeSessions.size()); + ikeSocketOne.releaseReference(); + assertEquals(1, ikeSocketOne.mRefCount); - ikeSocketTwo.releaseReference(mMockIkeSessionTwo); - assertEquals(0, ikeSocketTwo.mAliveIkeSessions.size()); + ikeSocketTwo.releaseReference(); + assertEquals(0, ikeSocketTwo.mRefCount); } @Test public void testSendIkePacket() throws Exception { - if (Looper.myLooper() == null) Looper.prepare(); + if (Looper.myLooper() == null) Looper.myLooper().prepare(); // Send IKE packet - IkeSocket ikeSocket = - IkeSocket.getIkeSocket(mClientUdpEncapSocket, mMockIkeSessionStateMachine); + IkeSocket ikeSocket = IkeSocket.getIkeSocket(mClientUdpEncapSocket); ikeSocket.sendIkePacket(mDataOne, mLocalAddress); byte[] receivedData = receive(mDummyRemoteServerFd); @@ -181,7 +174,7 @@ public final class IkeSocketTest { assertArrayEquals(expectedBuffer.array(), receivedData); - ikeSocket.releaseReference(mMockIkeSessionStateMachine); + ikeSocket.releaseReference(); } @Test @@ -199,9 +192,7 @@ public final class IkeSocketTest { () -> { try { socketReceiver.setIkeSocket( - IkeSocket.getIkeSocket( - mClientUdpEncapSocket, - mMockIkeSessionStateMachine)); + IkeSocket.getIkeSocket(mClientUdpEncapSocket)); createLatch.countDown(); Log.d("IkeSocketTest", "IkeSocket created."); } catch (ErrnoException e) { @@ -238,7 +229,7 @@ public final class IkeSocketTest { .getHandler() .post( () -> { - ikeSocket.releaseReference(mMockIkeSessionStateMachine); + ikeSocket.releaseReference(); closeLatch.countDown(); }); closeLatch.await(); diff --git a/tests/iketests/src/java/android/net/ipsec/ike/IkeTrafficSelectorTest.java b/tests/iketests/src/java/com/android/ike/ikev2/IkeTrafficSelectorTest.java index 65cf0566..1982e62f 100644 --- a/tests/iketests/src/java/android/net/ipsec/ike/IkeTrafficSelectorTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/IkeTrafficSelectorTest.java @@ -14,25 +14,19 @@ * limitations under the License. */ -package android.net.ipsec.ike; +package com.android.ike.ikev2; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.message.TestUtils; import libcore.net.InetAddressUtils; import org.junit.Test; import java.net.Inet4Address; -import java.net.Inet6Address; -import java.nio.ByteBuffer; public final class IkeTrafficSelectorTest { private static final String TS_IPV4_ONE_HEX_STRING = "070000100010fff0c0000264c0000365"; @@ -60,26 +54,6 @@ public final class IkeTrafficSelectorTest { private static final int PROTOCOL_ID_OFFSET = 1; private static final int TS_LENGTH_OFFSET = 2; - private IkeTrafficSelector mTsOne; - private IkeTrafficSelector mTsTwo; - - public IkeTrafficSelectorTest() { - mTsOne = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - mTsTwo = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_TWO_START_PORT, - TS_TWO_END_PORT, - TS_TWO_START_ADDRESS, - TS_TWO_END_ADDRESS); - } - @Test public void testDecodeIkeTrafficSelectors() throws Exception { int numTs = 2; @@ -115,53 +89,6 @@ public final class IkeTrafficSelectorTest { } @Test - public void testBuildAndEncodeIkeTrafficSelector() throws Exception { - IkeTrafficSelector ts = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - - ByteBuffer byteBuffer = ByteBuffer.allocate(ts.selectorLength); - ts.encodeToByteBuffer(byteBuffer); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(TS_IPV4_ONE_HEX_STRING); - assertArrayEquals(expectedBytes, byteBuffer.array()); - } - - @Test - public void testEquals() throws Exception { - IkeTrafficSelector tsOneOther = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - - assertEquals(mTsOne, tsOneOther); - assertNotEquals(mTsOne, mTsTwo); - } - - @Test - public void testContains() throws Exception { - IkeTrafficSelector tsOneSubset = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT + 1, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - assertTrue(mTsOne.contains(tsOneSubset)); - assertFalse(tsOneSubset.contains(mTsOne)); - - assertTrue(mTsOne.contains(mTsOne)); - assertFalse(mTsOne.contains(mTsTwo)); - } - - @Test public void testDecodeIkeTrafficSelectorWithInvalidTsType() throws Exception { int numTs = 1; byte[] tsBytes = TestUtils.hexStringToByteArray(TS_IPV4_ONE_HEX_STRING); @@ -244,69 +171,4 @@ public final class IkeTrafficSelectorTest { } } - - @Test - public void testBuildIkeTrafficSelectorWithInvalidTsType() throws Exception { - try { - IkeTrafficSelector ts = - new IkeTrafficSelector( - 0, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - fail("Expected to fail due to unrecognized Traffic Selector type."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildIkeTrafficSelectorWithInvalidPortRange() throws Exception { - try { - IkeTrafficSelector ts = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_END_PORT, - TS_ONE_START_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - fail("Expected to fail due to invalid port range."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildIkeTrafficSelectorWithMismatchedAddressType() throws Exception { - Inet6Address inet6Address = - (Inet6Address) (InetAddressUtils.parseNumericAddress("0:2001:0:db8::1")); - try { - IkeTrafficSelector ts = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - inet6Address, - TS_ONE_END_ADDRESS); - fail("Expected to fail due to mismatched address format."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildIkeTrafficSelectorWithInvalidAddressRange() throws Exception { - try { - IkeTrafficSelector ts = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - TS_ONE_END_ADDRESS, - TS_ONE_START_ADDRESS); - fail("Expected to fail due to invalid address range."); - } catch (IllegalArgumentException e) { - } - } } diff --git a/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java b/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java index d4efb0c3..7f40d729 100644 --- a/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java @@ -14,10 +14,7 @@ * limitations under the License. */ -package android.net.ipsec.ike; - -import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128; -import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED; +package com.android.ike.ikev2; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -25,12 +22,13 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform; +import com.android.ike.ikev2.SaProposal.Builder; +import com.android.ike.ikev2.message.IkePayload; +import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform; +import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform; +import com.android.ike.ikev2.message.IkeSaPayload.IntegrityTransform; +import com.android.ike.ikev2.message.IkeSaPayload.PrfTransform; +import com.android.ike.ikev2.message.IkeSaPayload.Transform; import org.junit.Test; @@ -44,8 +42,7 @@ public final class SaProposalTest { private final DhGroupTransform mDhGroup1024Transform; public SaProposalTest() { - mEncryption3DesTransform = - new EncryptionTransform(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED); + mEncryption3DesTransform = new EncryptionTransform(SaProposal.ENCRYPTION_ALGORITHM_3DES); mEncryptionAesGcm8Transform = new EncryptionTransform( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128); @@ -61,10 +58,9 @@ public final class SaProposalTest { @Test public void testBuildIkeSaProposalWithNormalModeCipher() throws Exception { - IkeSaProposal proposal = - new IkeSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + Builder builder = Builder.newIkeSaProposalBuilder(); + SaProposal proposal = + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) @@ -85,9 +81,9 @@ public final class SaProposalTest { @Test public void testBuildIkeSaProposalWithCombinedModeCipher() throws Exception { - IkeSaProposal proposal = - new IkeSaProposal.Builder() - .addEncryptionAlgorithm( + Builder builder = Builder.newIkeSaProposalBuilder(); + SaProposal proposal = + builder.addEncryptionAlgorithm( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128) .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) @@ -106,49 +102,53 @@ public final class SaProposalTest { } @Test - public void testBuildChildSaProposalWithNormalCipher() throws Exception { - ChildSaProposal proposal = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + public void testBuildFirstChildSaProposalWithCombinedCipher() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(true); + SaProposal proposal = + builder.addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.KEY_LEN_AES_128) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) - .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) .build(); assertEquals(IkePayload.PROTOCOL_ID_ESP, proposal.getProtocolId()); assertArrayEquals( - new EncryptionTransform[] {mEncryption3DesTransform}, + new EncryptionTransform[] {mEncryptionAesGcm8Transform}, proposal.getEncryptionTransforms()); assertArrayEquals( new IntegrityTransform[] {mIntegrityNoneTransform}, proposal.getIntegrityTransforms()); - assertArrayEquals( - new DhGroupTransform[] {mDhGroup1024Transform}, proposal.getDhGroupTransforms()); + assertTrue(proposal.getPrfTransforms().length == 0); + assertTrue(proposal.getDhGroupTransforms().length == 0); } @Test - public void testGetCopyWithoutDhGroup() throws Exception { - ChildSaProposal proposal = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + public void testBuildAdditionalChildSaProposalWithNormalCipher() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + + SaProposal proposal = + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) .build(); - ChildSaProposal proposalWithoutDh = proposal.getCopyWithoutDhTransform(); + assertEquals(IkePayload.PROTOCOL_ID_ESP, proposal.getProtocolId()); assertArrayEquals( - proposal.getEncryptionTransforms(), proposalWithoutDh.getEncryptionTransforms()); + new EncryptionTransform[] {mEncryption3DesTransform}, + proposal.getEncryptionTransforms()); + assertArrayEquals( + new IntegrityTransform[] {mIntegrityNoneTransform}, + proposal.getIntegrityTransforms()); assertArrayEquals( - proposal.getIntegrityTransforms(), proposalWithoutDh.getIntegrityTransforms()); - assertTrue(proposal.getDhGroupTransforms().length == 1); - assertTrue(proposalWithoutDh.getDhGroupTransforms().length == 0); + new DhGroupTransform[] {mDhGroup1024Transform}, proposal.getDhGroupTransforms()); + assertTrue(proposal.getPrfTransforms().length == 0); } @Test public void testBuildEncryptAlgosWithNoAlgorithm() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); try { - new IkeSaProposal.Builder().build(); + builder.build(); fail("Expected to fail when no encryption algorithm is proposed."); } catch (IllegalArgumentException expected) { @@ -157,8 +157,9 @@ public final class SaProposalTest { @Test public void testBuildEncryptAlgosWithUnrecognizedAlgorithm() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); try { - new IkeSaProposal.Builder().addEncryptionAlgorithm(-1, KEY_LEN_UNUSED); + builder.addEncryptionAlgorithm(-1); fail("Expected to fail when unrecognized encryption algorithm is proposed."); } catch (IllegalArgumentException expected) { @@ -167,11 +168,10 @@ public final class SaProposalTest { @Test public void testBuildEncryptAlgosWithTwoModes() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); try { - new IkeSaProposal.Builder() - .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12); fail( "Expected to fail when " + "normal and combined-mode ciphers are proposed together."); @@ -182,24 +182,36 @@ public final class SaProposalTest { @Test public void testBuildIkeProposalWithoutPrf() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); try { - new IkeSaProposal.Builder() - .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) - .build(); + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES).build(); fail("Expected to fail when PRF is not provided in IKE SA proposal."); } catch (IllegalArgumentException expected) { } } + @Test + public void testBuildChildProposalWithPrf() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) + .build(); + + fail("Expected to fail when PRF is provided in Child SA proposal."); + } catch (IllegalArgumentException expected) { + + } + } + // Test throwing exception when building IKE SA Proposal with AEAD and not-none integrity // algorithm. @Test public void testBuildAeadWithIntegrityAlgo() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); try { - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128) + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) .build(); @@ -214,9 +226,9 @@ public final class SaProposalTest { // integrity algorithm. @Test public void testBuildIkeProposalNormalCipherWithoutIntegrityAlgo() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); try { - new IkeSaProposal.Builder() - .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) .build(); @@ -232,9 +244,9 @@ public final class SaProposalTest { // integrity algorithm. @Test public void testBuildIkeProposalNormalCipherWithNoneValueIntegrityAlgo() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); try { - new IkeSaProposal.Builder() - .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) @@ -250,9 +262,9 @@ public final class SaProposalTest { @Test public void testBuildIkeProposalWithoutDhGroup() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); try { - new IkeSaProposal.Builder() - .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) .build(); @@ -265,9 +277,9 @@ public final class SaProposalTest { @Test public void testBuildIkeProposalWithNoneValueDhGroup() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); try { - new IkeSaProposal.Builder() - .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED) + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) @@ -280,6 +292,24 @@ public final class SaProposalTest { } } + // Test throwing exception when building first Child SA Proposal with not-none-value DH Group. + @Test + public void testBuildFirstChildProposalWithNotNoneValueDhGroup() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(true); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .build(); + + fail( + "Expected to fail when" + + " not-none-value DH Group is proposed in first Child SA proposal."); + } catch (IllegalArgumentException expected) { + + } + } + @Test public void testIsTransformSelectedFrom() throws Exception { assertTrue(SaProposal.isTransformSelectedFrom(new Transform[0], new Transform[0])); diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacPrfTest.java b/tests/iketests/src/java/com/android/ike/ikev2/SaRecordTest.java index 717886f7..5c61105c 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacPrfTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/SaRecordTest.java @@ -14,32 +14,15 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.crypto; +package com.android.ike.ikev2; import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; -import android.net.ipsec.ike.SaProposal; +import com.android.ike.ikev2.message.TestUtils; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; - -import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; - -@RunWith(JUnit4.class) -public final class IkeMacPrfTest { - - private static final String PRF_KEY_HEX_STRING = "094787780EE466E2CB049FA327B43908BC57E485"; - private static final String DATA_TO_SIGN_HEX_STRING = "010000000a50500d"; - private static final String CALCULATED_MAC_HEX_STRING = - "D83B20CC6A0932B2A7CEF26E4020ABAAB64F0C6A"; +public final class SaRecordTest { private static final String IKE_INIT_SPI = "5F54BF6D8B48E6E1"; private static final String IKE_RESP_SPI = "909232B3D1EDCB5C"; @@ -100,58 +83,24 @@ public final class IkeMacPrfTest { private static final int FIRST_CHILD_AUTH_ALGO_KEY_LEN = 20; private static final int FIRST_CHILD_ENCR_ALGO_KEY_LEN = 16; - private IkeMacPrf mIkeHmacSha1Prf; - - @Before - public void setUp() throws Exception { - mIkeHmacSha1Prf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - } - - @Test - public void testsignBytes() throws Exception { - byte[] skpBytes = TestUtils.hexStringToByteArray(PRF_KEY_HEX_STRING); - byte[] dataBytes = TestUtils.hexStringToByteArray(DATA_TO_SIGN_HEX_STRING); - - byte[] calculatedBytes = mIkeHmacSha1Prf.signBytes(skpBytes, dataBytes); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(CALCULATED_MAC_HEX_STRING); - assertArrayEquals(expectedBytes, calculatedBytes); - } + private static final String PRF_HMAC_SHA1_ALGO_NAME = "HmacSHA1"; @Test - public void testGenerateSKeySeed() throws Exception { + public void testCalculateSKeySeed() throws Exception { byte[] nonceInit = TestUtils.hexStringToByteArray(IKE_NONCE_INIT_HEX_STRING); byte[] nonceResp = TestUtils.hexStringToByteArray(IKE_NONCE_RESP_HEX_STRING); byte[] sharedDhKey = TestUtils.hexStringToByteArray(IKE_SHARED_DH_KEY_HEX_STRING); byte[] calculatedSKeySeed = - mIkeHmacSha1Prf.generateSKeySeed(nonceInit, nonceResp, sharedDhKey); + SaRecord.generateSKeySeed( + PRF_HMAC_SHA1_ALGO_NAME, nonceInit, nonceResp, sharedDhKey); byte[] expectedSKeySeed = TestUtils.hexStringToByteArray(IKE_SKEYSEED_HEX_STRING); assertArrayEquals(expectedSKeySeed, calculatedSKeySeed); } @Test - public void testGenerateRekeyedSKeySeed() throws Exception { - byte[] nonceInit = TestUtils.hexStringToByteArray(IKE_NONCE_INIT_HEX_STRING); - byte[] nonceResp = TestUtils.hexStringToByteArray(IKE_NONCE_RESP_HEX_STRING); - byte[] sharedDhKey = TestUtils.hexStringToByteArray(IKE_SHARED_DH_KEY_HEX_STRING); - byte[] old_skd = TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING); - - byte[] calculatedSKeySeed = - mIkeHmacSha1Prf.generateRekeyedSKeySeed(old_skd, nonceInit, nonceResp, sharedDhKey); - - // Verify that the new sKeySeed is different. - // TODO: Find actual test vectors to test positive case. - byte[] oldSKeySeed = TestUtils.hexStringToByteArray(IKE_SKEYSEED_HEX_STRING); - assertFalse(Arrays.equals(oldSKeySeed, calculatedSKeySeed)); - } - - @Test - public void testGenerateKeyMatForIke() throws Exception { + public void testSignWithPrfPlusForIke() throws Exception { byte[] prfKey = TestUtils.hexStringToByteArray(IKE_SKEYSEED_HEX_STRING); byte[] prfData = TestUtils.hexStringToByteArray( @@ -165,21 +114,23 @@ public final class IkeMacPrfTest { + IKE_ENCR_ALGO_KEY_LEN * 2 + IKE_PRF_KEY_LEN * 2; - byte[] calculatedKeyMat = mIkeHmacSha1Prf.generateKeyMat(prfKey, prfData, keyMaterialLen); + byte[] calculatedKeyMat = + SaRecord.generateKeyMat(PRF_HMAC_SHA1_ALGO_NAME, prfKey, prfData, keyMaterialLen); byte[] expectedKeyMat = TestUtils.hexStringToByteArray(IKE_KEY_MAT); assertArrayEquals(expectedKeyMat, calculatedKeyMat); } @Test - public void testGenerateKeyMatForFirstChild() throws Exception { + public void testSignWithPrfPlusForFirstChild() throws Exception { byte[] prfKey = TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING); byte[] prfData = TestUtils.hexStringToByteArray( IKE_NONCE_INIT_HEX_STRING + IKE_NONCE_RESP_HEX_STRING); int keyMaterialLen = FIRST_CHILD_AUTH_ALGO_KEY_LEN * 2 + FIRST_CHILD_ENCR_ALGO_KEY_LEN * 2; - byte[] calculatedKeyMat = mIkeHmacSha1Prf.generateKeyMat(prfKey, prfData, keyMaterialLen); + byte[] calculatedKeyMat = + SaRecord.generateKeyMat(PRF_HMAC_SHA1_ALGO_NAME, prfKey, prfData, keyMaterialLen); byte[] expectedKeyMat = TestUtils.hexStringToByteArray(FIRST_CHILD_KEY_MAT); assertArrayEquals(expectedKeyMat, calculatedKeyMat); diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayloadTest.java new file mode 100644 index 00000000..a2c4d1f0 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayloadTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public final class IkeAuthDigitalSignPayloadTest { + + private static final String AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING = + "0e0000000f300d06092a864886f70d01010b05007b2f4456878b1344e803f094" + + "159a59361bc639071b69de41915452c478b77a46ce4a2c96ddc7ba2c18d08406" + + "50ce51c77124605423a2f75d8ed4b5a1ec5944c3396221a39e25def09abe5c9f" + + "6d9cd70e8f6254d4c835015256c9d6c26f0c6d31ac96a2ed802ccb16e48e7ff3" + + "daf736221b18c2a972130a69edb197a505a312882baed95d38a47bf6784533f2" + + "ffee671d742b5ae463216e46ef970ee6a335ffb3fc9c170a680fb802bb950cb0" + + "5601339be8869a73f8f85254d792b6e91697d8893ccd34b5fb6aad6268c4ab0f" + + "9ead7b3f8a4a255e1b2eabfa3da0de284f3954cf49271918dd2d2db95c8e7812" + + "9aea77e5761ac5683a0b5af300ceb52f5e8d8168"; + // TODO: Build a RSA_SHA1 signature and add tests for it. + + @Test + public void testDecodeGenericDigitalSignPayload() throws Exception { + byte[] inputPacket = + TestUtils.hexStringToByteArray(AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING); + IkeAuthPayload payload = IkeAuthPayload.getIkeAuthPayload(false, inputPacket); + + assertTrue(payload instanceof IkeAuthDigitalSignPayload); + IkeAuthDigitalSignPayload dsPayload = (IkeAuthDigitalSignPayload) payload; + assertEquals( + IkeAuthDigitalSignPayload.SIGNATURE_ALGO_RSA_SHA2_256, + dsPayload.signatureAlgoAndHash); + } +} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPayloadTest.java index 989b4689..7c630b76 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPayloadTest.java @@ -14,23 +14,17 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; - -import org.junit.Before; import org.junit.Test; +import javax.crypto.Mac; + public final class IkeAuthPayloadTest { private static final String PSK_AUTH_PAYLOAD_HEX_STRING = "02000000df7c038aefaaa32d3f44b228b52a332744dfb2c1"; @@ -84,15 +78,7 @@ public final class IkeAuthPayloadTest { private static final int AUTH_METHOD_POSITION = 0; - private IkeMacPrf mIkeHmacSha1Prf; - - @Before - public void setUp() throws Exception { - mIkeHmacSha1Prf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - } + private static final String PRF_HMAC_SHA1_ALGO_NAME = "HmacSHA1"; @Test public void testDecodeIkeAuthPayload() throws Exception { @@ -114,12 +100,26 @@ public final class IkeAuthPayloadTest { try { IkeAuthPayload payload = IkeAuthPayload.getIkeAuthPayload(false, inputPacket); fail("Expected Exception: authentication method is not supported"); - } catch (AuthenticationFailedException e) { + } catch (UnsupportedOperationException e) { + // TODO: Catch AuthenticationFailedException after it is implemented. } } @Test + public void testSignWithPrf() throws Exception { + Mac prfMac = Mac.getInstance(PRF_HMAC_SHA1_ALGO_NAME, IkeMessage.getSecurityProvider()); + byte[] skpBytes = TestUtils.hexStringToByteArray(PSK_SKP_HEX_STRING); + byte[] idBytes = TestUtils.hexStringToByteArray(PSK_ID_PAYLOAD_HEX_STRING); + byte[] calculatedBytes = IkeAuthPayload.signWithPrf(prfMac, skpBytes, idBytes); + + byte[] expectedBytes = + TestUtils.hexStringToByteArray(PSK_SIGNED_OCTETS_APPENDIX_HEX_STRING); + assertArrayEquals(expectedBytes, calculatedBytes); + } + + @Test public void testGetSignedOctets() throws Exception { + Mac prfMac = Mac.getInstance(PRF_HMAC_SHA1_ALGO_NAME, IkeMessage.getSecurityProvider()); byte[] skpBytes = TestUtils.hexStringToByteArray(PSK_SKP_HEX_STRING); byte[] idBytes = TestUtils.hexStringToByteArray(PSK_ID_PAYLOAD_HEX_STRING); byte[] ikeInitRequest = TestUtils.hexStringToByteArray(PSK_IKE_INIT_REQUEST_HEX_STRING); @@ -127,7 +127,7 @@ public final class IkeAuthPayloadTest { byte[] calculatedBytes = IkeAuthPayload.getSignedOctets( - ikeInitRequest, nonceResp, idBytes, mIkeHmacSha1Prf, skpBytes); + ikeInitRequest, nonceResp, idBytes, prfMac, skpBytes); byte[] expectedBytes = TestUtils.hexStringToByteArray(PSK_INIT_SIGNED_OCTETS); } } diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPskPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPskPayloadTest.java index cad60522..baa8059c 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthPskPayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPskPayloadTest.java @@ -14,25 +14,21 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import android.net.ipsec.ike.SaProposal; +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; - -import org.junit.Before; import org.junit.Test; import java.nio.ByteBuffer; import java.util.Arrays; +import javax.crypto.Mac; + public final class IkeAuthPskPayloadTest { private static final String PSK_AUTH_PAYLOAD_HEX_STRING = "2100001c02000000df7c038aefaaa32d3f44b228b52a332744dfb2c1"; @@ -66,6 +62,8 @@ public final class IkeAuthPskPayloadTest { private static final String PSK_HEX_STRING = "6A756E69706572313233"; private static final String PSK_SKP_HEX_STRING = "094787780EE466E2CB049FA327B43908BC57E485"; + private static final String PRF_HMAC_SHA1_ALGO_NAME = "HmacSHA1"; + private static final byte[] PSK = TestUtils.hexStringToByteArray(PSK_HEX_STRING); private static final byte[] IKE_INIT_REQUEST = TestUtils.hexStringToByteArray(PSK_IKE_INIT_REQUEST_HEX_STRING); @@ -76,21 +74,13 @@ public final class IkeAuthPskPayloadTest { private static final byte[] SIGNATURE = TestUtils.hexStringToByteArray(PSK_AUTH_PAYLOAD_SIGNATURE_HEX_STRING); - private IkeMacPrf mIkeHmacSha1Prf; - - @Before - public void setUp() throws Exception { - mIkeHmacSha1Prf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - } - @Test public void testBuildOutboundIkeAuthPskPayload() throws Exception { + Mac prfMac = Mac.getInstance(PRF_HMAC_SHA1_ALGO_NAME, IkeMessage.getSecurityProvider()); + IkeAuthPskPayload payload = new IkeAuthPskPayload( - PSK, IKE_INIT_REQUEST, NONCE, ID_PAYLOAD_BODY, mIkeHmacSha1Prf, PRF_KEY); + PSK, IKE_INIT_REQUEST, NONCE, ID_PAYLOAD_BODY, prfMac, PRF_KEY); assertEquals(IkeAuthPayload.AUTH_METHOD_PRE_SHARED_KEY, payload.authMethod); assertArrayEquals(SIGNATURE, payload.signature); @@ -122,21 +112,23 @@ public final class IkeAuthPskPayloadTest { @Test public void testVerifyReceivedSignature() throws Exception { + Mac prfMac = Mac.getInstance(PRF_HMAC_SHA1_ALGO_NAME, IkeMessage.getSecurityProvider()); IkeAuthPskPayload pskPayload = buildPskPayload(); pskPayload.verifyInboundSignature( - PSK, IKE_INIT_REQUEST, NONCE, ID_PAYLOAD_BODY, mIkeHmacSha1Prf, PRF_KEY); + PSK, IKE_INIT_REQUEST, NONCE, ID_PAYLOAD_BODY, prfMac, PRF_KEY); } @Test public void testVerifyReceivedSignatureFailure() throws Exception { + Mac prfMac = Mac.getInstance(PRF_HMAC_SHA1_ALGO_NAME, IkeMessage.getSecurityProvider()); IkeAuthPskPayload pskPayload = buildPskPayload(); byte[] nonce = Arrays.copyOf(NONCE, NONCE.length); nonce[0]++; try { pskPayload.verifyInboundSignature( - PSK, IKE_INIT_REQUEST, nonce, ID_PAYLOAD_BODY, mIkeHmacSha1Prf, PRF_KEY); + PSK, IKE_INIT_REQUEST, nonce, ID_PAYLOAD_BODY, prfMac, PRF_KEY); fail("Expected signature verification to have failed due to mismatched signatures."); } catch (AuthenticationFailedException expected) { } diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeCertX509CertPayloadTest.java index ef6ec289..fcf5f555 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeCertX509CertPayloadTest.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; import org.junit.Test; diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeDeletePayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeDeletePayloadTest.java new file mode 100644 index 00000000..1a7b9a29 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeDeletePayloadTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; + +import org.junit.Test; + +import java.nio.ByteBuffer; + +public final class IkeDeletePayloadTest { + private static final String DELETE_IKE_PAYLOAD_HEX_STRING = "0000000801000000"; + private static final String DELETE_CHILD_PAYLOAD_HEX_STRING = "0000000c030400012ad4c0a2"; + private static final String CHILD_SPI = "2ad4c0a2"; + + private static final int NUM_CHILD_SPI = 1; + + private static final int PROTOCOL_ID_OFFSET = 4; + private static final int SPI_SIZE_OFFSET = 5; + private static final int NUM_OF_SPI_OFFSET = 6; + + @Test + public void testDecodeDeleteIkePayload() throws Exception { + ByteBuffer inputBuffer = + ByteBuffer.wrap(TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING)); + + IkePayload payload = + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer) + .first; + + assertTrue(payload instanceof IkeDeletePayload); + + IkeDeletePayload deletePayload = (IkeDeletePayload) payload; + assertEquals(IkePayload.PROTOCOL_ID_IKE, deletePayload.protocolId); + assertEquals(IkePayload.SPI_LEN_NOT_INCLUDED, deletePayload.spiSize); + assertEquals(0, deletePayload.numSpi); + assertArrayEquals(new int[0], deletePayload.spisToDelete); + } + + @Test + public void testDecodeDeleteChildPayload() throws Exception { + ByteBuffer inputBuffer = + ByteBuffer.wrap(TestUtils.hexStringToByteArray(DELETE_CHILD_PAYLOAD_HEX_STRING)); + + IkePayload payload = + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer) + .first; + + assertTrue(payload instanceof IkeDeletePayload); + + IkeDeletePayload deletePayload = (IkeDeletePayload) payload; + assertEquals(IkePayload.PROTOCOL_ID_ESP, deletePayload.protocolId); + assertEquals(IkePayload.SPI_LEN_IPSEC, deletePayload.spiSize); + assertEquals(NUM_CHILD_SPI, deletePayload.numSpi); + + byte[] childSpiBytes = TestUtils.hexStringToByteArray(CHILD_SPI); + ByteBuffer buffer = ByteBuffer.wrap(childSpiBytes); + int expectedChildSpi = buffer.getInt(); + + assertArrayEquals(new int[] {expectedChildSpi}, deletePayload.spisToDelete); + } + + @Test + public void testDecodeWithInvalidProtocol() throws Exception { + byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); + deletePayloadBytes[PROTOCOL_ID_OFFSET] = -1; + ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); + + try { + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); + fail("Expected to fail due to unrecognized protocol ID."); + } catch (InvalidSyntaxException expected) { + + } + } + + @Test + public void testDecodeWithInvalidSpiSize() throws Exception { + byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); + deletePayloadBytes[SPI_SIZE_OFFSET] = IkePayload.SPI_LEN_IPSEC; + ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); + + try { + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); + fail("Expected to fail due to invalid SPI size in Delete IKE Payload."); + } catch (InvalidSyntaxException expected) { + + } + } + + @Test + public void testDecodeWithInvalidNumSpi() throws Exception { + byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); + deletePayloadBytes[NUM_OF_SPI_OFFSET] = 1; + ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); + + try { + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); + fail("Expected to fail because number of SPI is not zero in Delete IKE Payload."); + } catch (InvalidSyntaxException expected) { + + } + } + + @Test + public void testDecodeWithInvalidNumSpiAndSpiSize() throws Exception { + byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); + deletePayloadBytes[SPI_SIZE_OFFSET] = 1; + deletePayloadBytes[NUM_CHILD_SPI] = 4; + ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); + + try { + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); + fail("Expected to fail due to invalid SPI size in Delete IKE Payload."); + } catch (InvalidSyntaxException expected) { + + } + } +} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java new file mode 100644 index 00000000..fcb3eff6 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +import java.security.GeneralSecurityException; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +public final class IkeEncryptedPayloadBodyTest { + + private static final String IKE_AUTH_INIT_REQUEST_HEADER = + "5f54bf6d8b48e6e1909232b3d1edcb5c2e20230800000001000000ec"; + private static final String IKE_AUTH_INIT_REQUEST_SK_HEADER = "230000d0"; + private static final String IKE_AUTH_INIT_REQUEST_IV = "b9132b7bb9f658dfdc648e5017a6322a"; + private static final String IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA = + "030c316ce55f365760d46426ce5cfc78bd1ed9abff63eb9594c1bd58" + + "46de333ecd3ea2b705d18293b130395300ba92a351041345" + + "0a10525cea51b2753b4e92b081fd78d995659a98f742278f" + + "f9b8fd3e21554865c15c79a5134d66b2744966089e416c60" + + "a274e44a9a3f084eb02f3bdce1e7de9de8d9a62773ab563b" + + "9a69ba1db03c752acb6136452b8a86c41addb4210d68c423" + + "efed80e26edca5fa3fe5d0a5ca9375ce332c474b93fb1fa3" + + "59eb4e81"; + private static final String IKE_AUTH_INIT_REQUEST_CHECKSUM = "ae6e0f22abdad69ba8007d50"; + + private static final String IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA = + "2400000c010000000a50500d2700000c010000000a505050" + + "2100001c02000000df7c038aefaaa32d3f44b228b52a3327" + + "44dfb2c12c00002c00000028010304032ad4c0a20300000c" + + "0100000c800e008003000008030000020000000805000000" + + "2d00001801000000070000100000ffff00000000ffffffff" + + "2900001801000000070000100000ffff00000000ffffffff" + + "29000008000040000000000c0000400100000001"; + private static final String IKE_AUTH_INIT_REQUEST_PADDING = "0000000000000000000000"; + private static final int HMAC_SHA1_CHECKSUM_LEN = 12; + + private static final String ENCR_KEY_FROM_INIT_TO_RESP = "5cbfd33f75796c0188c4a3a546aec4a1"; + private static final String INTE_KEY_FROM_INIT_TO_RESP = + "554fbf5a05b7f511e05a30ce23d874db9ef55e51"; + + private static final String ENCR_ALGO_AES_CBC = "AES/CBC/NoPadding"; + private static final String INTE_ALGO_HMAC_SHA1 = "HmacSHA1"; + + private Cipher mAesCbcCipher; + private SecretKey mAesCbcKey; + private Mac mHmacSha1IntegrityMac; + + private byte[] mDataToPadAndEncrypt; + private byte[] mDataToAuthenticate; + private byte[] mEncryptedPaddedData; + private byte[] mIkeMessage; + + private byte[] mChecksum; + private byte[] mIv; + private byte[] mPadding; + + // TODO: Add tests for authenticating and decrypting received message. + @Before + public void setUp() throws Exception { + mDataToPadAndEncrypt = + TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA); + String hexStringToAuthenticate = + IKE_AUTH_INIT_REQUEST_HEADER + + IKE_AUTH_INIT_REQUEST_SK_HEADER + + IKE_AUTH_INIT_REQUEST_IV + + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA; + mDataToAuthenticate = TestUtils.hexStringToByteArray(hexStringToAuthenticate); + mEncryptedPaddedData = + TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA); + mIkeMessage = + TestUtils.hexStringToByteArray( + IKE_AUTH_INIT_REQUEST_HEADER + + IKE_AUTH_INIT_REQUEST_SK_HEADER + + IKE_AUTH_INIT_REQUEST_IV + + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA + + IKE_AUTH_INIT_REQUEST_CHECKSUM); + + mChecksum = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_CHECKSUM); + mIv = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_IV); + mPadding = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_PADDING); + + mAesCbcCipher = Cipher.getInstance(ENCR_ALGO_AES_CBC, IkeMessage.getSecurityProvider()); + byte[] encryptKeyBytes = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP); + mAesCbcKey = new SecretKeySpec(encryptKeyBytes, ENCR_ALGO_AES_CBC); + + mHmacSha1IntegrityMac = + Mac.getInstance(INTE_ALGO_HMAC_SHA1, IkeMessage.getSecurityProvider()); + byte[] integrityKeyBytes = TestUtils.hexStringToByteArray(INTE_KEY_FROM_INIT_TO_RESP); + SecretKeySpec integrityKey = new SecretKeySpec(integrityKeyBytes, INTE_ALGO_HMAC_SHA1); + mHmacSha1IntegrityMac.init(integrityKey); + } + + @Test + public void testCalculateChecksum() throws Exception { + byte[] calculatedChecksum = + IkeEncryptedPayloadBody.calculateChecksum( + mDataToAuthenticate, mHmacSha1IntegrityMac, HMAC_SHA1_CHECKSUM_LEN); + + assertArrayEquals(mChecksum, calculatedChecksum); + } + + @Test + public void testValidateChecksum() throws Exception { + IkeEncryptedPayloadBody.validateChecksumOrThrow( + mDataToAuthenticate, mHmacSha1IntegrityMac, mChecksum); + } + + @Test + public void testThrowForInvalidChecksum() throws Exception { + byte[] dataToAuthenticate = Arrays.copyOf(mDataToAuthenticate, mDataToAuthenticate.length); + dataToAuthenticate[0]++; + + try { + IkeEncryptedPayloadBody.validateChecksumOrThrow( + dataToAuthenticate, mHmacSha1IntegrityMac, mChecksum); + fail("Expected GeneralSecurityException due to mismatched checksum."); + } catch (GeneralSecurityException expected) { + } + } + + @Test + public void testCalculatePaddingPlaintextShorterThanBlockSize() throws Exception { + int blockSize = 16; + int plainTextLength = 15; + int expectedPadLength = 0; + + byte[] calculatedPadding = + IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize); + assertEquals(expectedPadLength, calculatedPadding.length); + } + + @Test + public void testCalculatePaddingPlaintextInBlockSize() throws Exception { + int blockSize = 16; + int plainTextLength = 16; + int expectedPadLength = 15; + + byte[] calculatedPadding = + IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize); + assertEquals(expectedPadLength, calculatedPadding.length); + } + + @Test + public void testCalculatePaddingPlaintextLongerThanBlockSize() throws Exception { + int blockSize = 16; + int plainTextLength = 17; + int expectedPadLength = 14; + + byte[] calculatedPadding = + IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize); + assertEquals(expectedPadLength, calculatedPadding.length); + } + + @Test + public void testEncrypt() throws Exception { + byte[] calculatedData = + IkeEncryptedPayloadBody.encrypt( + mDataToPadAndEncrypt, mAesCbcCipher, mAesCbcKey, mIv, mPadding); + + assertArrayEquals(mEncryptedPaddedData, calculatedData); + } + + @Test + public void testDecrypt() throws Exception { + byte[] calculatedPlainText = + IkeEncryptedPayloadBody.decrypt( + mEncryptedPaddedData, mAesCbcCipher, mAesCbcKey, mIv); + + assertArrayEquals(mDataToPadAndEncrypt, calculatedPlainText); + } + + @Test + public void testBuildAndEncodeOutboundIkeEncryptedPayloadBody() throws Exception { + IkeHeader ikeHeader = new IkeHeader(mIkeMessage); + + IkeEncryptedPayloadBody paylaodBody = + new IkeEncryptedPayloadBody( + ikeHeader, + IkePayload.PAYLOAD_TYPE_ID_INITIATOR, + mDataToPadAndEncrypt, + mHmacSha1IntegrityMac, + HMAC_SHA1_CHECKSUM_LEN, + mAesCbcCipher, + mAesCbcKey, + mIv, + mPadding); + + byte[] expectedEncodedData = + TestUtils.hexStringToByteArray( + IKE_AUTH_INIT_REQUEST_IV + + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA + + IKE_AUTH_INIT_REQUEST_CHECKSUM); + assertArrayEquals(expectedEncodedData, paylaodBody.encode()); + } + + @Test + public void testAuthenticateAndDecryptInboundIkeEncryptedPayloadBody() throws Exception { + IkeEncryptedPayloadBody paylaodBody = + new IkeEncryptedPayloadBody( + mIkeMessage, + mHmacSha1IntegrityMac, + HMAC_SHA1_CHECKSUM_LEN, + mAesCbcCipher, + mAesCbcKey); + + assertArrayEquals(mDataToPadAndEncrypt, paylaodBody.getUnencryptedData()); + } +} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeHeaderTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeHeaderTest.java index 4592815d..08b1612b 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeHeaderTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeHeaderTest.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.InvalidMajorVersionException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.InvalidMajorVersionException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import org.junit.Test; @@ -100,12 +100,15 @@ public final class IkeHeaderTest { inputPacket[VERSION_OFFSET] = (byte) 0x30; // Set Exchange type 0 inputPacket[EXCHANGE_TYPE_OFFSET] = (byte) 0x00; - - InvalidMajorVersionException exception = - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg( - inputPacket, InvalidMajorVersionException.class); - - assertEquals(3, exception.getMajorVerion()); + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage.decode(header, inputPacket); + fail( + "Expected InvalidMajorVersionException: major version is 3" + + "and exchange type is 0"); + } catch (InvalidMajorVersionException expected) { + assertEquals(3, expected.receivedMajorVersion); + } } @Test @@ -113,8 +116,12 @@ public final class IkeHeaderTest { byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); // Set Exchange type 0 inputPacket[EXCHANGE_TYPE_OFFSET] = (byte) 0x00; - - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg(inputPacket, InvalidSyntaxException.class); + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage.decode(header, inputPacket); + fail("Expected InvalidSyntaxException: exchange type is 0"); + } catch (InvalidSyntaxException expected) { + } } @Test @@ -122,8 +129,12 @@ public final class IkeHeaderTest { byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); // Set Exchange type 0 inputPacket[MESSAGE_LENGTH_OFFSET] = (byte) 0x01; - - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg(inputPacket, InvalidSyntaxException.class); + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage.decode(header, inputPacket); + fail("Expected InvalidSyntaxException: IKE message length."); + } catch (InvalidSyntaxException expected) { + } } @Test diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeIdPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeIdPayloadTest.java new file mode 100644 index 00000000..893857a8 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeIdPayloadTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.android.ike.ikev2.IkeIdentification; +import com.android.ike.ikev2.IkeIdentification.IkeIpv4AddrIdentification; +import com.android.ike.ikev2.IkeIdentification.IkeIpv6AddrIdentification; +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; + +import org.junit.Test; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +public final class IkeIdPayloadTest { + + private static final String IPV4_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING = + "2700000c01000000c0000264"; + private static final String IPV4_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING = "01000000c0000264"; + private static final String IPV4_ADDR_STRING = "192.0.2.100"; + + private static final String IPV6_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING = + "27000018050000000000200100000db80000000000000001"; + private static final String IPV6_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING = + "050000000000200100000db80000000000000001"; + private static final String IPV6_ADDR_STRING = "0:2001:0:db8::1"; + + private static final int ID_TYPE_OFFSET = 0; + + @Test + public void testDecodeIpv4AddrIdPayload() throws Exception { + byte[] inputPacket = + TestUtils.hexStringToByteArray(IPV4_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING); + IkeIdPayload payload = new IkeIdPayload(false, inputPacket, false); + + assertEquals(IkePayload.PAYLOAD_TYPE_ID_RESPONDER, payload.payloadType); + assertEquals(IkeIdentification.ID_TYPE_IPV4_ADDR, payload.ikeId.idType); + IkeIpv4AddrIdentification ikeId = (IkeIpv4AddrIdentification) payload.ikeId; + Inet4Address expectedAddr = (Inet4Address) Inet4Address.getByName(IPV4_ADDR_STRING); + assertEquals(expectedAddr, ikeId.ipv4Address); + } + + @Test + public void testDecodeIpv6AddrIdPayload() throws Exception { + byte[] inputPacket = + TestUtils.hexStringToByteArray(IPV6_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING); + IkeIdPayload payload = new IkeIdPayload(false, inputPacket, false); + + assertEquals(IkePayload.PAYLOAD_TYPE_ID_RESPONDER, payload.payloadType); + assertEquals(IkeIdentification.ID_TYPE_IPV6_ADDR, payload.ikeId.idType); + IkeIpv6AddrIdentification ikeId = (IkeIpv6AddrIdentification) payload.ikeId; + Inet6Address expectedAddr = (Inet6Address) Inet6Address.getByName(IPV6_ADDR_STRING); + assertEquals(expectedAddr, ikeId.ipv6Address); + } + + @Test + public void testDecodeUnsupportedIdType() throws Exception { + byte[] inputPacket = + TestUtils.hexStringToByteArray(IPV4_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING); + inputPacket[ID_TYPE_OFFSET] = 0; + + try { + new IkeIdPayload(false, inputPacket, true); + fail("Expected AuthenticationFailedException: ID Type is unsupported."); + } catch (AuthenticationFailedException expected) { + } + } + + @Test + public void testConstructAndEncodeIpv4AddrIdPayload() throws Exception { + Inet4Address ipv4Address = (Inet4Address) Inet4Address.getByName(IPV4_ADDR_STRING); + IkeIdPayload payload = new IkeIdPayload(false, new IkeIpv4AddrIdentification(ipv4Address)); + + ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); + payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_AUTH, inputBuffer); + + byte[] expectedBytes = + TestUtils.hexStringToByteArray(IPV4_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING); + assertArrayEquals(expectedBytes, inputBuffer.array()); + } + + @Test + public void testConstructAndEncodeIpv6AddrIdPayload() throws Exception { + Inet6Address ipv6Address = (Inet6Address) Inet6Address.getByName(IPV6_ADDR_STRING); + IkeIdPayload payload = new IkeIdPayload(false, new IkeIpv6AddrIdentification(ipv6Address)); + + ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); + payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_AUTH, inputBuffer); + + byte[] expectedBytes = + TestUtils.hexStringToByteArray(IPV6_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING); + assertArrayEquals(expectedBytes, inputBuffer.array()); + } +} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeKePayloadTest.java index f5046dca..1bb0b709 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeKePayloadTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -22,19 +22,15 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.net.ipsec.ike.SaProposal; +import com.android.ike.ikev2.IkeDhParams; +import com.android.ike.ikev2.SaProposal; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.utils.BigIntegerUtils; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.IkeDhParams; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.utils.BigIntegerUtils; - -import org.junit.Before; import org.junit.Test; import java.math.BigInteger; import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; import java.util.Arrays; import javax.crypto.spec.DHPrivateKeySpec; @@ -97,19 +93,6 @@ public final class IkeKePayloadTest { + "F408ED31B63C6E6D"; private static final String KEY_EXCHANGE_ALGORITHM = "DH"; - private DHPrivateKeySpec mPrivateKeySpec; - - @Before - public void setUp() throws Exception { - BigInteger primeValue = - BigIntegerUtils.unsignedHexStringToBigInteger(PRIME_1024_BIT_MODP_160_SUBGROUP); - BigInteger baseGenValue = - BigIntegerUtils.unsignedHexStringToBigInteger(GENERATOR_1024_BIT_MODP_160_SUBGROUP); - BigInteger privateKeyValue = - BigIntegerUtils.unsignedHexStringToBigInteger(PRIVATE_KEY_LOCAL); - mPrivateKeySpec = new DHPrivateKeySpec(privateKeyValue, primeValue, baseGenValue); - } - @Test public void testDecodeIkeKePayload() throws Exception { byte[] inputPacket = TestUtils.hexStringToByteArray(KE_PAYLOAD_RAW_PACKET); @@ -180,21 +163,19 @@ public final class IkeKePayloadTest { // recipient test in real Key Exchange process. But it is suitable for testing. @Test public void testGetSharedkey() throws Exception { + BigInteger primeValue = + BigIntegerUtils.unsignedHexStringToBigInteger(PRIME_1024_BIT_MODP_160_SUBGROUP); + BigInteger baseGenValue = + BigIntegerUtils.unsignedHexStringToBigInteger(GENERATOR_1024_BIT_MODP_160_SUBGROUP); + BigInteger privateKeyValue = + BigIntegerUtils.unsignedHexStringToBigInteger(PRIVATE_KEY_LOCAL); byte[] remotePublicKey = TestUtils.hexStringToByteArray(PUBLIC_KEY_REMOTE); - byte[] sharedKeyBytes = IkeKePayload.getSharedKey(mPrivateKeySpec, remotePublicKey); + + DHPrivateKeySpec privateKeySpec = + new DHPrivateKeySpec(privateKeyValue, primeValue, baseGenValue); + byte[] sharedKeyBytes = IkeKePayload.getSharedKey(privateKeySpec, remotePublicKey); byte[] expectedSharedKeyBytes = TestUtils.hexStringToByteArray(EXPECTED_SHARED_KEY); assertTrue(Arrays.equals(expectedSharedKeyBytes, sharedKeyBytes)); } - - @Test - public void testGetSharedkeyWithInvalidRemoteKey() throws Exception { - byte[] remotePublicKey = TestUtils.hexStringToByteArray(PRIME_1024_BIT_MODP_160_SUBGROUP); - - try { - byte[] sharedKeyBytes = IkeKePayload.getSharedKey(mPrivateKeySpec, remotePublicKey); - fail("Expected to fail because of invalid remote public key."); - } catch (GeneralSecurityException expected) { - } - } } diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java new file mode 100644 index 00000000..961abb30 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.UnsupportedCriticalPayloadException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.nio.ByteBuffer; + +public final class IkeMessageTest { + private static final String IKE_SA_INIT_HEADER_RAW_PACKET = + "8f54bf6d8b48e6e10000000000000000212022080000000000000150"; + private static final String IKE_SA_INIT_BODY_RAW_PACKET = + "220000300000002c010100040300000c0100000c" + + "800e00800300000803000002030000080400000200000008" + + "020000022800008800020000b4a2faf4bb54878ae21d6385" + + "12ece55d9236fc5046ab6cef82220f421f3ce6361faf3656" + + "4ecb6d28798a94aad7b2b4b603ddeaaa5630adb9ece8ac37" + + "534036040610ebdd92f46bef84f0be7db860351843858f8a" + + "cf87056e272377f70c9f2d81e29c7b0ce4f291a3a72476bb" + + "0b278fd4b7b0a4c26bbeb08214c707137607958729000024" + + "c39b7f368f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c7" + + "2cb4240eb5c464122900001c00004004e54f73b7d83f6beb" + + "881eab2051d8663f421d10b02b00001c00004005d915368c" + + "a036004cb578ae3e3fb268509aeab1900000002069936922" + + "8741c6d4ca094c93e242c9de19e7b7c60000000500000500"; + private static final String IKE_SA_INIT_RAW_PACKET = + IKE_SA_INIT_HEADER_RAW_PACKET + IKE_SA_INIT_BODY_RAW_PACKET; + + // Byte offsets of first payload type in IKE message header. + private static final int FIRST_PAYLOAD_TYPE_OFFSET = 16; + // Byte offsets of first payload's critical bit in IKE message body. + private static final int PAYLOAD_CRITICAL_BIT_OFFSET = 1; + // Byte offsets of first payload length in IKE message body. + private static final int FIRST_PAYLOAD_LENGTH_OFFSET = 2; + // Byte offsets of last payload length in IKE message body. + private static final int LAST_PAYLOAD_LENGTH_OFFSET = 278; + + private static final int[] SUPPORTED_PAYLOAD_LIST = { + IkePayload.PAYLOAD_TYPE_SA, + IkePayload.PAYLOAD_TYPE_KE, + IkePayload.PAYLOAD_TYPE_NONCE, + IkePayload.PAYLOAD_TYPE_NOTIFY, + IkePayload.PAYLOAD_TYPE_NOTIFY, + IkePayload.PAYLOAD_TYPE_VENDOR + }; + + class TestIkeSupportedPayload extends IkePayload { + TestIkeSupportedPayload(int payload, boolean critical) { + super(payload, critical); + } + + @Override + protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { + throw new UnsupportedOperationException( + "It is not supported to encode " + getTypeString()); + } + + @Override + protected int getPayloadLength() { + throw new UnsupportedOperationException( + "It is not supported to get payload length of " + getTypeString()); + } + + @Override + public String getTypeString() { + return "Test Payload"; + } + } + + @Before + public void setUp() { + IkePayloadFactory.sDecoderInstance = + new IkePayloadFactory.IIkePayloadDecoder() { + + @Override + public IkePayload decodeIkePayload( + int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody) + throws IkeException { + if (support(payloadType)) { + return new TestIkeSupportedPayload(payloadType, isCritical); + } else { + return new IkeUnsupportedPayload(payloadType, isCritical); + } + } + }; + } + + @After + public void tearDown() { + IkePayloadFactory.sDecoderInstance = new IkePayloadFactory.IkePayloadDecoder(); + } + + @Test + public void testDecodeIkeMessage() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); + IkeHeader header = new IkeHeader(inputPacket); + IkeMessage message = IkeMessage.decode(header, inputPacket); + assertEquals(SUPPORTED_PAYLOAD_LIST.length, message.ikePayloadList.size()); + for (int i = 0; i < SUPPORTED_PAYLOAD_LIST.length; i++) { + assertEquals(SUPPORTED_PAYLOAD_LIST[i], message.ikePayloadList.get(i).payloadType); + } + } + + @Test + public void testDecodeMessageWithUnsupportedUncriticalPayload() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); + // Set first payload unsupported uncritical + inputPacket[FIRST_PAYLOAD_TYPE_OFFSET] = (byte) 0xff; + IkeHeader header = new IkeHeader(inputPacket); + IkeMessage message = IkeMessage.decode(header, inputPacket); + assertEquals(SUPPORTED_PAYLOAD_LIST.length - 1, message.ikePayloadList.size()); + for (int i = 0; i < SUPPORTED_PAYLOAD_LIST.length - 1; i++) { + assertEquals(SUPPORTED_PAYLOAD_LIST[i + 1], message.ikePayloadList.get(i).payloadType); + } + } + + @Test + public void testThrowUnsupportedCriticalPayloadException() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); + // Set first payload unsupported critical + inputPacket[FIRST_PAYLOAD_TYPE_OFFSET] = (byte) 0xff; + inputPacket[IkeHeader.IKE_HEADER_LENGTH + PAYLOAD_CRITICAL_BIT_OFFSET] = (byte) 0x80; + + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage.decode(header, inputPacket); + fail( + "Expected UnsupportedCriticalPayloadException: first" + + "payload is unsupported critical."); + } catch (UnsupportedCriticalPayloadException expected) { + assertEquals(1, expected.payloadTypeList.size()); + } + } + + @Test + public void testDecodeMessageWithTooShortPayloadLength() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); + // Set first payload length to 0 + inputPacket[IkeHeader.IKE_HEADER_LENGTH + FIRST_PAYLOAD_LENGTH_OFFSET] = (byte) 0; + inputPacket[IkeHeader.IKE_HEADER_LENGTH + FIRST_PAYLOAD_LENGTH_OFFSET + 1] = (byte) 0; + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage message = IkeMessage.decode(header, inputPacket); + fail("Expected InvalidSyntaxException: Payload length is too short."); + } catch (InvalidSyntaxException expected) { + } + } + + @Test + public void testDecodeMessageWithTooLongPayloadLength() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); + // Increase last payload length by one byte + inputPacket[IkeHeader.IKE_HEADER_LENGTH + LAST_PAYLOAD_LENGTH_OFFSET]++; + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage message = IkeMessage.decode(header, inputPacket); + fail("Expected InvalidSyntaxException: Payload length is too long."); + } catch (InvalidSyntaxException expected) { + } + } + + @Test + public void testDecodeMessageWithExpectedBytesInTheEnd() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET + "0000"); + IkeHeader header = new IkeHeader(inputPacket); + try { + IkeMessage message = IkeMessage.decode(header, inputPacket); + fail("Expected InvalidSyntaxException: Unexpected bytes in the end of packet."); + } catch (InvalidSyntaxException expected) { + } + } + + private boolean support(int payloadType) { + return (payloadType == IkePayload.PAYLOAD_TYPE_SA + || payloadType == IkePayload.PAYLOAD_TYPE_KE + || payloadType == IkePayload.PAYLOAD_TYPE_NONCE + || payloadType == IkePayload.PAYLOAD_TYPE_NOTIFY + || payloadType == IkePayload.PAYLOAD_TYPE_VENDOR + || payloadType == IkePayload.PAYLOAD_TYPE_SK); + } + + @Test + public void testAttachEncodedHeader() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); + byte[] ikeBodyBytes = TestUtils.hexStringToByteArray(IKE_SA_INIT_BODY_RAW_PACKET); + IkeHeader header = new IkeHeader(inputPacket); + IkeMessage message = IkeMessage.decode(header, inputPacket); + + byte[] encodedIkeMessage = message.attachEncodedHeader(ikeBodyBytes); + assertArrayEquals(inputPacket, encodedIkeMessage); + } + + // TODO: Implement encodeToByteBuffer() of each payload and add test for encoding message +} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNoncePayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNoncePayloadTest.java index dd92a45e..f4e4a984 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNoncePayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNoncePayloadTest.java @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; -import com.android.internal.net.TestUtils; - import org.junit.Test; import java.nio.ByteBuffer; diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNotifyPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNotifyPayloadTest.java new file mode 100644 index 00000000..3e9ba80f --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNotifyPayloadTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; + +import org.junit.Test; + +import java.net.InetAddress; +import java.nio.ByteBuffer; + +public final class IkeNotifyPayloadTest { + private static final String NOTIFY_PAYLOAD_GENERIC_HEADER = "2900001c"; + private static final String NOTIFY_PAYLOAD_BODY_RAW_PACKET = + "00004004e54f73b7d83f6beb881eab2051d8663f421d10b0"; + + private static final String NAT_DETECTION_SOURCE_IP_DATA_HEX_STRING = + "e54f73b7d83f6beb881eab2051d8663f421d10b0"; + private static final String IKE_INITIATOR_SPI_HEX_STRING = "5f54bf6d8b48e6e1"; + private static final String IKE_RESPODNER_SPI_HEX_STRING = "0000000000000000"; + private static final String IP_ADDR = "10.80.80.13"; + private static final int PORT = 500; + + private static final int EXPECTED_PROTOCOL_ID = IkePayload.PROTOCOL_ID_UNSET; + private static final int EXPECTED_SPI_SIZE = IkePayload.SPI_LEN_NOT_INCLUDED; + + @IkePayload.PayloadType + private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_NOTIFY; + + @IkeNotifyPayload.NotifyType + private static final int EXPECTED_NOTIFY_TYPE = + IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP; + + private static final int EXPECTED_NOTIFY_DATA_LEN = 20; + + private static final int POS_PROTOCOL_ID = 0; + + @Test + public void testDecodeNotifyPayload() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(NOTIFY_PAYLOAD_BODY_RAW_PACKET); + IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); + assertEquals(EXPECTED_PROTOCOL_ID, payload.protocolId); + assertEquals(EXPECTED_SPI_SIZE, payload.spiSize); + assertEquals(EXPECTED_NOTIFY_TYPE, payload.notifyType); + assertEquals(EXPECTED_SPI_SIZE, payload.spi); + assertEquals(EXPECTED_NOTIFY_DATA_LEN, payload.notifyData.length); + } + + @Test + public void testDecodeNotifyPayloadThrowException() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(NOTIFY_PAYLOAD_BODY_RAW_PACKET); + // Change Protocol ID to ESP + inputPacket[POS_PROTOCOL_ID] = (byte) (IkePayload.PROTOCOL_ID_ESP & 0xFF); + try { + IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); + fail("Expected InvalidSyntaxException: Protocol ID should not be ESP"); + } catch (InvalidSyntaxException expected) { + } + } + + @Test + public void testGenerateNatDetectionData() throws Exception { + long initiatorIkeSpi = Long.parseLong(IKE_INITIATOR_SPI_HEX_STRING, 16); + long responderIkespi = Long.parseLong(IKE_RESPODNER_SPI_HEX_STRING, 16); + InetAddress inetAddress = InetAddress.getByName(IP_ADDR); + + byte[] netDetectionData = + IkeNotifyPayload.generateNatDetectionData( + initiatorIkeSpi, responderIkespi, inetAddress, PORT); + + byte[] expectedBytes = + TestUtils.hexStringToByteArray(NAT_DETECTION_SOURCE_IP_DATA_HEX_STRING); + assertArrayEquals(expectedBytes, netDetectionData); + } + + @Test + public void testEncodeNotifyPayload() throws Exception { + byte[] inputPacket = TestUtils.hexStringToByteArray(NOTIFY_PAYLOAD_BODY_RAW_PACKET); + IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); + + ByteBuffer byteBuffer = ByteBuffer.allocate(payload.getPayloadLength()); + payload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, byteBuffer); + + byte[] expectedNoncePayload = + TestUtils.hexStringToByteArray( + NOTIFY_PAYLOAD_GENERIC_HEADER + NOTIFY_PAYLOAD_BODY_RAW_PACKET); + assertArrayEquals(expectedNoncePayload, byteBuffer.array()); + } +} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java index 750ff463..afb3a810 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java @@ -14,69 +14,54 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.net.IpSecManager; -import android.net.IpSecSpiResponse; -import android.net.ipsec.ike.ChildSaProposal; -import android.net.ipsec.ike.IkeSaProposal; -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.util.Pair; -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Attribute; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.AttributeDecoder; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.ChildProposal; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EsnTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IkeProposal; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.KeyLengthAttribute; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Proposal; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.TransformDecoder; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.UnrecognizedAttribute; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.UnrecognizedTransform; -import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils; -import com.android.server.IpSecService; - -import libcore.net.InetAddressUtils; +import com.android.ike.ikev2.SaProposal; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.exceptions.InvalidSyntaxException; +import com.android.ike.ikev2.exceptions.NoValidProposalChosenException; +import com.android.ike.ikev2.message.IkeSaPayload.Attribute; +import com.android.ike.ikev2.message.IkeSaPayload.AttributeDecoder; +import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform; +import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform; +import com.android.ike.ikev2.message.IkeSaPayload.EsnTransform; +import com.android.ike.ikev2.message.IkeSaPayload.IntegrityTransform; +import com.android.ike.ikev2.message.IkeSaPayload.KeyLengthAttribute; +import com.android.ike.ikev2.message.IkeSaPayload.PrfTransform; +import com.android.ike.ikev2.message.IkeSaPayload.Proposal; +import com.android.ike.ikev2.message.IkeSaPayload.Transform; +import com.android.ike.ikev2.message.IkeSaPayload.TransformDecoder; +import com.android.ike.ikev2.message.IkeSaPayload.UnrecognizedAttribute; +import com.android.ike.ikev2.message.IkeSaPayload.UnrecognizedTransform; import org.junit.Before; import org.junit.Test; -import java.net.Inet4Address; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Random; public final class IkeSaPayloadTest { private static final String OUTBOUND_SA_PAYLOAD_HEADER = "22000030"; private static final String OUTBOUND_PROPOSAL_RAW_PACKET = - "0000002C010100040300000C0100000C800E0080030000080300000203000008040" - + "000020000000802000002"; + "0000002C010100040300000C0100000C800E0080030000080200000203000008030" + + "000020000000804000002"; private static final String INBOUND_PROPOSAL_RAW_PACKET = "0000002c010100040300000c0100000c800e0080030000080300000203000008040" + "000020000000802000002"; @@ -98,12 +83,6 @@ public final class IkeSaPayloadTest { + "0300000804000015030000080400001c030000080400001d030000080" + "400001e030000080400001f030000080400000f030000080400001003" + "00000804000012000000080400000e"; - private static final String INBOUND_CHILD_PROPOSAL_RAW_PACKET = - "0000002801030403cae7019f0300000c0100000c800e00800300000803000002000" + "0000805000000"; - private static final String INBOUND_CHILD_TWO_PROPOSAL_RAW_PACKET = - "0200002801030403cae7019f0300000c0100000c800e00800300000803000002000" - + "00008050000000000001802030401cae7019e0000000c01000012800e" - + "0080"; private static final String ENCR_TRANSFORM_RAW_PACKET = "0300000c0100000c800e0080"; private static final String PRF_TRANSFORM_RAW_PACKET = "0000000802000002"; private static final String INTEG_TRANSFORM_RAW_PACKET = "0300000803000002"; @@ -123,19 +102,6 @@ public final class IkeSaPayloadTest { // Constants for multiple proposals test private static final byte[] PROPOSAL_NUMBER_LIST = {1, 2}; - private static final Inet4Address LOCAL_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("8.8.4.4")); - private static final Inet4Address REMOTE_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("8.8.8.8")); - - private static final int DUMMY_CHILD_SPI_RESOURCE_ID_LOCAL_ONE = 0x1234; - private static final int DUMMY_CHILD_SPI_RESOURCE_ID_LOCAL_TWO = 0x1235; - private static final int DUMMY_CHILD_SPI_RESOURCE_ID_REMOTE = 0x2234; - - private static final int CHILD_SPI_LOCAL_ONE = 0x2ad4c0a2; - private static final int CHILD_SPI_LOCAL_TWO = 0x2ad4c0a3; - private static final int CHILD_SPI_REMOTE = 0xcae70197; - private AttributeDecoder mMockedAttributeDecoder; private KeyLengthAttribute mAttributeKeyLength128; private List<Attribute> mAttributeListWithKeyLength128; @@ -148,21 +114,9 @@ public final class IkeSaPayloadTest { private Transform[] mValidNegotiatedTransformSet; - private IkeSaProposal mIkeSaProposalOne; - private IkeSaProposal mIkeSaProposalTwo; - private IkeSaProposal[] mTwoIkeSaProposalsArray; - - private ChildSaProposal mChildSaProposalOne; - private ChildSaProposal mChildSaProposalTwo; - private ChildSaProposal[] mTwoChildSaProposalsArray; - - private MockIpSecTestUtils mMockIpSecTestUtils; - private IpSecService mMockIpSecService; - private IpSecManager mIpSecManager; - - private IpSecSpiResponse mDummyIpSecSpiResponseLocalOne; - private IpSecSpiResponse mDummyIpSecSpiResponseLocalTwo; - private IpSecSpiResponse mDummyIpSecSpiResponseRemote; + private SaProposal mSaProposalOne; + private SaProposal mSaProposalTwo; + private SaProposal[] mTwoSaProposalsArray; @Before public void setUp() throws Exception { @@ -190,8 +144,8 @@ public final class IkeSaPayloadTest { mDhGroup1024Transform }; - mIkeSaProposalOne = - new IkeSaProposal.Builder() + mSaProposalOne = + SaProposal.Builder.newIkeSaProposalBuilder() .addEncryptionAlgorithm( SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) @@ -199,8 +153,8 @@ public final class IkeSaPayloadTest { .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) .build(); - mIkeSaProposalTwo = - new IkeSaProposal.Builder() + mSaProposalTwo = + SaProposal.Builder.newIkeSaProposalBuilder() .addEncryptionAlgorithm( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128) @@ -211,35 +165,7 @@ public final class IkeSaPayloadTest { .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) .addDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP) .build(); - mTwoIkeSaProposalsArray = new IkeSaProposal[] {mIkeSaProposalOne, mIkeSaProposalTwo}; - - mChildSaProposalOne = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) - .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) - .build(); - mChildSaProposalTwo = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, - SaProposal.KEY_LEN_AES_128) - .build(); - mTwoChildSaProposalsArray = - new ChildSaProposal[] {mChildSaProposalOne, mChildSaProposalTwo}; - - mMockIpSecTestUtils = MockIpSecTestUtils.setUpMockIpSec(); - mIpSecManager = mMockIpSecTestUtils.getIpSecManager(); - - IpSecService mMockIpSecService = mMockIpSecTestUtils.getIpSecService(); - when(mMockIpSecService.allocateSecurityParameterIndex( - eq(LOCAL_ADDRESS.getHostAddress()), anyInt(), anyObject())) - .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(CHILD_SPI_LOCAL_ONE)) - .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(CHILD_SPI_LOCAL_TWO)); - - when(mMockIpSecService.allocateSecurityParameterIndex( - eq(REMOTE_ADDRESS.getHostAddress()), anyInt(), anyObject())) - .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(CHILD_SPI_REMOTE)); + mTwoSaProposalsArray = new SaProposal[] {mSaProposalOne, mSaProposalTwo}; } // TODO: Add tearDown() to reset Proposal.sTransformDecoder and Transform.sAttributeDecoder. @@ -602,7 +528,7 @@ public final class IkeSaPayloadTest { return new TransformDecoder() { @Override public Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) - throws IkeProtocolException { + throws IkeException { for (int i = 0; i < count; i++) { // Read length field and move position inputBuffer.getShort(); @@ -628,7 +554,7 @@ public final class IkeSaPayloadTest { assertEquals(IkePayload.SPI_LEN_NOT_INCLUDED, proposal.spiSize); assertEquals(IkePayload.SPI_NOT_INCLUDED, proposal.spi); assertFalse(proposal.hasUnrecognizedTransform); - assertNotNull(proposal.getSaProposal()); + assertNotNull(proposal.saProposal); } @Test @@ -649,13 +575,14 @@ public final class IkeSaPayloadTest { @Test public void testEncodeProposal() throws Exception { - // Construct Proposal for IKE INIT exchange. - IkeProposal proposal = - IkeProposal.createIkeProposal( + Proposal proposal = + new Proposal( (byte) PROPOSAL_NUMBER, + IkePayload.PROTOCOL_ID_IKE, IkePayload.SPI_LEN_NOT_INCLUDED, - mIkeSaProposalOne, - LOCAL_ADDRESS); + IkePayload.SPI_NOT_INCLUDED, + mSaProposalOne, + false /*has no unrecognized Tramsform*/); ByteBuffer byteBuffer = ByteBuffer.allocate(proposal.getProposalLength()); proposal.encodeToByteBuffer(true /*is the last*/, byteBuffer); @@ -678,66 +605,42 @@ public final class IkeSaPayloadTest { } @Test - public void testBuildOutboundIkeRekeySaResponsePayload() throws Exception { + public void testBuildIkeSaResponsePayload() throws Exception { + final long ikeSpi = new Random().nextLong(); + final SaProposal[] saProposals = new SaProposal[] {mSaProposalOne}; IkeSaPayload saPayload = - IkeSaPayload.createRekeyIkeSaResponsePayload( - (byte) 1, mIkeSaProposalOne, LOCAL_ADDRESS); + new IkeSaPayload( + true, true, IkePayload.SPI_LEN_IKE, new long[] {ikeSpi}, saProposals); assertTrue(saPayload.isSaResponse); - assertEquals(1, saPayload.proposalList.size()); + assertEquals(saProposals.length, saPayload.proposalList.size()); - IkeProposal proposal = (IkeProposal) saPayload.proposalList.get(0); + Proposal proposal = saPayload.proposalList.get(0); assertEquals(IkePayload.PROTOCOL_ID_IKE, proposal.protocolId); assertEquals(IkePayload.SPI_LEN_IKE, proposal.spiSize); - assertEquals(mIkeSaProposalOne, proposal.saProposal); - - assertNotNull(proposal.getIkeSpiResource()); + assertEquals(ikeSpi, proposal.spi); + assertEquals(mSaProposalOne, proposal.saProposal); } @Test - public void testBuildOutboundInitialIkeSaRequestPayload() throws Exception { - IkeSaPayload saPayload = IkeSaPayload.createInitialIkeSaPayload(mTwoIkeSaProposalsArray); + public void testBuildInitialIkeSaRequestPayload() throws Exception { + IkeSaPayload saPayload = new IkeSaPayload(mTwoSaProposalsArray); assertFalse(saPayload.isSaResponse); assertEquals(PROPOSAL_NUMBER_LIST.length, saPayload.proposalList.size()); for (int i = 0; i < saPayload.proposalList.size(); i++) { - IkeProposal proposal = (IkeProposal) saPayload.proposalList.get(i); + Proposal proposal = saPayload.proposalList.get(i); assertEquals(PROPOSAL_NUMBER_LIST[i], proposal.number); assertEquals(IkePayload.PROTOCOL_ID_IKE, proposal.protocolId); assertEquals(IkePayload.SPI_LEN_NOT_INCLUDED, proposal.spiSize); - assertEquals(mTwoIkeSaProposalsArray[i], proposal.saProposal); - - // SA Payload for IKE INIT exchange does not include IKE SPIs. - assertNull(proposal.getIkeSpiResource()); - } - } - - @Test - public void testBuildOutboundChildSaRequest() throws Exception { - IkeSaPayload saPayload = - IkeSaPayload.createChildSaRequestPayload( - mTwoChildSaProposalsArray, mIpSecManager, LOCAL_ADDRESS); - - assertFalse(saPayload.isSaResponse); - assertEquals(PROPOSAL_NUMBER_LIST.length, saPayload.proposalList.size()); - - int[] expectedSpis = new int[] {CHILD_SPI_LOCAL_ONE, CHILD_SPI_LOCAL_TWO}; - for (int i = 0; i < saPayload.proposalList.size(); i++) { - ChildProposal proposal = (ChildProposal) saPayload.proposalList.get(i); - assertEquals(PROPOSAL_NUMBER_LIST[i], proposal.number); - assertEquals(IkePayload.PROTOCOL_ID_ESP, proposal.protocolId); - assertEquals(IkePayload.SPI_LEN_IPSEC, proposal.spiSize); - assertEquals(mTwoChildSaProposalsArray[i], proposal.saProposal); - - assertEquals(expectedSpis[i], proposal.getChildSpiResource().getSpi()); + assertEquals(mTwoSaProposalsArray[i], proposal.saProposal); } } @Test public void testEncodeIkeSaPayload() throws Exception { - IkeSaPayload saPayload = - IkeSaPayload.createInitialIkeSaPayload(new IkeSaProposal[] {mIkeSaProposalOne}); + IkeSaPayload saPayload = new IkeSaPayload(new SaProposal[] {mSaProposalOne}); ByteBuffer byteBuffer = ByteBuffer.allocate(saPayload.getPayloadLength()); saPayload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_KE, byteBuffer); @@ -748,109 +651,25 @@ public final class IkeSaPayloadTest { assertArrayEquals(expectedBytes, byteBuffer.array()); } - private void buildAndVerifyIkeSaRespProposal( - byte[] saResponseBytes, Transform[] decodedTransforms) throws Exception { + private void buildAndVerifySaRespProposal(byte[] saResponseBytes, Transform[] decodedTransforms) + throws Exception { // Build response SA payload from decoding bytes. Proposal.sTransformDecoder = getDummyTransformDecoder(decodedTransforms); IkeSaPayload respPayload = new IkeSaPayload(false, true, saResponseBytes); - // Build request SA payload for IKE INIT exchange from SaProposal. - IkeSaPayload reqPayload = IkeSaPayload.createInitialIkeSaPayload(mTwoIkeSaProposalsArray); - - Pair<IkeProposal, IkeProposal> negotiatedProposalPair = - IkeSaPayload.getVerifiedNegotiatedIkeProposalPair( - reqPayload, respPayload, REMOTE_ADDRESS); - IkeProposal reqProposal = negotiatedProposalPair.first; - IkeProposal respProposal = negotiatedProposalPair.second; + // Build request SA payload from SaProposal. + IkeSaPayload reqPayload = new IkeSaPayload(mTwoSaProposalsArray); - assertEquals(respPayload.proposalList.get(0).getSaProposal(), respProposal.getSaProposal()); + SaProposal saProposal = respPayload.getVerifiedNegotiatedProposal(reqPayload); - // SA Payload for IKE INIT exchange does not include IKE SPIs. - assertNull(reqProposal.getIkeSpiResource()); - assertNull(respProposal.getIkeSpiResource()); + assertEquals(respPayload.proposalList.get(0).saProposal, saProposal); } @Test - public void testGetVerifiedNegotiatedIkeProposal() throws Exception { + public void testGetVerifiedNegotiatedProposal() throws Exception { byte[] inputPacket = TestUtils.hexStringToByteArray(INBOUND_PROPOSAL_RAW_PACKET); - buildAndVerifyIkeSaRespProposal(inputPacket, mValidNegotiatedTransformSet); - } - - private void verifyChildSaNegotiation( - IkeSaPayload reqPayload, - IkeSaPayload respPayload, - IpSecManager ipSecManager, - InetAddress remoteAddress, - boolean isLocalInit) - throws Exception { - // SA negotiation - Pair<ChildProposal, ChildProposal> negotiatedProposalPair = - IkeSaPayload.getVerifiedNegotiatedChildProposalPair( - reqPayload, respPayload, ipSecManager, remoteAddress); - ChildProposal reqProposal = negotiatedProposalPair.first; - ChildProposal respProposal = negotiatedProposalPair.second; - - // Verify results - assertEquals(respPayload.proposalList.get(0).getSaProposal(), respProposal.getSaProposal()); - - int initSpi = isLocalInit ? CHILD_SPI_LOCAL_ONE : CHILD_SPI_REMOTE; - int respSpi = isLocalInit ? CHILD_SPI_REMOTE : CHILD_SPI_LOCAL_ONE; - assertEquals(initSpi, reqProposal.getChildSpiResource().getSpi()); - assertEquals(respSpi, respProposal.getChildSpiResource().getSpi()); - - // Verify SPIs in unselected Proposals have been released. - for (Proposal proposal : reqPayload.proposalList) { - if (proposal != reqProposal) { - assertNull(((ChildProposal) proposal).getChildSpiResource()); - } - } - } - - @Test - public void testGetVerifiedNegotiatedChildProposalForLocalCreate() throws Exception { - // Build local request - IkeSaPayload reqPayload = - IkeSaPayload.createChildSaRequestPayload( - mTwoChildSaProposalsArray, mIpSecManager, LOCAL_ADDRESS); - - // Build remote response - Proposal.sTransformDecoder = - getDummyTransformDecoder(mChildSaProposalOne.getAllTransforms()); - IkeSaPayload respPayload = - new IkeSaPayload( - false /*critical*/, - true /*isResp*/, - TestUtils.hexStringToByteArray(INBOUND_CHILD_PROPOSAL_RAW_PACKET)); - - verifyChildSaNegotiation( - reqPayload, respPayload, mIpSecManager, REMOTE_ADDRESS, true /*isLocalInit*/); - } - - @Test - public void testGetVerifiedNegotiatedChildProposalForRemoteCreate() throws Exception { - Transform[] transformsOne = mChildSaProposalOne.getAllTransforms(); - Transform[] transformsTwo = mChildSaProposalTwo.getAllTransforms(); - Transform[] decodedTransforms = new Transform[transformsOne.length + transformsTwo.length]; - System.arraycopy(transformsOne, 0, decodedTransforms, 0, transformsOne.length); - System.arraycopy( - transformsTwo, 0, decodedTransforms, transformsOne.length, transformsTwo.length); - - // Build remote request - Proposal.sTransformDecoder = getDummyTransformDecoder(decodedTransforms); - IkeSaPayload reqPayload = - new IkeSaPayload( - false /*critical*/, - false /*isResp*/, - TestUtils.hexStringToByteArray(INBOUND_CHILD_TWO_PROPOSAL_RAW_PACKET)); - - // Build local response - IkeSaPayload respPayload = - IkeSaPayload.createChildSaResponsePayload( - (byte) 1, mChildSaProposalOne, mIpSecManager, LOCAL_ADDRESS); - - verifyChildSaNegotiation( - reqPayload, respPayload, mIpSecManager, REMOTE_ADDRESS, false /*isLocalInit*/); + buildAndVerifySaRespProposal(inputPacket, mValidNegotiatedTransformSet); } // Test throwing when negotiated proposal in SA response payload has unrecognized Transform. @@ -864,7 +683,7 @@ public final class IkeSaPayloadTest { negotiatedTransformSet[0] = new UnrecognizedTransform(-1, 1, new LinkedList<>()); try { - buildAndVerifyIkeSaRespProposal(inputPacket, negotiatedTransformSet); + buildAndVerifySaRespProposal(inputPacket, negotiatedTransformSet); fail("Expected to fail because negotiated proposal has unrecognized Transform."); } catch (NoValidProposalChosenException expected) { } @@ -877,7 +696,7 @@ public final class IkeSaPayloadTest { inputPacket[PROPOSAL_NUMBER_OFFSET] = (byte) 10; try { - buildAndVerifyIkeSaRespProposal(inputPacket, mValidNegotiatedTransformSet); + buildAndVerifySaRespProposal(inputPacket, mValidNegotiatedTransformSet); fail("Expected to fail due to invalid proposal number."); } catch (NoValidProposalChosenException expected) { } @@ -890,7 +709,7 @@ public final class IkeSaPayloadTest { inputPacket[PROTOCOL_ID_OFFSET] = IkePayload.PROTOCOL_ID_ESP; try { - buildAndVerifyIkeSaRespProposal(inputPacket, mValidNegotiatedTransformSet); + buildAndVerifySaRespProposal(inputPacket, mValidNegotiatedTransformSet); fail("Expected to fail due to mismatched protocol ID."); } catch (NoValidProposalChosenException expected) { } @@ -907,7 +726,7 @@ public final class IkeSaPayloadTest { negotiatedTransformSet[0] = mEncrAesGcm8Key128Transform; try { - buildAndVerifyIkeSaRespProposal(inputPacket, negotiatedTransformSet); + buildAndVerifySaRespProposal(inputPacket, negotiatedTransformSet); fail("Expected to fail due to mismatched Transform."); } catch (NoValidProposalChosenException expected) { } @@ -919,7 +738,7 @@ public final class IkeSaPayloadTest { byte[] inputPacket = TestUtils.hexStringToByteArray(INBOUND_PROPOSAL_RAW_PACKET); try { - buildAndVerifyIkeSaRespProposal(inputPacket, new Transform[0]); + buildAndVerifySaRespProposal(inputPacket, new Transform[0]); fail("Expected to fail due to absence of Transform."); } catch (NoValidProposalChosenException expected) { } diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSkPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java index 898db30f..13866fb6 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSkPayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java @@ -14,24 +14,21 @@ * limitations under the License. */ -package com.android.internal.net.ipsec.ike.message; +package com.android.ike.ikev2.message; import static org.junit.Assert.assertArrayEquals; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; - import org.junit.Before; import org.junit.Test; import java.nio.ByteBuffer; import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + public final class IkeSkPayloadTest { private static final String IKE_AUTH_INIT_REQUEST_HEX_STRING = @@ -60,29 +57,26 @@ public final class IkeSkPayloadTest { "554fbf5a05b7f511e05a30ce23d874db9ef55e51"; private static final String ENCR_ALGO_AES_CBC = "AES/CBC/NoPadding"; + private static final String INTE_ALGO_HMAC_SHA1 = "HmacSHA1"; private static final int CHECKSUM_LEN = 12; - private IkeCipher mAesCbcDecryptCipher; - private byte[] mAesCbcDecryptionKey; - - private IkeMacIntegrity mHmacSha1IntegrityMac; - private byte[] mHmacSha1IntegrityKey; + private Cipher mAesCbcDecryptCipher; + private SecretKey mAesCbcDecryptKey; + private Mac mHmacSha1IntegrityMac; @Before public void setUp() throws Exception { mAesCbcDecryptCipher = - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, - SaProposal.KEY_LEN_AES_128), - IkeMessage.getSecurityProvider()); - mAesCbcDecryptionKey = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP); + Cipher.getInstance(ENCR_ALGO_AES_CBC, IkeMessage.getSecurityProvider()); + byte[] decryptKeyBytes = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP); + mAesCbcDecryptKey = new SecretKeySpec(decryptKeyBytes, ENCR_ALGO_AES_CBC); + mHmacSha1IntegrityMac = - IkeMacIntegrity.create( - new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96), - IkeMessage.getSecurityProvider()); - mHmacSha1IntegrityKey = TestUtils.hexStringToByteArray(INTE_KEY_FROM_INIT_TO_RESP); + Mac.getInstance(INTE_ALGO_HMAC_SHA1, IkeMessage.getSecurityProvider()); + byte[] integrityKeyBytes = TestUtils.hexStringToByteArray(INTE_KEY_FROM_INIT_TO_RESP); + SecretKeySpec integrityKey = new SecretKeySpec(integrityKeyBytes, INTE_ALGO_HMAC_SHA1); + mHmacSha1IntegrityMac.init(integrityKey); } @Test @@ -93,12 +87,11 @@ public final class IkeSkPayloadTest { IkeSkPayload payload = IkePayloadFactory.getIkeSkPayload( - false /*isSkf*/, message, mHmacSha1IntegrityMac, + CHECKSUM_LEN, mAesCbcDecryptCipher, - mHmacSha1IntegrityKey, - mAesCbcDecryptionKey) + mAesCbcDecryptKey) .first; int payloadLength = payload.getPayloadLength(); ByteBuffer buffer = ByteBuffer.allocate(payloadLength); diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeTsPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeTsPayloadTest.java new file mode 100644 index 00000000..f547526a --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeTsPayloadTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 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 com.android.ike.ikev2.message; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import java.nio.ByteBuffer; + +public final class IkeTsPayloadTest { + private static final String TS_INITIATOR_PAYLOAD_HEX_STRING = + "2d00002802000000070000100000ffff00000000ffffffff070000100000ffff00000001fffffffe"; + private static final int NUMBER_OF_TS = 2; + + @Test + public void testDecodeTsInitiatorPayload() throws Exception { + ByteBuffer inputBuffer = + ByteBuffer.wrap(TestUtils.hexStringToByteArray(TS_INITIATOR_PAYLOAD_HEX_STRING)); + + IkePayload payload = + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_TS_INITIATOR, false, inputBuffer) + .first; + assertTrue(payload instanceof IkeTsPayload); + + IkeTsPayload tsPayload = (IkeTsPayload) payload; + assertEquals(IkePayload.PAYLOAD_TYPE_TS_INITIATOR, tsPayload.payloadType); + assertEquals(NUMBER_OF_TS, tsPayload.numTs); + } +} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java b/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java new file mode 100644 index 00000000..1fcdac06 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 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 com.android.ike.ikev2.message; + +import android.util.Pair; + +import com.android.ike.ikev2.exceptions.IkeException; + +import java.nio.ByteBuffer; + +/** TestUtils provides utility methods for parsing Hex String */ +public final class TestUtils { + + public static byte[] hexStringToByteArray(String hexString) throws IllegalArgumentException { + int len = hexString.length(); + if (len % 2 != 0) { + throw new IllegalArgumentException("Invalid Hex String"); + } + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = + (byte) + ((Character.digit(hexString.charAt(i), 16) << 4) + + Character.digit(hexString.charAt(i + 1), 16)); + } + return data; + } + + public static IkePayload hexStringToIkePayload( + @IkePayload.PayloadType int payloadType, boolean isResp, String payloadHexString) + throws IkeException { + byte[] payloadBytes = hexStringToByteArray(payloadHexString); + // Returned Pair consists of the IkePayload and the following IkePayload's type. + Pair<IkePayload, Integer> pair = + IkePayloadFactory.getIkePayload(payloadType, isResp, ByteBuffer.wrap(payloadBytes)); + return pair.first; + } +} diff --git a/tests/iketests/src/java/com/android/internal/net/utils/BigIntegerUtilsTest.java b/tests/iketests/src/java/com/android/ike/ikev2/utils/BigIntegerUtilsTest.java index 29bb313d..e3d06ce7 100644 --- a/tests/iketests/src/java/com/android/internal/net/utils/BigIntegerUtilsTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/utils/BigIntegerUtilsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.net.utils; +package com.android.ike.ikev2.utils; import static org.junit.Assert.assertArrayEquals; diff --git a/tests/iketests/src/java/com/android/internal/net/TestUtils.java b/tests/iketests/src/java/com/android/internal/net/TestUtils.java deleted file mode 100644 index 6dd5ce5e..00000000 --- a/tests/iketests/src/java/com/android/internal/net/TestUtils.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net; - -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.spy; - -import com.android.internal.net.utils.Log; - -import java.nio.ByteBuffer; - -/** TestUtils provides utility methods for parsing Hex String and constructing testing Logger. */ -public class TestUtils { - public static byte[] hexStringToByteArray(String hexString) { - int len = hexString.length(); - if (len % 2 != 0) { - throw new IllegalArgumentException("Invalid Hex String"); - } - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = - (byte) - ((Character.digit(hexString.charAt(i), 16) << 4) - + Character.digit(hexString.charAt(i + 1), 16)); - } - return data; - } - - public static int hexStringToInt(String hexString) { - if (hexString.length() > 8) { - throw new IllegalArgumentException("Invalid hex string length for integer type"); - } - - for (int i = hexString.length(); i < 8; i++) { - hexString = "0" + hexString; - } - - return ByteBuffer.wrap(hexStringToByteArray(hexString)).getInt(); - } - - public static String stringToHexString(String s) { - // two hex characters for each char in s - StringBuilder sb = new StringBuilder(s.length() * 2); - char[] chars = s.toCharArray(); - for (char c : chars) { - sb.append(Integer.toHexString(c)); - } - return sb.toString(); - } - - public static Log makeSpyLogThrowExceptionForWtf(String tag) { - Log spyLog = spy(new Log(tag, true /*logSensitive*/)); - - doAnswer( - (invocation) -> { - throw new IllegalStateException((String) invocation.getArguments()[1]); - }) - .when(spyLog) - .wtf(anyString(), anyString()); - - doAnswer( - (invocation) -> { - throw (Throwable) invocation.getArguments()[2]; - }) - .when(spyLog) - .wtf(anyString(), anyString(), anyObject()); - - return spyLog; - } - - public static Log makeSpyLogDoLogErrorForWtf(String tag) { - Log spyLog = spy(new Log(tag, true /*logSensitive*/)); - - doAnswer( - (invocation) -> { - spyLog.e( - "Mock logging WTF: " + invocation.getArguments()[0], - (String) invocation.getArguments()[1]); - return null; - }) - .when(spyLog) - .wtf(anyString(), anyString()); - - doAnswer( - (invocation) -> { - spyLog.e( - "Mock logging WTF: " + invocation.getArguments()[0], - (String) invocation.getArguments()[1], - (Throwable) invocation.getArguments()[2]); - return null; - }) - .when(spyLog) - .wtf(anyString(), anyString(), anyObject()); - - return spyLog; - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapAkaPrimeTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapAkaPrimeTest.java deleted file mode 100644 index 0872b67f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapAkaPrimeTest.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.eap.EapSessionConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.statemachine.EapStateMachine; - -import org.junit.Before; -import org.junit.Test; - -public class EapAkaPrimeTest extends EapMethodEndToEndTest { - private static final long AUTHENTICATOR_TIMEOUT_MILLIS = 250L; - - private static final int SUB_ID = 1; - private static final String UNFORMATTED_IDENTITY = "123456789ABCDEF"; // IMSI - - // EAP_IDENTITY = hex("test@android.net") - private static final byte[] EAP_IDENTITY = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - private static final boolean ALLOW_MISMATCHED_NETWORK_NAMES = false; - private static final String PEER_NETWORK_NAME_1 = "foo:bar"; - private static final String PEER_NETWORK_NAME_2 = "bar"; - - // hex("foo:bar:buzz") - private static final String SERVER_NETWORK_NAME = "666F6F3A6261723A62757A7A"; - - // TODO(b/142667016): replace with externally generated test values - - // IK: 7320EE404E055EF2B5AB0F86E96C48BE - // CK: E9D1707652E13BF3E05975F601678E5C - // Server Network Name: 666F6F3A6261723A62757A7A - // SQN ^ AK: 35A9143ED9E1 - // IK': 79DC30692F3D2303D148549E5D50D0AA - // CK': BBD0A7AD3F14757BA604C4CBE70F9090 - // K_encr: 4c22c289bcf40367cf2bdb6a6e3fe56b - // K_aut: c64abd508ab628f842e9fb40a14fea769d2ccc67a8412794fe3b4c2556431e78 - // K_re: 5454ccf7ecc227f25c6cd1023e09394fa5cedc14a2f155e9d96a70dc404b4dca - private static final String RAND_1 = "D6A296F030A305601B311D38A004505C"; - private static final String RAND_2 = "000102030405060708090A0B0C0D0E0F"; - private static final String AUTN = "35A9143ED9E100011795E785DAFAAD9B"; - private static final String RES = "E5167A255FDCDE9248AF6B50ADA0D944"; - private static final String AUTS = "0102030405060708090A0B0C0D0E"; - private static final byte[] MSK = - hexStringToByteArray( - "695788d8f33af56b5b2fea065a0e8656" - + "7dc48120d6070d96056f9668614ec3e7" - + "feb4933a3aaab3587980a624998c8b5e" - + "a69d7295b824ef4a2201720be89d04df"); - private static final byte[] EMSK = - hexStringToByteArray( - "2db1f574d6e92cec294779defef5a7f0" - + "49319cc75367102815d0244087f23660" - + "0986b47a862c1aeeca418c84a2f9581b" - + "0738fdefd229a5f7a4ca76709379bf00"); - - // IK: 7320EE404E055EF2B5AB0F86E96C48BE - // CK: E9D1707652E13BF3E05975F601678E5C - // Server Network Name: 666F6F3A6261723A62757A7A - // SQN ^ AK: 35A9143ED9E1 - // IK': 6C45FB0B12FF8172223B6D0E599EAE20 - // CK': A01C894696BEB759ABE0340F71A20D7B - // K_encr: c039213c78fcf78a34bef30219a77822 - // K_aut: 95b014e569144eba71a387f91fb6b72e06781df12d61bfe88e5149477cd232aa - // K_re: 1000c2e2f01766a4d2581ac454e41fce1ee17bcccbc32dfad78815075d884c5e - private static final byte[] MSK_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "ad75a86586773134dcd9e78e3f75b282" - + "7a42435cb1be7235be58cddc60a0ba19" - + "dd5c30accfdb0db5ef065f46c3c25d7b" - + "9f8703d9493a2dc6fb6563dbdc854658"); - private static final byte[] EMSK_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "31a3f2bb0e3e831d991dc8666438297f" - + "4a5bc157fc1e31537e5a4927206d7b4b" - + "db830761eea3441d9b90da48aebb9734" - + "d3cbdec96072230a64043f54932a8841"); - - // Base 64 of: [Length][RAND_1][Length][AUTN] - private static final String BASE64_CHALLENGE_1 = - "ENailvAwowVgGzEdOKAEUFwQNakUPtnhAAEXleeF2vqtmw=="; - - // Base 64 of: ['DB'][Length][RES][Length][IK][Length][CK] - private static final String BASE_64_RESPONSE_SUCCESS = - "2xDlFnolX9zekkiva1CtoNlEEHMg7kBOBV7ytasPhulsSL4Q6dFwdlLhO/PgWXX2AWeOXA=="; - - // Base 64 of: [Length][RAND_2][Length][AUTN] - private static final String BASE64_CHALLENGE_2 = - "EAABAgMEBQYHCAkKCwwNDg8QNakUPtnhAAEXleeF2vqtmw=="; - - // Base 64 of: ['DC'][Length][AUTS] - private static final String BASE_64_RESPONSE_SYNC_FAIL = "3A4BAgMEBQYHCAkKCwwNDg=="; - - private static final String REQUEST_MAC = "9089f89b2f99bb85f2f2b529779f98db"; - private static final String RESPONSE_MAC = "48d7d6a80e1e2ff26a1e4148e0a2303e"; - private static final String REQUEST_MAC_WITHOUT_IDENTITY_REQ = - "59f680ede020a3d0156eef56affb6997"; - private static final String RESPONSE_MAC_WITHOUT_IDENTITY_REQ = - "e15322ff4abe51479c0fa92d00e343d7"; - - private static final byte[] EAP_AKA_PRIME_IDENTITY_REQUEST = - hexStringToByteArray( - "01CD000C" // EAP-Request | ID | length in bytes - + "32050000" // EAP-AKA' | Identity | 2B padding - + "0D010000"); // AT_ANY_ID_REQ attribute - private static final byte[] EAP_AKA_PRIME_IDENTITY_RESPONSE = - hexStringToByteArray( - "02CD001C" // EAP-Response | ID | length in bytes - + "32050000" // EAP-AKA' | Identity | 2B padding - + "0E05001036313233343536373839414243444546"); // AT_IDENTITY attribute - - private static final byte[] EAP_AKA_PRIME_CHALLENGE_REQUEST = - hexStringToByteArray( - "01CE0044" // EAP-Request | ID | length in bytes - + "32010000" // EAP-AKA' | Challenge | 2B padding - + "01050000" + RAND_1 // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "1704000C" + SERVER_NETWORK_NAME // AT_KDF_INPUT attribute - + "18010001" // AT_KDF attribute - + "0B050000" + REQUEST_MAC); // AT_MAC attribute - private static final byte[] EAP_AKA_PRIME_CHALLENGE_RESPONSE = - hexStringToByteArray( - "02CE0030" // EAP-Response | ID | length in bytes - + "32010000" // EAP-AKA' | Challenge | 2B padding - + "03050080" + RES // AT_RES attribute - + "0B050000" + RESPONSE_MAC); // AT_MAC attribute - - private static final byte[] EAP_AKA_PRIME_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "01CE0044" // EAP-Request | ID | length in bytes - + "32010000" // EAP-AKA' | Challenge | 2B padding - + "01050000" + RAND_1 // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "1704000C" + SERVER_NETWORK_NAME // AT_KDF_INPUT attribute - + "18010001" // AT_KDF attribute - + "0B050000" + REQUEST_MAC_WITHOUT_IDENTITY_REQ); // AT_MAC attribute - private static final byte[] EAP_AKA_PRIME_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQUEST = - hexStringToByteArray( - "02CE0030" // EAP-Response | ID | length in bytes - + "32010000" // EAP-AKA' | Challenge | 2B padding - + "03050080" + RES // AT_RES attribute - + "0B050000" + RESPONSE_MAC_WITHOUT_IDENTITY_REQ); // AT_MAC attribute - - private static final byte[] EAP_AKA_PRIME_CHALLENGE_REQUEST_SYNC_FAIL = - hexStringToByteArray( - "01CE0044" // EAP-Request | ID | length in bytes - + "32010000" // EAP-AKA' | Challenge | 2B padding - + "01050000" + RAND_2 // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "1704000C" + SERVER_NETWORK_NAME // AT_KDF_INPUT attribute - + "18010001" // AT_KDF attribute - + "0B050000" + REQUEST_MAC); // AT_MAC attribute - private static final byte[] EAP_AKA_PRIME_SYNC_FAIL_RESPONSE = - hexStringToByteArray( - "02CE0018" // EAP-Response | ID | length in bytes - + "32040000" // EAP-AKA' | Synchronization-Failure | 2B padding - + "0404" + AUTS); // AT_AUTS attribute - - private static final byte[] EAP_AKA_PRIME_AUTHENTICATION_REJECT = - hexStringToByteArray( - "02CE0008" // EAP-Response | ID | length in bytes - + "32020000"); // EAP-AKA' | Authentication-Reject | 2B padding - - private static final byte[] EAP_RESPONSE_NAK_PACKET = - hexStringToByteArray("021000060332"); // NAK with EAP-AKA' listed - - private TelephonyManager mMockTelephonyManager; - - @Before - @Override - public void setUp() { - super.setUp(); - - setUp(ALLOW_MISMATCHED_NETWORK_NAMES, PEER_NETWORK_NAME_1); - } - - private void setUp(boolean allowMismatchedNetworkNames, String peerNetworkName) { - mMockTelephonyManager = mock(TelephonyManager.class); - - mEapSessionConfig = - new EapSessionConfig.Builder() - .setEapIdentity(EAP_IDENTITY) - .setEapAkaPrimeConfig( - SUB_ID, APPTYPE_USIM, peerNetworkName, allowMismatchedNetworkNames) - .build(); - mEapAuthenticator = - new EapAuthenticator( - mTestLooper.getLooper(), - mMockCallback, - new EapStateMachine(mMockContext, mEapSessionConfig, mMockSecureRandom), - (runnable) -> runnable.run(), - AUTHENTICATOR_TIMEOUT_MILLIS); - - when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE)) - .thenReturn(mMockTelephonyManager); - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - } - - @Test - public void testEapAkaPrimeEndToEnd() { - verifyEapPrimeAkaIdentity(); - verifyEapAkaPrimeChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_PRIME_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaPrimeEndToEndWithoutIdentityRequest() { - verifyEapAkaPrimeChallengeWithoutIdentityReq(); - verifyEapSuccess(MSK_WITHOUT_IDENTITY_REQ, EMSK_WITHOUT_IDENTITY_REQ); - } - - @Test - public void testEapAkaPrimeWithEapNotifications() { - verifyEapNotification(1); - verifyEapPrimeAkaIdentity(); - - verifyEapNotification(2); - verifyEapAkaPrimeChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_PRIME_CHALLENGE_RESPONSE); - - verifyEapNotification(3); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaPrimeUnsupportedType() { - verifyUnsupportedType(EAP_REQUEST_SIM_START_PACKET, EAP_RESPONSE_NAK_PACKET); - - verifyEapPrimeAkaIdentity(); - verifyEapAkaPrimeChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_PRIME_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaPrimeSynchronizationFailure() { - verifyEapPrimeAkaIdentity(); - verifyEapAkaPrimeSynchronizationFailure(); - verifyEapAkaPrimeChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_PRIME_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaPrimeAuthenticationReject() { - verifyEapPrimeAkaIdentity(); - - // return null from TelephonyManager to simluate rejection of AUTN - verifyEapAkaPrimeChallenge(null, EAP_AKA_PRIME_AUTHENTICATION_REJECT); - verifyEapFailure(); - } - - @Test - public void testEapAkaPrimeMismatchedNetworkNamesNotAllowed() { - // use mismatched peer network name - setUp(false, PEER_NETWORK_NAME_2); - verifyEapPrimeAkaIdentity(); - verifyEapAkaPrimeChallengeMismatchedNetworkNames(); - verifyEapFailure(); - } - - @Test - public void testEapAkaPrimeMismatchedNetworkNamesAllowed() { - setUp(true, PEER_NETWORK_NAME_2); - verifyEapPrimeAkaIdentity(); - verifyEapAkaPrimeChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_PRIME_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - private void verifyEapPrimeAkaIdentity() { - // EAP-AKA'/Identity request - when(mMockTelephonyManager.getSubscriberId()).thenReturn(UNFORMATTED_IDENTITY); - - mEapAuthenticator.processEapMessage(EAP_AKA_PRIME_IDENTITY_REQUEST); - mTestLooper.dispatchAll(); - - // verify EAP-AKA'/Identity response - verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE)); - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - verify(mMockTelephonyManager).getSubscriberId(); - verify(mMockCallback).onResponse(eq(EAP_AKA_PRIME_IDENTITY_RESPONSE)); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaPrimeChallenge( - String challengeBase64, - String responseBase64, - byte[] incomingEapPacket, - byte[] outgoingEapPacket) { - // EAP-AKA'/Challenge request - when(mMockTelephonyManager.getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - challengeBase64)) - .thenReturn(responseBase64); - - mEapAuthenticator.processEapMessage(incomingEapPacket); - mTestLooper.dispatchAll(); - - // verify EAP-AKA'/Challenge response - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - challengeBase64); - verify(mMockCallback).onResponse(eq(outgoingEapPacket)); - } - - private void verifyEapAkaPrimeChallenge(String responseBase64, byte[] outgoingPacket) { - verifyEapAkaPrimeChallenge( - BASE64_CHALLENGE_1, - responseBase64, - EAP_AKA_PRIME_CHALLENGE_REQUEST, - outgoingPacket); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaPrimeChallengeWithoutIdentityReq() { - verifyEapAkaPrimeChallenge( - BASE64_CHALLENGE_1, - BASE_64_RESPONSE_SUCCESS, - EAP_AKA_PRIME_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ, - EAP_AKA_PRIME_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQUEST); - - // also need to verify interactions with Context and TelephonyManager - verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE)); - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaPrimeSynchronizationFailure() { - verifyEapAkaPrimeChallenge( - BASE64_CHALLENGE_2, - BASE_64_RESPONSE_SYNC_FAIL, - EAP_AKA_PRIME_CHALLENGE_REQUEST_SYNC_FAIL, - EAP_AKA_PRIME_SYNC_FAIL_RESPONSE); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaPrimeChallengeMismatchedNetworkNames() { - // EAP-AKA'/Challenge request - mEapAuthenticator.processEapMessage(EAP_AKA_PRIME_CHALLENGE_REQUEST); - mTestLooper.dispatchAll(); - verify(mMockCallback).onResponse(eq(EAP_AKA_PRIME_AUTHENTICATION_REJECT)); - } - - @Override - protected void verifyEapSuccess(byte[] msk, byte[] emsk) { - super.verifyEapSuccess(msk, emsk); - - verifyNoMoreInteractions(mMockTelephonyManager); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapAkaTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapAkaTest.java deleted file mode 100644 index e982a85d..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapAkaTest.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.eap.EapSessionConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.statemachine.EapStateMachine; - -import org.junit.Before; -import org.junit.Test; - -/** - * This test verifies that EAP-AKA is functional for an end-to-end implementation - */ -public class EapAkaTest extends EapMethodEndToEndTest { - private static final long AUTHENTICATOR_TIMEOUT_MILLIS = 250L; - - private static final int SUB_ID = 1; - private static final String UNFORMATTED_IDENTITY = "123456789ABCDEF"; // IMSI - - // EAP_IDENTITY = hex("test@android.net") - private static final byte[] EAP_IDENTITY = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - // TODO(b/140797965): find valid AUTN/RAND values for the CTS test sim - // IK: 7320EE404E055EF2B5AB0F86E96C48BE - // CK: E9D1707652E13BF3E05975F601678E5C - // MK: 2AE8AD50432246E6ACED9AA0FC794A22CE9CE4BB - // K_encr: DB6F06910D5D19CC9DA5F2687F5C5737 - // K_aut: B20A586592796E08E7408FB53356E9B1 - private static final String RAND_1 = "D6A296F030A305601B311D38A004505C"; - private static final String RAND_2 = "000102030405060708090A0B0C0D0E0F"; - private static final String AUTN = "35A9143ED9E100011795E785DAFAAD9B"; - private static final String RES = "E5167A255FDCDE9248AF6B50ADA0D944"; - private static final String AUTS = "0102030405060708090A0B0C0D0E"; - private static final byte[] MSK = - hexStringToByteArray( - "EFC4FB9F54D99A3F4A04B756993CA813" - + "E463CA0ADBF3CB2A296519ED4C600FF5" - + "81898B1C425C20FE7471FC43A4BB3C00" - + "DDF80A7083972B660BC7153CBF2C9AA1"); - private static final byte[] EMSK = - hexStringToByteArray( - "5C95F3E2476ED4D6588CE6DE2618D808" - + "9ECA12A4636C8A1B0C678562CBFC31D3" - + "94B578DE0A3686E17F96F14D5341FE75" - + "2012944CA394E5288BA1B2C70CB65063"); - - // IK: 7320EE404E055EF2B5AB0F86E96C48BE - // CK: E9D1707652E13BF3E05975F601678E5C - // MK: 8183017CD8ADDB4617F4A2274DD5BCEA99354FB7 - // K_encr: 891D5DB8CACAF657D68BE72371F927A2 - // K_aut: E042A1CC5672358685EC012881EA02DE - private static final byte[] MSK_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "629DE03704E15EF1B8BADFF7FA5D84D5" - + "8574B6A3A46F274796346A86AE3455AC" - + "711E2D4D3F96EE71E664B1B947D7E9E7" - + "D227CBB6199A68BD7D43E6E4863D08D6"); - private static final byte[] EMSK_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "30A6638AE3AB5C5D29554D8256C3A287" - + "FDF6255E4D726C0622DDF89609C16A8D" - + "563768166A8111A083547DE4C8E280D6" - + "113A608DE9227FC7C02679A1E04DB3CF"); - - // Base 64 of: [Length][RAND_1][Length][AUTN] - private static final String BASE64_CHALLENGE_1 = - "ENailvAwowVgGzEdOKAEUFwQNakUPtnhAAEXleeF2vqtmw=="; - - // Base 64 of: ['DB'][Length][RES][Length][IK][Length][CK] - private static final String BASE_64_RESPONSE_SUCCESS = - "2xDlFnolX9zekkiva1CtoNlEEHMg7kBOBV7ytasPhulsSL4Q6dFwdlLhO/PgWXX2AWeOXA=="; - - // Base 64 of: [Length][RAND_2][Length][AUTN] - private static final String BASE64_CHALLENGE_2 = - "EAABAgMEBQYHCAkKCwwNDg8QNakUPtnhAAEXleeF2vqtmw=="; - - // Base 64 of: ['DC'][Length][AUTS] - private static final String BASE_64_RESPONSE_SYNC_FAIL = "3A4BAgMEBQYHCAkKCwwNDg=="; - - private static final String REQUEST_MAC = "90C3554783D49A18F9EAA231F3C261EC"; - private static final String RESPONSE_MAC = "D085987D3D15FA50A80D0CECFA2412EB"; - private static final String REQUEST_MAC_WITHOUT_IDENTITY_REQ = - "6AD7E3F43ED99384E751F55AB8EA48B4"; - private static final String RESPONSE_MAC_WITHOUT_IDENTITY_REQ = - "83E9F5B8B44BDE39B50538BF49864209"; - - private static final byte[] EAP_AKA_IDENTITY_REQUEST = - hexStringToByteArray( - "01CD000C" // EAP-Request | ID | length in bytes - + "17050000" // EAP-AKA | Identity | 2B padding - + "0D010000"); // AT_ANY_ID_REQ attribute - private static final byte[] EAP_AKA_IDENTITY_RESPONSE = - hexStringToByteArray( - "02CD001C" // EAP-Response | ID | length in bytes - + "17050000" // EAP-AKA | Identity | 2B padding - + "0E05001030313233343536373839414243444546"); // AT_IDENTITY attribute - - private static final byte[] EAP_AKA_CHALLENGE_REQUEST = - hexStringToByteArray( - "01CE0044" // EAP-Request | ID | length in bytes - + "17010000" // EAP-AKA | Challenge | 2B padding - + "01050000" + RAND_1 // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "0B050000" + REQUEST_MAC); // AT_MAC attribute - private static final byte[] EAP_AKA_CHALLENGE_RESPONSE = - hexStringToByteArray( - "02CE0030" // EAP-Response | ID | length in bytes - + "17010000" // EAP-AKA | Challenge | 2B padding - + "03050080" + RES // AT_RES attribute - + "0B050000" + RESPONSE_MAC); // AT_MAC attribute - - private static final byte[] EAP_AKA_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "01CE0044" // EAP-Request | ID | length in bytes - + "17010000" // EAP-AKA | Challenge | 2B padding - + "01050000" + RAND_1 // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "0B050000" + REQUEST_MAC_WITHOUT_IDENTITY_REQ); // AT_MAC attribute - private static final byte[] EAP_AKA_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQUEST = - hexStringToByteArray( - "02CE0030" // EAP-Response | ID | length in bytes - + "17010000" // EAP-AKA | Challenge | 2B padding - + "03050080" + RES // AT_RES attribute - + "0B050000" + RESPONSE_MAC_WITHOUT_IDENTITY_REQ); // AT_MAC attribute - - private static final byte[] EAP_AKA_CHALLENGE_REQUEST_SYNC_FAIL = - hexStringToByteArray( - "01CE0044" // EAP-Request | ID | length in bytes - + "17010000" // EAP-AKA | Challenge | 2B padding - + "01050000" + RAND_2 // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "0B050000" + REQUEST_MAC); // AT_MAC attribute - private static final byte[] EAP_AKA_SYNC_FAIL_RESPONSE = - hexStringToByteArray( - "02CE0018" // EAP-Response | ID | length in bytes - + "17040000" // EAP-AKA | Synchronization-Failure | 2B padding - + "0404" + AUTS); // AT_AUTS attribute - - private static final byte[] EAP_AKA_AUTHENTICATION_REJECT = - hexStringToByteArray( - "02CE0008" // EAP-Response | ID | length in bytes - + "17020000"); // EAP-AKA | Authentication-Reject | 2B padding - - private static final byte[] EAP_RESPONSE_NAK_PACKET = - hexStringToByteArray("021000060317"); // NAK with EAP-AKA listed - - private TelephonyManager mMockTelephonyManager; - - @Before - @Override - public void setUp() { - super.setUp(); - - mMockTelephonyManager = mock(TelephonyManager.class); - - mEapSessionConfig = - new EapSessionConfig.Builder() - .setEapIdentity(EAP_IDENTITY) - .setEapAkaConfig(SUB_ID, APPTYPE_USIM) - .build(); - mEapAuthenticator = - new EapAuthenticator( - mTestLooper.getLooper(), - mMockCallback, - new EapStateMachine(mMockContext, mEapSessionConfig, mMockSecureRandom), - (runnable) -> runnable.run(), - AUTHENTICATOR_TIMEOUT_MILLIS); - - when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE)) - .thenReturn(mMockTelephonyManager); - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - } - - @Test - public void testEapAkaEndToEnd() { - verifyEapAkaIdentity(); - verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaEndToEndWithoutIdentityRequest() { - verifyEapAkaChallengeWithoutIdentityReq(); - verifyEapSuccess(MSK_WITHOUT_IDENTITY_REQ, EMSK_WITHOUT_IDENTITY_REQ); - } - - @Test - public void testEapAkaWithEapNotifications() { - verifyEapNotification(1); - verifyEapAkaIdentity(); - - verifyEapNotification(2); - verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE); - - verifyEapNotification(3); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaUnsupportedType() { - verifyUnsupportedType(EAP_REQUEST_SIM_START_PACKET, EAP_RESPONSE_NAK_PACKET); - - verifyEapAkaIdentity(); - verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaSynchronizationFailure() { - verifyEapAkaIdentity(); - verifyEapAkaSynchronizationFailure(); - verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapAkaAuthenticationReject() { - verifyEapAkaIdentity(); - - // return null from TelephonyManager to simluate rejection of AUTN - verifyEapAkaChallenge(null, EAP_AKA_AUTHENTICATION_REJECT); - verifyEapFailure(); - } - - private void verifyEapAkaIdentity() { - // EAP-AKA/Identity request - when(mMockTelephonyManager.getSubscriberId()).thenReturn(UNFORMATTED_IDENTITY); - - mEapAuthenticator.processEapMessage(EAP_AKA_IDENTITY_REQUEST); - mTestLooper.dispatchAll(); - - // verify EAP-AKA/Identity response - verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE)); - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - verify(mMockTelephonyManager).getSubscriberId(); - verify(mMockCallback).onResponse(eq(EAP_AKA_IDENTITY_RESPONSE)); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaChallenge( - String challengeBase64, - String responseBase64, - byte[] incomingEapPacket, - byte[] outgoingEapPacket) { - // EAP-AKA/Challenge request - when(mMockTelephonyManager.getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - challengeBase64)) - .thenReturn(responseBase64); - - mEapAuthenticator.processEapMessage(incomingEapPacket); - mTestLooper.dispatchAll(); - - // verify EAP-AKA/Challenge response - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - challengeBase64); - verify(mMockCallback).onResponse(eq(outgoingEapPacket)); - } - - private void verifyEapAkaChallenge(String responseBase64, byte[] outgoingPacket) { - verifyEapAkaChallenge( - BASE64_CHALLENGE_1, responseBase64, EAP_AKA_CHALLENGE_REQUEST, outgoingPacket); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaChallengeWithoutIdentityReq() { - verifyEapAkaChallenge( - BASE64_CHALLENGE_1, - BASE_64_RESPONSE_SUCCESS, - EAP_AKA_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ, - EAP_AKA_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQUEST); - - // also need to verify interactions with Context and TelephonyManager - verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE)); - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - private void verifyEapAkaSynchronizationFailure() { - verifyEapAkaChallenge( - BASE64_CHALLENGE_2, - BASE_64_RESPONSE_SYNC_FAIL, - EAP_AKA_CHALLENGE_REQUEST_SYNC_FAIL, - EAP_AKA_SYNC_FAIL_RESPONSE); - verifyNoMoreInteractions( - mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback); - } - - @Override - protected void verifyEapSuccess(byte[] msk, byte[] emsk) { - super.verifyEapSuccess(msk, emsk); - - verifyNoMoreInteractions(mMockTelephonyManager); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapAuthenticatorTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapAuthenticatorTest.java deleted file mode 100644 index 4c868a03..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapAuthenticatorTest.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_FAILURE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_RESPONSE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SUCCESS_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.REQUEST_UNSUPPORTED_TYPE_PACKET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.os.Looper; -import android.os.test.TestLooper; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.statemachine.EapStateMachine; - -import org.junit.Before; -import org.junit.Test; - -import java.util.concurrent.TimeoutException; - -public class EapAuthenticatorTest { - private static final long AUTHENTICATOR_TIMEOUT_MILLIS = 250L; - private static final long TEST_TIMEOUT_MILLIS = 2 * AUTHENTICATOR_TIMEOUT_MILLIS; - private static final byte[] MSK = hexStringToByteArray( - "00112233445566778899AABBCCDDEEFF" - + "00112233445566778899AABBCCDDEEFF" - + "00112233445566778899AABBCCDDEEFF" - + "00112233445566778899AABBCCDDEEFF"); - private static final byte[] EMSK = hexStringToByteArray( - "FFEEDDCCBBAA99887766554433221100" - + "FFEEDDCCBBAA99887766554433221100" - + "FFEEDDCCBBAA99887766554433221100" - + "FFEEDDCCBBAA99887766554433221100"); - - private EapStateMachine mMockEapStateMachine; - - private TestLooper mTestLooper; - private boolean mCallbackFired; - - @Before - public void setUp() { - if (Looper.myLooper() == null) Looper.prepare(); - - mMockEapStateMachine = mock(EapStateMachine.class); - - mTestLooper = new TestLooper(); - mCallbackFired = false; - } - - @Test - public void testProcessEapMessageResponse() { - EapCallback eapCallback = new EapCallback() { - @Override - public void onResponse(byte[] eapMsg) { - assertArrayEquals(EAP_SIM_RESPONSE_PACKET, eapMsg); - assertFalse("Callback has already been fired", mCallbackFired); - mCallbackFired = true; - } - }; - - EapResponse eapResponse = new EapResponse(EAP_SIM_RESPONSE_PACKET); - when(mMockEapStateMachine.process(eq(EAP_REQUEST_SIM_START_PACKET))) - .thenReturn(eapResponse); - - getEapAuthenticatorWithCallback(eapCallback) - .processEapMessage(EAP_REQUEST_SIM_START_PACKET); - mTestLooper.dispatchAll(); - - assertTrue("Callback didn't fire", mCallbackFired); - verify(mMockEapStateMachine).process(eq(EAP_REQUEST_SIM_START_PACKET)); - verifyNoMoreInteractions(mMockEapStateMachine); - } - - @Test - public void testProcessEapMessageError() { - EapCallback eapCallback = new EapCallback() { - @Override - public void onError(Throwable cause) { - assertTrue(cause instanceof EapInvalidRequestException); - assertFalse("Callback has already been fired", mCallbackFired); - mCallbackFired = true; - } - }; - Exception cause = new EapInvalidRequestException("Error"); - EapError eapError = new EapError(cause); - when(mMockEapStateMachine.process(eq(REQUEST_UNSUPPORTED_TYPE_PACKET))) - .thenReturn(eapError); - - getEapAuthenticatorWithCallback(eapCallback) - .processEapMessage(REQUEST_UNSUPPORTED_TYPE_PACKET); - mTestLooper.dispatchAll(); - - assertTrue("Callback didn't fire", mCallbackFired); - verify(mMockEapStateMachine).process(eq(REQUEST_UNSUPPORTED_TYPE_PACKET)); - verifyNoMoreInteractions(mMockEapStateMachine); - } - - @Test - public void testProcessEapMessageSuccess() { - EapCallback eapCallback = new EapCallback() { - @Override - public void onSuccess(byte[] msk, byte[] emsk) { - assertArrayEquals(MSK, msk); - assertArrayEquals(EMSK, emsk); - assertFalse("Callback has already been fired", mCallbackFired); - mCallbackFired = true; - } - }; - EapSuccess eapSuccess = new EapSuccess(MSK, EMSK); - when(mMockEapStateMachine.process(eq(EAP_SUCCESS_PACKET))) - .thenReturn(eapSuccess); - - getEapAuthenticatorWithCallback(eapCallback) - .processEapMessage(EAP_SUCCESS_PACKET); - mTestLooper.dispatchAll(); - - assertTrue("Callback didn't fire", mCallbackFired); - verify(mMockEapStateMachine).process(eq(EAP_SUCCESS_PACKET)); - verifyNoMoreInteractions(mMockEapStateMachine); - } - - @Test - public void testProcessEapMessageFailure() { - EapCallback eapCallback = new EapCallback() { - @Override - public void onFail() { - // nothing to check here - assertFalse("Callback has already been fired", mCallbackFired); - mCallbackFired = true; - } - }; - when(mMockEapStateMachine.process(eq(EAP_FAILURE_PACKET))) - .thenReturn(new EapFailure()); - - getEapAuthenticatorWithCallback(eapCallback) - .processEapMessage(EAP_FAILURE_PACKET); - mTestLooper.dispatchAll(); - - assertTrue("Callback didn't fire", mCallbackFired); - verify(mMockEapStateMachine).process(eq(EAP_FAILURE_PACKET)); - verifyNoMoreInteractions(mMockEapStateMachine); - } - - @Test - public void testProcessEapMessageExceptionThrown() { - EapCallback eapCallback = new EapCallback() { - @Override - public void onError(Throwable cause) { - assertTrue(cause instanceof NullPointerException); - assertFalse("Callback has already been fired", mCallbackFired); - mCallbackFired = true; - } - }; - when(mMockEapStateMachine.process(EAP_REQUEST_SIM_START_PACKET)) - .thenThrow(new NullPointerException()); - - getEapAuthenticatorWithCallback(eapCallback) - .processEapMessage(EAP_REQUEST_SIM_START_PACKET); - mTestLooper.dispatchAll(); - - assertTrue("Callback didn't fire", mCallbackFired); - verify(mMockEapStateMachine).process(eq(EAP_REQUEST_SIM_START_PACKET)); - verifyNoMoreInteractions(mMockEapStateMachine); - } - - @Test - public void testProcessEapMessageStateMachineTimeout() { - EapCallback eapCallback = new EapCallback() { - @Override - public void onError(Throwable cause) { - assertTrue(cause instanceof TimeoutException); - assertFalse("Callback has already been fired", mCallbackFired); - mCallbackFired = true; - } - }; - EapResponse eapResponse = new EapResponse(EAP_SIM_RESPONSE_PACKET); - when(mMockEapStateMachine.process(eq(EAP_REQUEST_SIM_START_PACKET))) - .then((invocation) -> { - // move time forward to trigger the timeout - mTestLooper.moveTimeForward(TEST_TIMEOUT_MILLIS); - return eapResponse; - }); - - getEapAuthenticatorWithCallback(eapCallback) - .processEapMessage(EAP_REQUEST_SIM_START_PACKET); - mTestLooper.dispatchAll(); - - assertTrue("Callback didn't fire", mCallbackFired); - verify(mMockEapStateMachine).process(eq(EAP_REQUEST_SIM_START_PACKET)); - verifyNoMoreInteractions(mMockEapStateMachine); - } - - private EapAuthenticator getEapAuthenticatorWithCallback(EapCallback eapCallback) { - return new EapAuthenticator( - mTestLooper.getLooper(), - eapCallback, - mMockEapStateMachine, - (runnable) -> runnable.run(), - AUTHENTICATOR_TIMEOUT_MILLIS); - } - - /** - * Default {@link IEapCallback} implementation that throws {@link UnsupportedOperationException} - * for all calls. - */ - private abstract static class EapCallback implements IEapCallback { - @Override - public void onSuccess(byte[] msk, byte[] emsk) { - throw new UnsupportedOperationException(); - } - - @Override - public void onFail() { - throw new UnsupportedOperationException(); - } - - @Override - public void onResponse(byte[] eapMsg) { - throw new UnsupportedOperationException(); - } - - @Override - public void onError(Throwable cause) { - throw new UnsupportedOperationException(); - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapErrorTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapErrorTest.java deleted file mode 100644 index ce497635..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapErrorTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static org.junit.Assert.assertEquals; - -import com.android.internal.net.eap.EapResult.EapError; - -import org.junit.Test; - -public class EapErrorTest { - private static final RuntimeException EXPECTED_EXCEPTION = new RuntimeException("expected"); - - @Test - public void testEapErrorConstructor() { - EapError eapError = new EapError(EXPECTED_EXCEPTION); - assertEquals(EXPECTED_EXCEPTION, eapError.cause); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapMethodEndToEndTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapMethodEndToEndTest.java deleted file mode 100644 index 412a4cf1..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapMethodEndToEndTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_FAILURE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SUCCESS; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import android.content.Context; -import android.net.eap.EapSessionConfig; -import android.os.test.TestLooper; - -import org.junit.Before; - -import java.security.SecureRandom; - -public class EapMethodEndToEndTest { - protected Context mMockContext; - protected SecureRandom mMockSecureRandom; - protected IEapCallback mMockCallback; - - protected TestLooper mTestLooper; - protected EapSessionConfig mEapSessionConfig; - protected EapAuthenticator mEapAuthenticator; - - @Before - public void setUp() { - mMockContext = mock(Context.class); - mMockSecureRandom = mock(SecureRandom.class); - mMockCallback = mock(IEapCallback.class); - - mTestLooper = new TestLooper(); - } - - protected void verifyUnsupportedType(byte[] invalidMessageType, byte[] nakResponse) { - mEapAuthenticator.processEapMessage(invalidMessageType); - mTestLooper.dispatchAll(); - - // verify EAP-Response/Nak returned - verify(mMockCallback).onResponse(eq(nakResponse)); - verifyNoMoreInteractions(mMockCallback); - } - - protected void verifyEapNotification(int callsToVerify) { - mEapAuthenticator.processEapMessage(EAP_REQUEST_NOTIFICATION_PACKET); - mTestLooper.dispatchAll(); - - verify(mMockCallback, times(callsToVerify)) - .onResponse(eq(EAP_RESPONSE_NOTIFICATION_PACKET)); - verifyNoMoreInteractions(mMockCallback); - } - - protected void verifyEapSuccess(byte[] msk, byte[] emsk) { - // EAP-Success - mEapAuthenticator.processEapMessage(EAP_SUCCESS); - mTestLooper.dispatchAll(); - - // verify that onSuccess callback made - verify(mMockCallback).onSuccess(eq(msk), eq(emsk)); - verifyNoMoreInteractions(mMockContext, mMockSecureRandom, mMockCallback); - } - - protected void verifyEapFailure() { - mEapAuthenticator.processEapMessage(EAP_FAILURE_PACKET); - mTestLooper.dispatchAll(); - - verify(mMockCallback).onFail(); - verifyNoMoreInteractions(mMockCallback); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapMsChapV2Test.java b/tests/iketests/src/java/com/android/internal/net/eap/EapMsChapV2Test.java deleted file mode 100644 index 01264f9d..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapMsChapV2Test.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_AKA_IDENTITY_PACKET; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import android.net.eap.EapSessionConfig; - -import com.android.internal.net.eap.statemachine.EapStateMachine; - -import org.junit.Before; -import org.junit.Test; - -public class EapMsChapV2Test extends EapMethodEndToEndTest { - private static final long AUTHENTICATOR_TIMEOUT_MILLIS = 250L; - - private static final String USERNAME = "User"; - private static final String PASSWORD = "clientPass"; - - private static final byte[] PEER_CHALLENGE = - hexStringToByteArray("21402324255E262A28295F2B3A337C7E"); - private static final byte[] MSK = - hexStringToByteArray( - "D5F0E9521E3EA9589645E86051C822268B7CDC149B993A1BA118CB153F56DCCB"); - - // Server-Name = hex("authenticator@android.net") - private static final byte[] EAP_MSCHAP_V2_CHALLENGE_REQUEST = - hexStringToByteArray("01110033" // EAP-Request | ID | length in bytes - + "1A0142" // EAP-MSCHAPv2 | Request | MSCHAPv2 ID - + "002E10" // MS length | Value Size (0x10) - + "5B5D7C7D7B3F2F3E3C2C602132262628" // Authenticator-Challenge - + "61757468656E74696361746F7240616E64726F69642E6E6574"); // Server-Name - private static final byte[] EAP_MSCHAP_V2_CHALLENGE_RESPONSE = - hexStringToByteArray("0211003F" // EAP-Response | ID | length in bytes - + "1A0242" // EAP-MSCHAPv2 | Response | MSCHAPv2 ID - + "003A31" // MS length | Value Size (0x31) - + "21402324255E262A28295F2B3A337C7E" // Peer-Challenge - + "0000000000000000" // 8B (reserved) - + "82309ECD8D708B5EA08FAA3981CD83544233114A3D85D6DF" // NT-Response - + "00" // Flags - + "55736572"); // hex(USERNAME) - private static final byte[] EAP_MSCHAP_V2_SUCCESS_REQUEST = - hexStringToByteArray("01120047" // EAP-Request | ID | length in bytes - + "1A03420042" // EAP-MSCHAPv2 | Success | MSCHAPv2 ID | MS length - + "533D" // hex("S=") - + "3430374135353839313135464430443632303946" - + "3531304645394330343536363933324344413536" // hex("<auth_string>") - + "204D3D" // hex(" M=") - + "7465737420416E64726F69642031323334"); // hex("test Android 1234") - private static final byte[] EAP_MSCHAP_V2_SUCCESS_RESPONSE = - hexStringToByteArray("02120006" // EAP-Response | ID | length in bytes - + "1A03"); // EAP-MSCHAPv2 | Success - private static final byte[] EAP_MSCHAP_V2_FAILURE_REQUEST = - hexStringToByteArray("01130049" // EAP-Request | ID | length in bytes - + "1A04420044" // EAP-MSCHAPv2 | Failure | MSCHAPv2 ID | MS length - + "453D363437" // hex("E=647") - + "20523D31" // hex(" R=1") - + "20433D" // hex(" C=") - + "30303031303230333034303530363037" - + "30383039304130423043304430453046" // hex("<authenticator challenge>") - + "20563D33" // hex(" V=3") - + "204D3D" // hex(" M=") - + "7465737420416E64726F69642031323334"); // hex("test Android 1234") - private static final byte[] EAP_MSCHAP_V2_FAILURE_RESPONSE = - hexStringToByteArray("02130006" // EAP-Response | ID | length in bytes - + "1A04"); // EAP-MSCHAPv2 | Failure - - private static final byte[] EAP_RESPONSE_NAK_PACKET = hexStringToByteArray("02100006031A"); - - @Before - @Override - public void setUp() { - super.setUp(); - - mEapSessionConfig = - new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build(); - mEapAuthenticator = - new EapAuthenticator( - mTestLooper.getLooper(), - mMockCallback, - new EapStateMachine(mMockContext, mEapSessionConfig, mMockSecureRandom), - (runnable) -> runnable.run(), - AUTHENTICATOR_TIMEOUT_MILLIS); - } - - @Test - public void testEapMsChapV2EndToEndSuccess() { - verifyEapMsChapV2Challenge(); - verifyEapMsChapV2SuccessRequest(); - verifyEapSuccess(MSK, new byte[0]); - } - - @Test - public void testEapMsChapV2EndToEndFailure() { - verifyEapMsChapV2Challenge(); - verifyEapMsChapV2FailureRequest(); - verifyEapFailure(); - } - - @Test - public void testEapMsChapV2UnsupportedType() { - verifyUnsupportedType(EAP_REQUEST_AKA_IDENTITY_PACKET, EAP_RESPONSE_NAK_PACKET); - - verifyEapMsChapV2Challenge(); - verifyEapMsChapV2SuccessRequest(); - verifyEapSuccess(MSK, new byte[0]); - } - - @Test - public void verifyEapMsChapV2WithEapNotifications() { - verifyEapNotification(1); - - verifyEapMsChapV2Challenge(); - verifyEapNotification(2); - - verifyEapMsChapV2SuccessRequest(); - verifyEapNotification(3); - - verifyEapSuccess(MSK, new byte[0]); - } - - private void verifyEapMsChapV2Challenge() { - doAnswer(invocation -> { - byte[] dst = invocation.getArgument(0); - System.arraycopy(PEER_CHALLENGE, 0, dst, 0, PEER_CHALLENGE.length); - return null; - }).when(mMockSecureRandom).nextBytes(eq(new byte[PEER_CHALLENGE.length])); - - mEapAuthenticator.processEapMessage(EAP_MSCHAP_V2_CHALLENGE_REQUEST); - mTestLooper.dispatchAll(); - - verify(mMockCallback).onResponse(eq(EAP_MSCHAP_V2_CHALLENGE_RESPONSE)); - verify(mMockSecureRandom).nextBytes(any(byte[].class)); - verifyNoMoreInteractions(mMockCallback); - } - - private void verifyEapMsChapV2SuccessRequest() { - mEapAuthenticator.processEapMessage(EAP_MSCHAP_V2_SUCCESS_REQUEST); - mTestLooper.dispatchAll(); - - verify(mMockCallback).onResponse(eq(EAP_MSCHAP_V2_SUCCESS_RESPONSE)); - verifyNoMoreInteractions(mMockCallback); - } - - private void verifyEapMsChapV2FailureRequest() { - mEapAuthenticator.processEapMessage(EAP_MSCHAP_V2_FAILURE_REQUEST); - mTestLooper.dispatchAll(); - - verify(mMockCallback).onResponse(eq(EAP_MSCHAP_V2_FAILURE_RESPONSE)); - verifyNoMoreInteractions(mMockCallback); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapResponseTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapResponseTest.java deleted file mode 100644 index 0c6dd52d..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapResponseTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NAK_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SUCCESS_PACKET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.InvalidEapResponseException; -import com.android.internal.net.eap.message.EapMessage; - -import org.junit.Before; -import org.junit.Test; - -public class EapResponseTest { - private EapMessage mEapResponse; - private EapMessage mEapSuccess; - - @Before - public void setUp() throws Exception { - mEapResponse = EapMessage.decode(EAP_RESPONSE_NAK_PACKET); - mEapSuccess = EapMessage.decode(EAP_SUCCESS_PACKET); - } - - @Test - public void testGetEapResponse() { - EapResult eapResult = EapResponse.getEapResponse(mEapResponse); - assertTrue(eapResult instanceof EapResponse); - - EapResponse eapResponse = (EapResponse) eapResult; - assertArrayEquals(EAP_RESPONSE_NAK_PACKET, eapResponse.packet); - } - - @Test - public void testGetEapResponseNullMessage() { - try { - EapResponse.getEapResponse(null); - fail("Expected IllegalArgumentException for null EapMessage"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testGetEapResponseNonRequestMessage() { - EapResult eapResult = EapResponse.getEapResponse(mEapSuccess); - assertTrue(eapResult instanceof EapError); - - EapError eapError = (EapError) eapResult; - assertTrue(eapError.cause instanceof InvalidEapResponseException); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapSimTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapSimTest.java deleted file mode 100644 index 8b11d278..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapSimTest.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_AKA_IDENTITY_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NAK_PACKET; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.eap.EapSessionConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.statemachine.EapStateMachine; - -import org.junit.Before; -import org.junit.Test; - -/** - * This test verifies that EAP-SIM is functional for an end-to-end implementation - */ -public class EapSimTest extends EapMethodEndToEndTest { - private static final long AUTHENTICATOR_TIMEOUT_MILLIS = 250L; - - private static final byte[] NONCE = hexStringToByteArray("37f3ddd3954c4831a5ee08c574844398"); - private static final String UNFORMATTED_IDENTITY = "123456789ABCDEF"; // IMSI - - // EAP_IDENTITY = hex("test@android.net") - private static final byte[] EAP_IDENTITY = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - private static final int SUB_ID = 1; - - // Base 64 of: RAND - private static final String BASE64_RAND_1 = "EAEjRWeJq83vESNFZ4mrze8="; - private static final String BASE64_RAND_2 = "EBEjRWeJq83vESNFZ4mrze8="; - private static final String BASE64_RAND_3 = "ECEjRWeJq83vESNFZ4mrze8="; - - // BASE 64 of: "04" + SRES + "08" + KC - // SRES 1: 0ABCDEF0 KC 1: FEDCBA9876543210 - // SRES 2: 1ABCDEF1 KC 2: FEDCBA9876543211 - // SRES 3: 2ABCDEF2 KC 3: FEDCBA9876543212 - private static final String BASE64_RESP_1 = "BAq83vAI/ty6mHZUMhA="; - private static final String BASE64_RESP_2 = "BBq83vEI/ty6mHZUMhE="; - private static final String BASE64_RESP_3 = "BCq83vII/ty6mHZUMhI="; - - // MK: 202FC68A3335E8A939A33BC0A0EA8C435DC10060 - // K_encr: F63E152461391FF655C2632E35D076ED - // K_aut: 48E001C8DBA37120FD0465153A56F712 - private static final byte[] MSK = - hexStringToByteArray( - "9B1E2B6892BC113F6B6D0B5789DD8ADD" - + "B83BE2A84AA50FCAECD0003F92D8DA16" - + "4BF983C923695C309F1D7D68DB6992B0" - + "76EA8CE7129647A6F198F3A6AA8ADED9"); - private static final byte[] EMSK = hexStringToByteArray( - "88210b6724400313539c740f417076b0" - + "41da7e64658ec365bd2901a7cd7c2763" - + "dad1a0508b92a42fdf85ac53c6f7e756" - + "7f99b62bcaf467441b567f19b58d86ae"); - - // MK: ED275A588A4C1AEC15C55261DCCD851189E5C5FD - // K_encr: FED573CFA6FC81267C08E264F50A0BB9 - // K_aut: 277B5D6A68FE5156A387996510AC5D61 - private static final byte[] MSK_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "8023A49840433464DA1A4F2457FAB3D6" - + "B1A3CA6E5E1DB212FA1AEA17F0A5C933" - + "5541DE7448FE448AC3F09DC25BBAE1EE" - + "17DCE3D32099519CC75840F0E3FB612B"); - private static final byte[] EMSK_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "F7E213F0E8F14A21C87F9B5DFADA9A75" - + "A8EAF4AD718BF8C3ED6557BDB60E4671" - + "E6AE109448B2F32F9B984667AE6C2B3F" - + "2FDFE67F97AF4D4727A2EA37F06B7785"); - - private static final byte[] EAP_SIM_START_REQUEST = hexStringToByteArray( - "01850014120a0000" // EAP header - + "0f02000200010000" // AT_VERSION_LIST attribute - + "0d010000"); // AT_ANY_ID_REQ attribute - private static final byte[] EAP_SIM_START_RESPONSE = hexStringToByteArray( - "02850034120a0000" // EAP header - + "0705000037f3ddd3954c4831a5ee08c574844398" // AT_NONCE_MT attribute - + "10010001" // AT_SELECTED_VERSION attribute - + "0e05001031313233343536373839414243444546"); // AT_IDENTITY attribute - private static final byte[] EAP_SIM_CHALLENGE_REQUEST = hexStringToByteArray( - "01860050120b0000" // EAP header - + "010d0000" // AT_RAND attribute - + "0123456789abcdef1123456789abcdef" // Rand 1 - + "1123456789abcdef1123456789abcdef" // Rand 2 - + "2123456789abcdef1123456789abcdef" // Rand 3 - + "0b050000e4675b17fa7ba4d93db48d1af9ecbb01"); // AT_MAC attribute - private static final byte[] EAP_SIM_CHALLENGE_RESPONSE = - hexStringToByteArray( - "0286001c120b0000" // EAP header - + "0b050000e5df9cb1d935ea5f54d449a038bed061"); // AT_MAC attribute - - private static final byte[] EAP_SIM_START_REQUEST_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "01850010" // EAP-Request | ID | length in bytes - + "120a0000" // EAP-SIM | Start| 2B padding - + "0f02000200010000"); // AT_VERSION_LIST attribute - private static final byte[] EAP_SIM_START_RESPONSE_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "02850020" // EAP-Response | ID | length in bytes - + "120a0000" // EAP-SIM | Start | 2B padding - + "0705000037f3ddd3954c4831a5ee08c574844398" // AT_NONCE_MT attribute - + "10010001"); // AT_SELECTED_VERSION attribute - private static final byte[] EAP_SIM_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "01860050" // EAP-Request | ID | length in bytes - + "120b0000" // EAP-SIM | Challenge | 2B padding - + "010d0000" // AT_RAND attribute - + "0123456789abcdef1123456789abcdef" // Rand 1 - + "1123456789abcdef1123456789abcdef" // Rand 2 - + "2123456789abcdef1123456789abcdef" // Rand 3 - + "0b050000F2F8C10FCA946AAFE9555E2BD3693DF6"); // AT_MAC attribute - private static final byte[] EAP_SIM_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQ = - hexStringToByteArray( - "0286001c" // EAP-Response | ID | length in bytes - + "120b0000" // EAP-SIM | Challenge | 2B padding - + "0b050000DAC3C1B7D9DBFBC923464A94F186E410"); // AT_MAC attribute - - private TelephonyManager mMockTelephonyManager; - - @Before - @Override - public void setUp() { - super.setUp(); - - mMockTelephonyManager = mock(TelephonyManager.class); - - mEapSessionConfig = - new EapSessionConfig.Builder() - .setEapIdentity(EAP_IDENTITY) - .setEapSimConfig(SUB_ID, APPTYPE_USIM) - .build(); - mEapAuthenticator = - new EapAuthenticator( - mTestLooper.getLooper(), - mMockCallback, - new EapStateMachine(mMockContext, mEapSessionConfig, mMockSecureRandom), - (runnable) -> runnable.run(), - AUTHENTICATOR_TIMEOUT_MILLIS); - } - - @Test - public void testEapSimEndToEnd() { - verifyEapSimStart(EAP_SIM_START_REQUEST, EAP_SIM_START_RESPONSE, true); - verifyEapSimChallenge(EAP_SIM_CHALLENGE_REQUEST, EAP_SIM_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void testEapSimEndToEndWithoutIdentityRequest() { - verifyEapSimStart( - EAP_SIM_START_REQUEST_WITHOUT_IDENTITY_REQ, - EAP_SIM_START_RESPONSE_WITHOUT_IDENTITY_REQ, - false); - verifyEapSimChallenge( - EAP_SIM_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ, - EAP_SIM_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQ); - verifyEapSuccess(MSK_WITHOUT_IDENTITY_REQ, EMSK_WITHOUT_IDENTITY_REQ); - } - - @Test - public void testEapSimUnsupportedType() { - verifyUnsupportedType(EAP_REQUEST_AKA_IDENTITY_PACKET, EAP_RESPONSE_NAK_PACKET); - - verifyEapSimStart(EAP_SIM_START_REQUEST, EAP_SIM_START_RESPONSE, true); - verifyEapSimChallenge(EAP_SIM_CHALLENGE_REQUEST, EAP_SIM_CHALLENGE_RESPONSE); - verifyEapSuccess(MSK, EMSK); - } - - @Test - public void verifyEapSimWithEapNotifications() { - verifyEapNotification(1); - verifyEapSimStart(EAP_SIM_START_REQUEST, EAP_SIM_START_RESPONSE, true); - - verifyEapNotification(2); - verifyEapSimChallenge(EAP_SIM_CHALLENGE_REQUEST, EAP_SIM_CHALLENGE_RESPONSE); - verifyEapNotification(3); - verifyEapSuccess(MSK, EMSK); - } - - private void verifyEapSimStart( - byte[] incomingEapPacket, byte[] outgoingEapPacket, boolean expectIdentityRequest) { - // EAP-SIM/Start request - when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE)) - .thenReturn(mMockTelephonyManager); - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(UNFORMATTED_IDENTITY); - doAnswer(invocation -> { - byte[] dst = invocation.getArgument(0); - System.arraycopy(NONCE, 0, dst, 0, NONCE.length); - return null; - }).when(mMockSecureRandom).nextBytes(eq(new byte[NONCE.length])); - - mEapAuthenticator.processEapMessage(incomingEapPacket); - mTestLooper.dispatchAll(); - verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE)); - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - - if (expectIdentityRequest) { - verify(mMockTelephonyManager).getSubscriberId(); - } - - verify(mMockSecureRandom).nextBytes(any(byte[].class)); - - // verify EAP-SIM/Start response - verify(mMockCallback).onResponse(eq(outgoingEapPacket)); - verifyNoMoreInteractions( - mMockContext, - mMockTelephonyManager, - mMockSecureRandom, - mMockCallback); - } - - private void verifyEapSimChallenge(byte[] incomingEapPacket, byte[] outgoingEapPacket) { - // EAP-SIM/Challenge request - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE64_RAND_1)) - .thenReturn(BASE64_RESP_1); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE64_RAND_2)) - .thenReturn(BASE64_RESP_2); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE64_RAND_3)) - .thenReturn(BASE64_RESP_3); - - mEapAuthenticator.processEapMessage(incomingEapPacket); - mTestLooper.dispatchAll(); - - // verify EAP-SIM/Challenge response - verify(mMockTelephonyManager) - .getIccAuthentication( - eq(TelephonyManager.APPTYPE_USIM), - eq(TelephonyManager.AUTHTYPE_EAP_SIM), - eq(BASE64_RAND_1)); - verify(mMockTelephonyManager) - .getIccAuthentication( - eq(TelephonyManager.APPTYPE_USIM), - eq(TelephonyManager.AUTHTYPE_EAP_SIM), - eq(BASE64_RAND_2)); - verify(mMockTelephonyManager) - .getIccAuthentication( - eq(TelephonyManager.APPTYPE_USIM), - eq(TelephonyManager.AUTHTYPE_EAP_SIM), - eq(BASE64_RAND_3)); - verify(mMockCallback).onResponse(eq(outgoingEapPacket)); - verifyNoMoreInteractions( - mMockContext, - mMockTelephonyManager, - mMockSecureRandom, - mMockCallback); - } - - @Override - protected void verifyEapSuccess(byte[] msk, byte[] emsk) { - super.verifyEapSuccess(msk, emsk); - - verifyNoMoreInteractions(mMockTelephonyManager); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapSuccessTest.java b/tests/iketests/src/java/com/android/internal/net/eap/EapSuccessTest.java deleted file mode 100644 index d7410540..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapSuccessTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.EapResult.EapSuccess; - -import org.junit.Test; - -public class EapSuccessTest { - public static final byte[] MSK = new byte[] {(byte) 1, (byte) 2, (byte) 3}; - public static final byte[] EMSK = new byte[] {(byte) 4, (byte) 5, (byte) 6}; - - @Test - public void testEapSuccessConstructor() { - EapSuccess eapSuccess = new EapSuccess(MSK, EMSK); - assertArrayEquals(MSK, eapSuccess.msk); - assertArrayEquals(EMSK, eapSuccess.emsk); - } - - @Test - public void testEapSuccessConstructorNullMsk() { - try { - new EapSuccess(null, EMSK); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testEapSuccessConstructorNullEmsk() { - try { - new EapSuccess(MSK, null); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/EapTestUtils.java b/tests/iketests/src/java/com/android/internal/net/eap/EapTestUtils.java deleted file mode 100644 index de058d15..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/EapTestUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import android.net.eap.EapSessionConfig; - -import java.util.HashMap; - -/** - * EapTestUtils is a util class for providing test-values of EAP-related objects. - */ -public class EapTestUtils { - /** - * Creates and returns a dummy EapSessionConfig instance. - * - * @return a new, empty EapSessionConfig instance - */ - public static EapSessionConfig getDummyEapSessionConfig() { - return new EapSessionConfig(new HashMap<>(), new byte[0]); - } - - /** - * Creates and returns a dummy EapSessionConfig instance with the given EAP-Identity. - * - * @param eapIdentity byte-array representing the EAP-Identity of the client - * @return a new, empty EapSessionConfig instance with the given EAP-Identity - */ - public static EapSessionConfig getDummyEapSessionConfig(byte[] eapIdentity) { - return new EapSessionConfig(new HashMap<>(), eapIdentity); - } - - /** - * Creates and returns a dummy EapSessionConfig instance with EAP-SIM configured. - * - * @return a new EapSessionConfig with EAP-SIM configs set - */ - public static EapSessionConfig getDummyEapSimSessionConfig() { - return new EapSessionConfig.Builder().setEapSimConfig(0, APPTYPE_USIM).build(); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/crypto/Fips186_2PrfTest.java b/tests/iketests/src/java/com/android/internal/net/eap/crypto/Fips186_2PrfTest.java deleted file mode 100644 index 978f070f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/crypto/Fips186_2PrfTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.eap.crypto; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.fail; - -import com.android.internal.net.TestUtils; - -import org.junit.Before; -import org.junit.Test; - -/** - * The test vectors in this file come directly from NIST: - * - * <p>The original link to the test vectors was here: - * http://csrc.nist.gov/publications/fips/fips186-2/fips186-2-change1.pdf - * - * <p>But has since been removed. A cached copy was found here: - * https://web.archive.org/web/20041031202951/http://www.csrc.nist.gov/CryptoToolkit/dss/Examples- - * 1024bit.pdf - */ -public final class Fips186_2PrfTest { - private static final String SEED = "bd029bbe7f51960bcf9edb2b61f06f0feb5a38b6"; - private static final String EXPECTED_RESULT = - "2070b3223dba372fde1c0ffc7b2e3b498b2606143c6c18bacb0f6c55babb13788e20d737a3275116"; - - private Fips186_2Prf mFipsPrf; - - @Before - public void setUp() { - mFipsPrf = new Fips186_2Prf(); - } - - @Test - public void testFips186_2Prf_Invalid_Seed() throws Exception { - try { - mFipsPrf.getRandom(new byte[0], 40); - fail("Expected exception for invalid length seed"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testFips186_2Prf() throws Exception { - byte[] seed = TestUtils.hexStringToByteArray(SEED); - byte[] actual = mFipsPrf.getRandom(seed, 40); - - assertArrayEquals(TestUtils.hexStringToByteArray(EXPECTED_RESULT), actual); - } - - // TODO: (b/136177143) Add more test vectors -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/crypto/HmacSha256ByteSignerTest.java b/tests/iketests/src/java/com/android/internal/net/eap/crypto/HmacSha256ByteSignerTest.java deleted file mode 100644 index 355c1db1..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/crypto/HmacSha256ByteSignerTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.crypto; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; - -import static org.junit.Assert.assertArrayEquals; - -import org.junit.Before; -import org.junit.Test; - -/** - * HmacSha256ByteSignerTest tests that {@link HmacSha256ByteSigner} correctly signs data using the - * HMAC-SHA-256 algorithm. - * - * <p>These test vectors are defined in RFC 4231#4. - * - * @see <a href="https://tools.ietf.org/html/rfc4231#section-4">Test Vectors</a> - */ -public class HmacSha256ByteSignerTest { - private static final String[] KEYS = { - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "4a656665", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "0102030405060708090a0b0c0d0e0f10111213141516171819", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaa", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - + "aaaaaa" - }; - private static final String[] DATA = { - "4869205468657265", - "7768617420646f2079612077616e7420666f72206e6f7468696e673f", - "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" - + "dddddddddddddddddddddddddddddddddddd", - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", - "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" - + "65204b6579202d2048617368204b6579204669727374", - "5468697320697320612074657374207573696e672061206c6172676572207468" - + "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" - + "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" - + "647320746f20626520686173686564206265666f7265206265696e6720757365" - + "642062792074686520484d414320616c676f726974686d2e" - }; - private static final String[] MACS = { - "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", - "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", - "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", - "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", - "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", - "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2" - }; - - private HmacSha256ByteSigner mMacByteSigner; - - @Before - public void setUp() { - mMacByteSigner = HmacSha256ByteSigner.getInstance(); - } - - @Test - public void testSignBytes() { - for (int i = 0; i < KEYS.length; i++) { - byte[] key = hexStringToByteArray(KEYS[i]); - byte[] data = hexStringToByteArray(DATA[i]); - - byte[] expected = hexStringToByteArray(MACS[i]); - - assertArrayEquals(expected, mMacByteSigner.signBytes(key, data)); - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/crypto/ParityBitUtilTest.java b/tests/iketests/src/java/com/android/internal/net/eap/crypto/ParityBitUtilTest.java deleted file mode 100644 index 7ba024e5..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/crypto/ParityBitUtilTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.crypto; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -/** - * Inputs taken from RFC 2759 Section 9.3. - * - * @see <a href="https://tools.ietf.org/html/rfc2759#section-9.3">RFC 2759 Section 9.3, Examples of - * DES Key Generation</a> - */ -public class ParityBitUtilTest { - private static final byte[] INPUT_BYTES = {(byte) 0xFC, (byte) 0x0E, (byte) 0x7E, (byte) 0x37}; - private static final byte[] OUTPUT_BYTES = {(byte) 0xFD, (byte) 0x0E, (byte) 0x7F, (byte) 0x37}; - - private static final String[] RAW_KEYS = {"FC156AF7EDCD6C", "0EDDE3337D427F"}; - private static final long[] RAW_KEY_LONGS = {0xFC156AF7EDCD6CL, 0xEDDE3337D427FL}; - private static final String[] PARITY_CORRECTED_KEYS = {"FD0B5B5E7F6E34D9", "0E6E796737EA08FE"}; - - @Test - public void testGetByteWithParityBit() { - for (int i = 0; i < INPUT_BYTES.length; i++) { - assertEquals(OUTPUT_BYTES[i], ParityBitUtil.getByteWithParityBit(INPUT_BYTES[i])); - } - } - - @Test - public void testByteArrayToLong() { - for (int i = 0; i < RAW_KEYS.length; i++) { - byte[] rawKey = hexStringToByteArray(RAW_KEYS[i]); - - assertEquals(RAW_KEY_LONGS[i], ParityBitUtil.byteArrayToLong(rawKey)); - } - } - - @Test - public void testAddParityBits() { - for (int i = 0; i < RAW_KEYS.length; i++) { - byte[] rawKey = hexStringToByteArray(RAW_KEYS[i]); - byte[] parityCorrectedKey = hexStringToByteArray(PARITY_CORRECTED_KEYS[i]); - - assertArrayEquals(parityCorrectedKey, ParityBitUtil.addParityBits(rawKey)); - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/EapDataTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/EapDataTest.java deleted file mode 100644 index 9d4cc7ca..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/EapDataTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class EapDataTest { - private static final byte[] EXPECTED_EAP_DATA_BYTES = new byte[] { - EAP_TYPE_SIM, - (byte) 1, - (byte) 2, - (byte) 3 - }; - private static final byte[] EAP_TYPE_DATA = new byte[] {(byte) 1, (byte) 2, (byte) 3}; - private static final int UNSUPPORTED_EAP_TYPE = -1; - - @Test - public void testEapDataConstructor() { - new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - } - - @Test - public void testEapDataConstructorNullEapData() { - try { - new EapData(EAP_TYPE_SIM, null); - fail("IllegalArgumentException expected for null eapTypeData"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testEapDataConstructorUnsupportedType() { - try { - new EapData(UNSUPPORTED_EAP_TYPE, EAP_TYPE_DATA); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testGetLength() { - EapData eapData = new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - assertEquals(EAP_TYPE_DATA.length + 1, eapData.getLength()); - } - - @Test - public void testEquals() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - EapData eapDataCopy = new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - assertEquals(eapData, eapDataCopy); - - EapData eapDataDifferent = new EapData(EAP_TYPE_SIM, new byte[0]); - assertNotEquals(eapData, eapDataDifferent); - } - - @Test - public void testHashCode() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - EapData eapDataCopy = new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - assertNotEquals(0, eapData.hashCode()); - assertEquals(eapData.hashCode(), eapDataCopy.hashCode()); - } - - @Test - public void testEncodeToByteBuffer() { - EapData eapData = new EapData(EAP_TYPE_SIM, EAP_TYPE_DATA); - - ByteBuffer b = ByteBuffer.allocate(eapData.getLength()); - eapData.encodeToByteBuffer(b); - assertArrayEquals(EXPECTED_EAP_DATA_BYTES, b.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/EapMessageTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/EapMessageTest.java deleted file mode 100644 index 813a30f7..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/EapMessageTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_NAK; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_RESPONSE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_TYPE_DATA; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NAK_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SUCCESS_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.INCOMPLETE_HEADER_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.INVALID_CODE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.LONG_SUCCESS_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.REQUEST_MISSING_TYPE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.REQUEST_UNSUPPORTED_TYPE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SHORT_PACKET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidPacketLengthException; -import com.android.internal.net.eap.exceptions.InvalidEapCodeException; -import com.android.internal.net.eap.exceptions.UnsupportedEapTypeException; - -import org.junit.Test; - -import java.util.Arrays; - -public class EapMessageTest { - @Test - public void testConstructorRequestWithoutType() throws Exception { - try { - new EapMessage(EAP_CODE_REQUEST, ID_INT, null); - fail("Expected EapInvalidPacketLengthException for an EAP-Request without Type value"); - } catch (EapInvalidPacketLengthException expected) { - } - } - - @Test - public void testDecode() throws Exception { - EapMessage result = EapMessage.decode(EAP_SUCCESS_PACKET); - assertEquals(EAP_CODE_SUCCESS, result.eapCode); - assertEquals(ID_INT, result.eapIdentifier); - assertEquals(EAP_SUCCESS_PACKET.length, result.eapLength); - assertNull(result.eapData); - - EapData expectedEapData = new EapData(EAP_TYPE_SIM, - hexStringToByteArray(EAP_REQUEST_SIM_TYPE_DATA)); - result = EapMessage.decode(EAP_REQUEST_SIM_START_PACKET); - assertEquals(EAP_CODE_REQUEST, result.eapCode); - assertEquals(ID_INT, result.eapIdentifier); - assertEquals(EAP_REQUEST_SIM_START_PACKET.length, result.eapLength); - assertEquals(expectedEapData, result.eapData); - } - - @Test - public void testDecodeInvalidCode() throws Exception { - try { - EapMessage.decode(INVALID_CODE_PACKET); - fail("Expected InvalidEapCodeException"); - } catch (InvalidEapCodeException expected) { - } - } - - @Test - public void testDecodeIncompleteHeader() throws Exception { - try { - EapMessage.decode(INCOMPLETE_HEADER_PACKET); - fail("Expected EapInvalidPacketLengthException"); - } catch (EapInvalidPacketLengthException expected) { - } - } - - @Test - public void testDecodeShortPacket() throws Exception { - try { - EapMessage.decode(SHORT_PACKET); - fail("Expected EapInvalidPacketLengthException"); - } catch (EapInvalidPacketLengthException expected) { - } - } - - @Test - public void testDecodeSuccessIncorrectLength() throws Exception { - try { - EapMessage.decode(LONG_SUCCESS_PACKET); - fail("Expected EapInvalidPacketLengthException"); - } catch (EapInvalidPacketLengthException expected) { - } - } - - @Test - public void testDecodeMissingTypeData() throws Exception { - try { - EapMessage.decode(REQUEST_MISSING_TYPE_PACKET); - fail("Expected EapInvalidPacketLengthException"); - } catch (EapInvalidPacketLengthException expected) { - } - } - - @Test - public void testDecodeUnsupportedEapType() throws Exception { - try { - EapMessage.decode(REQUEST_UNSUPPORTED_TYPE_PACKET); - fail("Expected UnsupportedEapDataTypeException"); - } catch (UnsupportedEapTypeException expected) { - assertEquals(ID_INT, expected.eapIdentifier); - } - } - - @Test - public void testEncode() throws Exception { - EapMessage eapMessage = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - byte[] actualPacket = eapMessage.encode(); - assertArrayEquals(EAP_SUCCESS_PACKET, actualPacket); - - EapData nakData = new EapData(EAP_NAK, new byte[] {EAP_TYPE_SIM}); - eapMessage = new EapMessage(EAP_CODE_RESPONSE, ID_INT, nakData); - actualPacket = eapMessage.encode(); - assertArrayEquals(EAP_RESPONSE_NAK_PACKET, actualPacket); - } - - @Test - public void testEncodeDecode() throws Exception { - EapMessage eapMessage = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - EapMessage result = EapMessage.decode(eapMessage.encode()); - - assertEquals(eapMessage.eapCode, result.eapCode); - assertEquals(eapMessage.eapIdentifier, result.eapIdentifier); - assertEquals(eapMessage.eapLength, result.eapLength); - assertEquals(eapMessage.eapData, result.eapData); - } - - @Test - public void testDecodeEncode() throws Exception { - byte[] result = EapMessage.decode(EAP_REQUEST_SIM_START_PACKET).encode(); - assertArrayEquals(EAP_REQUEST_SIM_START_PACKET, result); - } - - @Test - public void testGetNakResponse() { - EapResult nakResponse = EapMessage.getNakResponse(ID_INT, Arrays.asList(EAP_TYPE_SIM)); - - assertTrue(nakResponse instanceof EapResponse); - EapResponse eapResponse = (EapResponse) nakResponse; - assertArrayEquals(EAP_RESPONSE_NAK_PACKET, eapResponse.packet); - } - - @Test - public void testGetNotificationResponse() { - EapResult notificationResponse = EapMessage.getNotificationResponse(ID_INT); - - assertTrue(notificationResponse instanceof EapResponse); - EapResponse eapResponse = (EapResponse) notificationResponse; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/EapTestMessageDefinitions.java b/tests/iketests/src/java/com/android/internal/net/eap/message/EapTestMessageDefinitions.java deleted file mode 100644 index 20517583..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/EapTestMessageDefinitions.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_VERSION_LIST_DATA; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.IDENTITY_STRING; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_MT_STRING; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_2; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RES; - -/** - * EapTestMessageDefinitions provides byte[] encodings of commonly used EAP Messages. - * - * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication - * Protocol (EAP)</a> - */ -public class EapTestMessageDefinitions { - public static final String ID = "10"; - public static final int ID_INT = Integer.parseInt(ID, 16 /* radix */); - - // EAP-AKA Identity request - public static final String EAP_REQUEST_TYPE_DATA = "0500000D010000"; - public static final byte[] EAP_AKA_IDENTITY_REQUEST = - hexStringToByteArray(EAP_REQUEST_TYPE_DATA); - - // EAP-AKA/Identity request with no attributes - public static final byte[] EAP_REQUEST_AKA = hexStringToByteArray("01" + ID + "000817050000"); - public static final byte[] EAP_REQUEST_AKA_IDENTITY_PACKET = - hexStringToByteArray("01" + ID + "000A17" + EAP_REQUEST_TYPE_DATA); - public static final byte[] EAP_REQUEST_IDENTITY_PACKET = - hexStringToByteArray("01" + ID + "000501"); - - // EAP-Identity: hex for ASCII in "test@android.net" - public static final String EAP_IDENTITY_STRING = "7465737440616E64726F69642E6E6574"; - public static final byte[] EAP_IDENTITY = hexStringToByteArray(EAP_IDENTITY_STRING); - public static final byte[] EAP_RESPONSE_IDENTITY_PACKET = - hexStringToByteArray("02" + ID + "001501" + EAP_IDENTITY_STRING); - public static final byte[] EAP_RESPONSE_IDENTITY_DEFAULT_PACKET = - hexStringToByteArray("02" + ID + "000501"); - public static final byte[] EAP_REQUEST_NOTIFICATION_PACKET = - hexStringToByteArray("01" + ID + "000802AABBCC"); - public static final byte[] EAP_SUCCESS_PACKET = hexStringToByteArray("03" + ID + "0004"); - public static final byte[] EAP_FAILURE_PACKET = hexStringToByteArray("04" + ID + "0004"); - public static final byte[] EAP_SIM_CLIENT_ERROR_RESPONSE = - hexStringToByteArray("02" + ID + "000C120E000016010001"); - public static final byte[] EAP_SIM_CLIENT_ERROR_INSUFFICIENT_CHALLENGES = - hexStringToByteArray("02" + ID + "000C120E000016010002"); - public static final byte[] EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS = - hexStringToByteArray("02" + ID + "000C120E000016010000"); - public static final byte[] EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS = - hexStringToByteArray("02" + ID + "000C170E000016010000"); - - // EAP-SIM response containing SELECTED_VERSION (1) and IDENTITY attributes - public static final byte[] EAP_SIM_RESPONSE_PACKET = hexStringToByteArray( - "02" + ID + "0024120A0000100100010E060011" + IDENTITY_STRING + "000000"); - public static final byte[] EAP_SIM_RESPONSE_WITHOUT_IDENTITY = - hexStringToByteArray("02" + ID + "0020120A000007050000" + NONCE_MT_STRING + "10010001"); - public static final byte[] EAP_SIM_NOTIFICATION_RESPONSE = hexStringToByteArray( - "02" + ID + "0008120C0000"); - public static final byte[] EAP_AKA_NOTIFICATION_RESPONSE = - hexStringToByteArray("02" + ID + "0008170C0000"); - - // Body of EapData is the list of supported methods - public static final byte[] EAP_RESPONSE_NAK_PACKET = - hexStringToByteArray("02" + ID + "00060312"); - public static final byte[] EAP_RESPONSE_NOTIFICATION_PACKET = - hexStringToByteArray("02" + ID + "000502"); - public static final byte[] EAP_REQUEST_MD5_CHALLENGE = - hexStringToByteArray("01" + ID + "000504"); - public static final byte[] EAP_REQUEST_NAK_PACKET = - hexStringToByteArray("01" + ID + "000503"); - public static final String EAP_REQUEST_SIM_TYPE_DATA = "0A00000F02000200010000"; - public static final byte[] EAP_REQUEST_SIM_START_PACKET = - hexStringToByteArray("01" + ID + "001012" + EAP_REQUEST_SIM_TYPE_DATA); - - public static final byte[] REQUEST_UNSUPPORTED_TYPE_PACKET = - hexStringToByteArray("01" + ID + "0005FF"); - public static final byte[] REQUEST_MISSING_TYPE_PACKET = - hexStringToByteArray("01" + ID + "0004"); - public static final byte[] LONG_SUCCESS_PACKET = hexStringToByteArray("03" + ID + "000500"); - public static final byte[] SHORT_PACKET = hexStringToByteArray("01" + ID + "0005"); - public static final byte[] INCOMPLETE_HEADER_PACKET = hexStringToByteArray("03" + ID); - public static final byte[] INVALID_CODE_PACKET = hexStringToByteArray("F0" + ID + "0004"); - - // Attributes - public static final String SKIPPABLE_DATA = "112233445566"; - public static final byte[] SKIPPABLE_DATA_BYTES = hexStringToByteArray(SKIPPABLE_DATA); - public static final byte[] SKIPPABLE_INVALID_ATTRIBUTE = - hexStringToByteArray("FF02" + SKIPPABLE_DATA); - public static final byte[] NON_SKIPPABLE_INVALID_ATTRIBUTE = - hexStringToByteArray("7F010000"); - - // Type-Data - public static final byte[] EAP_SIM_START_SUBTYPE = - hexStringToByteArray("0A00000F02" + AT_VERSION_LIST_DATA + "0A010000"); - public static final byte[] INVALID_SUBTYPE = hexStringToByteArray("FF"); - public static final byte[] TYPE_DATA_INVALID_AT_RAND = - hexStringToByteArray("0A000001050000" + RAND_1); - public static final byte[] SHORT_TYPE_DATA = hexStringToByteArray("0A"); - public static final byte[] TYPE_DATA_INVALID_ATTRIBUTE = - hexStringToByteArray("0A00007F01"); - public static final byte[] EAP_SIM_START_DUPLICATE_ATTRIBUTES = - hexStringToByteArray("0A00000F02" + "0A010000" + "0A010000"); - - // RAND Challenge Results - public static final String SRES_1 = "11223344"; - public static final byte[] SRES_1_BYTES = hexStringToByteArray(SRES_1); - public static final String SRES_2 = "44332211"; - public static final byte[] SRES_2_BYTES = hexStringToByteArray(SRES_2); - public static final byte[] SRES_BYTES = hexStringToByteArray(SRES_1 + SRES_2); - public static final String KC_1 = "0102030405060708"; - public static final byte[] KC_1_BYTES = hexStringToByteArray(KC_1); - public static final String KC_2 = "0807060504030201"; - public static final byte[] KC_2_BYTES = hexStringToByteArray(KC_2); - public static final byte[] VALID_CHALLENGE_RESPONSE = - hexStringToByteArray("04" + SRES_1 + "08" + KC_1); - public static final byte[] CHALLENGE_RESPONSE_INVALID_SRES = hexStringToByteArray("03"); - public static final byte[] CHALLENGE_RESPONSE_INVALID_KC = - hexStringToByteArray("04" + SRES_1 + "04"); - - public static final String IMSI = "123456789012345"; - public static final String EAP_SIM_IDENTITY = "1" + IMSI; - public static final byte[] EAP_SIM_IDENTITY_BYTES = hexStringToByteArray(EAP_SIM_IDENTITY); - - // ASCII hex for "0" + IMSI (EAP-AKA identity format) - public static final String EAP_AKA_IDENTITY_BYTES = "30313233343536373839303132333435"; - - // Master Key generation - public static final String MK_STRING = "0123456789ABCDEF0123456789ABCDEF01234567"; - public static final byte[] MK = hexStringToByteArray(MK_STRING); - public static final String K_ENCR_STRING = "000102030405060708090A0B0C0D0E0F"; - public static final byte[] K_ENCR = hexStringToByteArray(K_ENCR_STRING); - public static final String K_AUT_STRING = "0F0E0D0C0B0A09080706050403020100"; - public static final byte[] K_AUT = hexStringToByteArray(K_AUT_STRING); - public static final String MSK_STRING = - "00112233445566778899AABBCCDDEEFF" - + "00112233445566778899AABBCCDDEEFF" - + "00112233445566778899AABBCCDDEEFF" - + "00112233445566778899AABBCCDDEEFF"; - public static final byte[] MSK = hexStringToByteArray(MSK_STRING); - public static final String EMSK_STRING = - "FFEEDDCCBBAA99887766554433221100" - + "FFEEDDCCBBAA99887766554433221100" - + "FFEEDDCCBBAA99887766554433221100" - + "FFEEDDCCBBAA99887766554433221100"; - public static final byte[] EMSK = hexStringToByteArray(EMSK_STRING); - - // MAC computation - public static final String ORIGINAL_MAC_STRING = "112233445566778899AABBCCDDEEFF11"; - public static final byte[] ORIGINAL_MAC = hexStringToByteArray(ORIGINAL_MAC_STRING); - public static final String COMPUTED_MAC_STRING = "FFEEDDCCBBAA998877665544332211FF"; - public static final byte[] COMPUTED_MAC = hexStringToByteArray(COMPUTED_MAC_STRING); - public static final String EAP_SIM_CHALLENGE_REQUEST_STRING = - "01" + ID + "0040" // EAP-Request | ID | length in bytes - + "120b0000" // EAP-SIM | Challenge | 2B padding - + "01090000" + RAND_1 + RAND_2 // EAP-SIM AT_RAND attribute - + "0B05000000000000000000000000000000000000"; // AT_MAC attribute with no MAC - public static final byte[] MAC_INPUT = - hexStringToByteArray(EAP_SIM_CHALLENGE_REQUEST_STRING + NONCE_MT_STRING); - - // Response Message with MAC - public static final String EAP_SIM_CHALLENGE_RESPONSE_EMPTY_MAC = - "02" + ID + "001C" // EAP-Response | ID | length in bytes - + "120b0000" // EAP-SIM | Challenge | 2B padding - + "0B05000000000000000000000000000000000000"; // AT_MAC attribute with no MAC - public static final byte[] EAP_SIM_CHALLENGE_RESPONSE_MAC_INPUT = - hexStringToByteArray(EAP_SIM_CHALLENGE_RESPONSE_EMPTY_MAC + SRES_1 + SRES_2); - public static final byte[] EAP_SIM_CHALLENGE_RESPONSE_WITH_MAC = hexStringToByteArray( - "02" + ID + "001C" // EAP-Response | ID | length in bytes - + "120b0000" // EAP-SIM | Challenge | 2B padding - + "0B050000" + COMPUTED_MAC_STRING); // AT_MAC attribute - public static final byte[] EAP_SIM_NOTIFICATION_REQUEST_WITH_EMPTY_MAC = hexStringToByteArray( - "01" + ID + "0020" // EAP-Request | ID | length in bytes - + "120C0000" // EAP-SIM | Notification | 2B padding - + "0C010000" // AT_NOTIFICATION attribute - + "0B05000000000000000000000000000000000000"); // empty AT_MAC attribute - public static final byte[] EAP_SIM_NOTIFICATION_RESPONSE_WITH_EMPTY_MAC = hexStringToByteArray( - "02" + ID + "001C" // EAP-Response | ID | length in bytes - + "120C0000" // EAP-SIM | Notification | 2B padding - + "0B05000000000000000000000000000000000000"); // empty AT_MAC attribute - public static final byte[] EAP_SIM_NOTIFICATION_RESPONSE_WITH_MAC = hexStringToByteArray( - "02" + ID + "001C" // EAP-Response | ID | length in bytes - + "120C0000" // EAP-SIM | Notification | 2B padding - + "0B050000" + COMPUTED_MAC_STRING); // AT_MAC attribute - - public static final byte[] EAP_AKA_IDENTITY_RESPONSE = - hexStringToByteArray("02" + ID + "001C" // EAP-Response | ID | length in bytes - + "17050000" // EAP-AKA | Identity | 2B padding - + "0E050010" + EAP_AKA_IDENTITY_BYTES); // AT_IDENTITY ("0" + IMSI) - - // Base64 of: FF0111 - public static final String EAP_AKA_UICC_RESP_INVALID_TAG = "/wER"; - - // Base64 of: DC0E112233445566778899AABBCCDDEE - public static final String EAP_AKA_UICC_RESP_SYNCHRONIZE_BASE_64 = "3A4RIjNEVWZ3iJmqu8zd7g=="; - - public static final byte[] EAP_AKA_SYNCHRONIZATION_FAILURE = - hexStringToByteArray("02" + ID + "0018" // EAP-Response | ID | length in bytes - + "17040000" // EAP-SIM | Synchronization-Failure | 2B padding - + "0404112233445566778899AABBCCDDEE"); // AT_AUTS attribute - - public static final String IK = "00112233445566778899AABBCCDDEEFF"; - public static final byte[] IK_BYTES = hexStringToByteArray(IK); - public static final String CK = "FFEEDDCCBBAA99887766554433221100"; - public static final byte[] CK_BYTES = hexStringToByteArray(CK); - - // Base-64 of: 'DB05' + RES_BYTES + '10' + IK + '10' + CK - // 'DB0511223344551000112233445566778899AABBCCDDEEFF10FFEEDDCCBBAA99887766554433221100' - public static final String EAP_AKA_UICC_RESP_SUCCESS_BASE_64 = - "2wURIjNEVRAAESIzRFVmd4iZqrvM3e7/EP/u3cy7qpmId2ZVRDMiEQA="; - - public static final byte[] EAP_AKA_AUTHENTICATION_REJECT = - hexStringToByteArray("02" + ID + "000817020000"); - public static final String EAP_AKA_CHALLENGE_RESPONSE_MAC = "C70366512D9C5EBA8E3484509A25DCE4"; - public static final byte[] EAP_AKA_CHALLENGE_RESPONSE_MAC_BYTES = - hexStringToByteArray(EAP_AKA_CHALLENGE_RESPONSE_MAC); - public static final byte[] EAP_AKA_CHALLENGE_RESPONSE_TYPE_DATA = - hexStringToByteArray( - "01000003030028" + RES + "0000000B050000" + EAP_AKA_CHALLENGE_RESPONSE_MAC); - public static final byte[] EAP_AKA_CHALLENGE_RESPONSE = - hexStringToByteArray( - "02100028" // EAP-Response | ID | length in bytes - + "17010000" // EAP-AKA | Challenge | 2B padding - + "03030028" + RES + "000000" // AT_RES attribute - + "0B050000" + EAP_AKA_CHALLENGE_RESPONSE_MAC); // AT_MAC attribute - - public static final byte[] EAP_SUCCESS = hexStringToByteArray("03860004"); - - public static final byte[] EAP_REQUEST_MSCHAP_V2 = - hexStringToByteArray("01" + ID + "00061A01"); - - // MSCHAPv2 Test vectors taken from RFC 2759#9.2 and RFC 3079#3.5.3 - public static final String MSCHAP_V2_USERNAME = "User"; - public static final String MSCHAP_V2_USERNAME_HEX = "55736572"; - public static final byte[] MSCHAP_V2_USERNAME_ASCII_BYTES = - hexStringToByteArray(MSCHAP_V2_USERNAME_HEX); - public static final String MSCHAP_V2_PASSWORD = "clientPass"; - public static final byte[] MSCHAP_V2_PASSWORD_UTF_BYTES = - hexStringToByteArray("63006C00690065006E0074005000610073007300"); - public static final String MSCHAP_V2_AUTHENTICATOR_CHALLENGE_STRING = - "5B5D7C7D7B3F2F3E3C2C602132262628"; - public static final byte[] MSCHAP_V2_AUTHENTICATOR_CHALLENGE = - hexStringToByteArray(MSCHAP_V2_AUTHENTICATOR_CHALLENGE_STRING); - public static final String MSCHAP_V2_PEER_CHALLENGE_STRING = "21402324255E262A28295F2B3A337C7E"; - public static final byte[] MSCHAP_V2_PEER_CHALLENGE = - hexStringToByteArray(MSCHAP_V2_PEER_CHALLENGE_STRING); - public static final byte[] MSCHAP_V2_CHALLENGE = hexStringToByteArray("D02E4386BCE91226"); - public static final byte[] MSCHAP_V2_PASSWORD_HASH = - hexStringToByteArray("44EBBA8D5312B8D611474411F56989AE"); - public static final byte[] MSCHAP_V2_PASSWORD_HASH_HASH = - hexStringToByteArray("41C00C584BD2D91C4017A2A12FA59F3F"); - public static final String MSCHAP_V2_NT_RESPONSE_STRING = - "82309ECD8D708B5EA08FAA3981CD83544233114A3D85D6DF"; - public static final byte[] MSCHAP_V2_NT_RESPONSE = - hexStringToByteArray(MSCHAP_V2_NT_RESPONSE_STRING); - public static final byte[] MSCHAP_V2_AUTHENTICATOR_RESPONSE = - hexStringToByteArray("407A5589115FD0D6209F510FE9C04566932CDA56"); - public static final byte[] MSCHAP_V2_MASTER_KEY = - hexStringToByteArray("FDECE3717A8C838CB388E527AE3CDD31"); - - // generated based on RFC 3079#3.5.3 params - public static final String SEND_KEY = "D5F0E9521E3EA9589645E86051C82226"; - public static final byte[] MSCHAP_V2_SEND_START_KEY = hexStringToByteArray(SEND_KEY); - - // This value is labeled 'send key' in RFC 3079#3.5.3. However, it's used as 'receive key' here, - // because send and receive keys are swapped for peers relative to authenticators. - public static final String RECEIVE_KEY = "8B7CDC149B993A1BA118CB153F56DCCB"; - public static final byte[] MSCHAP_V2_RECEIVE_START_KEY = hexStringToByteArray(RECEIVE_KEY); - - // MSK: MSCHAP_V2_SEND_START_KEY + MSCHAP_V2_RECEIVE_START_KEY - public static final byte[] MSCHAP_V2_MSK = hexStringToByteArray(SEND_KEY + RECEIVE_KEY); - - public static final String MSCHAP_V2_ID = "42"; - public static final int MSCHAP_V2_ID_INT = Integer.parseInt(MSCHAP_V2_ID, 16 /* radix */); - public static final byte[] EAP_MSCHAP_V2_CHALLENGE_RESPONSE = - hexStringToByteArray("02" + ID + "003F" // EAP-Response | ID | length in bytes - + "1A02" + MSCHAP_V2_ID // EAP-MSCHAPv2 | Response | MSCHAPv2 ID - + "003A31" // MS length | Value Size (0x31) - + MSCHAP_V2_PEER_CHALLENGE_STRING - + "0000000000000000" // 8B (reserved) - + MSCHAP_V2_NT_RESPONSE_STRING - + "00" // Flags (always 0) - + MSCHAP_V2_USERNAME_HEX); - - public static final byte[] EAP_MSCHAP_V2_SUCCESS_RESPONSE = - hexStringToByteArray("02" + ID + "0006" // EAP-Response | ID | length in bytes - + "1A03"); // EAP-MSCHAPv2 | Success - - public static final byte[] INVALID_AUTHENTICATOR_RESPONSE = new byte[20]; - - public static final byte[] EAP_MSCHAP_V2_FAILURE_RESPONSE = - hexStringToByteArray("02" + ID + "0006" // EAP-Response | ID | length in bytes - + "1A04"); // EAP-MSCHAPv2 | Failure - - public static final byte[] EAP_AKA_PRIME_REQUEST = - hexStringToByteArray("01" + ID + "000832050000"); - public static final byte[] EAP_AKA_PRIME_CLIENT_ERROR_UNABLE_TO_PROCESS = - hexStringToByteArray("02" + ID + "000C320E000016010000"); - public static final String EAP_AKA_PRIME_IDENTITY = "36313233343536373839303132333435"; - public static final byte[] EAP_AKA_PRIME_IDENTITY_BYTES = - hexStringToByteArray(EAP_AKA_PRIME_IDENTITY); - public static final byte[] EAP_AKA_PRIME_IDENTITY_RESPONSE = - hexStringToByteArray( - "02" + ID + "001C" // EAP-Response | ID | length in bytes - + "32050000" // EAP-AKA' | Identity | 2B padding - + "0E050010" + EAP_AKA_PRIME_IDENTITY); // AT_IDENTITY ("6" + IMSI) - public static final byte[] EAP_AKA_PRIME_AUTHENTICATION_REJECT = - hexStringToByteArray( - "02" + ID + "0008" // EAP-Response | ID | length in bytes - + "32020000"); // EAP-AKA' | Authentication Reject | 2B padding -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2ChallengeRequestTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2ChallengeRequestTest.java deleted file mode 100644 index 45a86735..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2ChallengeRequestTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_REQUEST_LONG_MS_LENGTH; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_REQUEST_SHORT_CHALLENGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_REQUEST_SHORT_MS_LENGTH; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_REQUEST_WRONG_OP_CODE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_CHALLENGE_REQUEST; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.ID_INT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SERVER_NAME_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_CHALLENGE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.BufferUnderflowException; - -public class EapMsChapV2ChallengeRequestTest { - private static final String TAG = EapMsChapV2ChallengeRequestTest.class.getSimpleName(); - - private EapMsChapV2TypeDataDecoder mTypeDataDecoder; - - @Before - public void setUp() { - mTypeDataDecoder = new EapMsChapV2TypeDataDecoder(); - } - - @Test - public void testDecodeChallengeRequest() { - DecodeResult<EapMsChapV2ChallengeRequest> result = - mTypeDataDecoder.decodeChallengeRequest(TAG, EAP_MSCHAP_V2_CHALLENGE_REQUEST); - assertTrue(result.isSuccessfulDecode()); - EapMsChapV2ChallengeRequest challengeRequest = result.eapTypeData; - - assertEquals(EAP_MSCHAP_V2_CHALLENGE, challengeRequest.opCode); - assertEquals(ID_INT, challengeRequest.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_CHALLENGE_REQUEST.length, challengeRequest.msLength); - assertArrayEquals(CHALLENGE_BYTES, challengeRequest.challenge); - assertArrayEquals(SERVER_NAME_BYTES, challengeRequest.name); - } - - @Test - public void testDecodeChallengeRequestWrongOpCode() { - DecodeResult<EapMsChapV2ChallengeRequest> result = - mTypeDataDecoder.decodeChallengeRequest(TAG, CHALLENGE_REQUEST_WRONG_OP_CODE); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeChallengeRequestShortChallenge() { - DecodeResult<EapMsChapV2ChallengeRequest> result = - mTypeDataDecoder.decodeChallengeRequest(TAG, CHALLENGE_REQUEST_SHORT_CHALLENGE); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeChallengeRequestShortMsLength() { - DecodeResult<EapMsChapV2ChallengeRequest> result = - mTypeDataDecoder.decodeChallengeRequest(TAG, CHALLENGE_REQUEST_SHORT_MS_LENGTH); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeChallengeRequestLongMsLength() { - DecodeResult<EapMsChapV2ChallengeRequest> result = - mTypeDataDecoder.decodeChallengeRequest(TAG, CHALLENGE_REQUEST_LONG_MS_LENGTH); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof BufferUnderflowException); - } - - @Test - public void testEncodeChallengeRequestFails() throws Exception { - EapMsChapV2ChallengeRequest challengeRequest = - new EapMsChapV2ChallengeRequest( - ID_INT, - EAP_MSCHAP_V2_CHALLENGE_REQUEST.length, - CHALLENGE_BYTES, - SERVER_NAME_BYTES); - try { - challengeRequest.encode(); - fail("Expected UnsupportedOperationException for encoding a Challenge Request"); - } catch (UnsupportedOperationException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2ChallengeResponseTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2ChallengeResponseTest.java deleted file mode 100644 index 3e243a98..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2ChallengeResponseTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_CHALLENGE_RESPONSE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.ID_INT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.NT_RESPONSE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.PEER_CHALLENGE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.PEER_NAME_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SHORT_CHALLENGE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SHORT_NT_RESPONSE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_RESPONSE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeResponse; - -import org.junit.Test; - -public class EapMsChapV2ChallengeResponseTest { - private static final int FLAGS = 0; - private static final int INVALID_FLAGS = 0xFF; - - @Test - public void testConstructor() throws Exception { - EapMsChapV2ChallengeResponse challengeResponse = - new EapMsChapV2ChallengeResponse( - ID_INT, PEER_CHALLENGE_BYTES, NT_RESPONSE_BYTES, FLAGS, PEER_NAME_BYTES); - assertEquals(EAP_MSCHAP_V2_RESPONSE, challengeResponse.opCode); - assertEquals(ID_INT, challengeResponse.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_CHALLENGE_RESPONSE.length, challengeResponse.msLength); - assertArrayEquals(PEER_CHALLENGE_BYTES, challengeResponse.peerChallenge); - assertArrayEquals(NT_RESPONSE_BYTES, challengeResponse.ntResponse); - assertEquals(FLAGS, challengeResponse.flags); - assertArrayEquals(PEER_NAME_BYTES, challengeResponse.name); - } - - @Test - public void testConstructorInvalidChallenge() { - try { - EapMsChapV2ChallengeResponse challengeResponse = - new EapMsChapV2ChallengeResponse( - ID_INT, - SHORT_CHALLENGE_BYTES, - NT_RESPONSE_BYTES, - FLAGS, - PEER_NAME_BYTES); - fail("Expected EapMsChapV2ParsingException for invalid Peer Challenge length"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testConstructorInvalidNtResponse() { - try { - EapMsChapV2ChallengeResponse challengeResponse = - new EapMsChapV2ChallengeResponse( - ID_INT, - PEER_CHALLENGE_BYTES, - SHORT_NT_RESPONSE, - FLAGS, - PEER_NAME_BYTES); - fail("Expected EapMsChapV2ParsingException for invalid NT-Response length"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testConstructorInvalidFlags() { - try { - EapMsChapV2ChallengeResponse challengeResponse = - new EapMsChapV2ChallengeResponse( - ID_INT, - PEER_CHALLENGE_BYTES, - NT_RESPONSE_BYTES, - INVALID_FLAGS, - PEER_NAME_BYTES); - fail("Expected EapMsChapV2ParsingException for non-zero Flags value"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testEncode() throws Exception { - EapMsChapV2ChallengeResponse challengeResponse = - new EapMsChapV2ChallengeResponse( - ID_INT, PEER_CHALLENGE_BYTES, NT_RESPONSE_BYTES, FLAGS, PEER_NAME_BYTES); - byte[] encodedChallengeResponse = challengeResponse.encode(); - - assertArrayEquals(EAP_MSCHAP_V2_CHALLENGE_RESPONSE, encodedChallengeResponse); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2FailureRequestTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2FailureRequestTest.java deleted file mode 100644 index ead8022a..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2FailureRequestTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_FAILURE_REQUEST; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE_WITH_SPACE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.ERROR_CODE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.FAILURE_REQUEST_EXTRA_ATTRIBUTE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.FAILURE_REQUEST_INVALID_CHALLENGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.FAILURE_REQUEST_INVALID_ERROR_CODE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.FAILURE_REQUEST_INVALID_PASSWORD_CHANGE_PROTOCOL; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.FAILURE_REQUEST_SHORT_CHALLENGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.ID_INT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE_MISSING_TEXT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.PASSWORD_CHANGE_PROTOCOL; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.RETRY_BIT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_FAILURE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2FailureRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; - -import org.junit.Before; -import org.junit.Test; - -public class EapMsChapV2FailureRequestTest { - private static final String TAG = EapMsChapV2FailureRequestTest.class.getSimpleName(); - - private EapMsChapV2TypeDataDecoder mTypeDataDecoder; - - @Before - public void setUp() { - mTypeDataDecoder = new EapMsChapV2TypeDataDecoder(); - } - - @Test - public void testDecodeFailureRequest() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest(TAG, EAP_MSCHAP_V2_FAILURE_REQUEST); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2FailureRequest failureRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_FAILURE, failureRequest.opCode); - assertEquals(ID_INT, failureRequest.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_FAILURE_REQUEST.length, failureRequest.msLength); - assertEquals(ERROR_CODE, failureRequest.errorCode); - assertEquals(RETRY_BIT, failureRequest.isRetryable); - assertArrayEquals(CHALLENGE_BYTES, failureRequest.challenge); - assertEquals(PASSWORD_CHANGE_PROTOCOL, failureRequest.passwordChangeProtocol); - assertEquals(MESSAGE, failureRequest.message); - } - - @Test - public void testDecodeFailureRequestMissingMessage() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest( - TAG, EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2FailureRequest failureRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_FAILURE, failureRequest.opCode); - assertEquals(ID_INT, failureRequest.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE.length, failureRequest.msLength); - assertEquals(ERROR_CODE, failureRequest.errorCode); - assertEquals(RETRY_BIT, failureRequest.isRetryable); - assertArrayEquals(CHALLENGE_BYTES, failureRequest.challenge); - assertEquals(PASSWORD_CHANGE_PROTOCOL, failureRequest.passwordChangeProtocol); - assertEquals(MESSAGE_MISSING_TEXT, failureRequest.message); - } - - @Test - public void testDecodeFailureRequestMissingMessageWithSpace() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest( - TAG, EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE_WITH_SPACE); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2FailureRequest failureRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_FAILURE, failureRequest.opCode); - assertEquals(ID_INT, failureRequest.msChapV2Id); - assertEquals( - EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE_WITH_SPACE.length, - failureRequest.msLength); - assertEquals(ERROR_CODE, failureRequest.errorCode); - assertEquals(RETRY_BIT, failureRequest.isRetryable); - assertArrayEquals(CHALLENGE_BYTES, failureRequest.challenge); - assertEquals(PASSWORD_CHANGE_PROTOCOL, failureRequest.passwordChangeProtocol); - assertEquals(MESSAGE_MISSING_TEXT, failureRequest.message); - } - - @Test - public void testDecodeFailureRequestInvalidErrorCode() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest(TAG, FAILURE_REQUEST_INVALID_ERROR_CODE); - assertTrue(!result.isSuccessfulDecode()); - assertTrue(result.eapError.cause instanceof NumberFormatException); - } - - @Test - public void testDecodeFailureRequestInvalidChallenge() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest(TAG, FAILURE_REQUEST_INVALID_CHALLENGE); - assertTrue(!result.isSuccessfulDecode()); - assertTrue(result.eapError.cause instanceof NumberFormatException); - } - - @Test - public void testDecodeFailureRequestShortChallenge() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest(TAG, FAILURE_REQUEST_SHORT_CHALLENGE); - assertTrue(!result.isSuccessfulDecode()); - assertTrue(result.eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeFailureRequestInvalidPasswordChangeProtocol() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest( - TAG, FAILURE_REQUEST_INVALID_PASSWORD_CHANGE_PROTOCOL); - assertTrue(!result.isSuccessfulDecode()); - assertTrue(result.eapError.cause instanceof NumberFormatException); - } - - @Test - public void testDecodeFailureExtraAttribute() { - DecodeResult<EapMsChapV2FailureRequest> result = - mTypeDataDecoder.decodeFailureRequest(TAG, FAILURE_REQUEST_EXTRA_ATTRIBUTE); - assertTrue(!result.isSuccessfulDecode()); - assertTrue(result.eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testEncodeFails() throws Exception { - EapMsChapV2FailureRequest failureRequest = - new EapMsChapV2FailureRequest( - ID_INT, - EAP_MSCHAP_V2_FAILURE_REQUEST.length, - ERROR_CODE, - RETRY_BIT, - CHALLENGE_BYTES, - PASSWORD_CHANGE_PROTOCOL, - MESSAGE); - try { - failureRequest.encode(); - fail("Expected UnsupportedOperationException for encoding a request"); - } catch (UnsupportedOperationException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2FailureResponseTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2FailureResponseTest.java deleted file mode 100644 index 261ddb28..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2FailureResponseTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_FAILURE_RESPONSE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_FAILURE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2FailureResponse; - -import org.junit.Test; - -public class EapMsChapV2FailureResponseTest { - @Test - public void testGetEapMsChapV2FailureResponse() { - EapMsChapV2FailureResponse failureResponse = - EapMsChapV2FailureResponse.getEapMsChapV2FailureResponse(); - assertEquals(EAP_MSCHAP_V2_FAILURE, failureResponse.opCode); - } - - @Test - public void testEncode() { - EapMsChapV2FailureResponse failureResponse = - EapMsChapV2FailureResponse.getEapMsChapV2FailureResponse(); - assertArrayEquals(EAP_MSCHAP_V2_FAILURE_RESPONSE, failureResponse.encode()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2PacketDefinitions.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2PacketDefinitions.java deleted file mode 100644 index e70c2a2f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2PacketDefinitions.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; - -public class EapMsChapV2PacketDefinitions { - public static final String ID = "1F"; - public static final int ID_INT = Integer.parseInt(ID, 16 /* radix */); - - public static final String CHALLENGE = "000102030405060708090A0B0C0D0E0F"; - public static final byte[] CHALLENGE_BYTES = hexStringToByteArray(CHALLENGE); - - // server name is the ASCII hex for "authenticator@android.net" - public static final String SERVER_NAME = "61757468656E74696361746F7240616E64726F69642E6E6574"; - public static final byte[] SERVER_NAME_BYTES = hexStringToByteArray(SERVER_NAME); - public static final byte[] EAP_MSCHAP_V2_CHALLENGE_REQUEST = - hexStringToByteArray("01" + ID + "002E10" + CHALLENGE + SERVER_NAME); - - public static final byte[] CHALLENGE_REQUEST_WRONG_OP_CODE = hexStringToByteArray("02"); - public static final String SHORT_CHALLENGE = "001122334455"; - public static final byte[] SHORT_CHALLENGE_BYTES = hexStringToByteArray(SHORT_CHALLENGE); - public static final byte[] CHALLENGE_REQUEST_SHORT_CHALLENGE = - hexStringToByteArray("01" + ID + "002406" + SHORT_CHALLENGE + SERVER_NAME); - public static final byte[] CHALLENGE_REQUEST_SHORT_MS_LENGTH = - hexStringToByteArray("01" + ID + "000110" + CHALLENGE + SERVER_NAME); - public static final byte[] CHALLENGE_REQUEST_LONG_MS_LENGTH = - hexStringToByteArray("01" + ID + "00FF10" + CHALLENGE + SERVER_NAME); - - public static final String PEER_CHALLENGE = "00112233445566778899AABBCCDDEEFF"; - public static final byte[] PEER_CHALLENGE_BYTES = hexStringToByteArray(PEER_CHALLENGE); - public static final String NT_RESPONSE = "FFEEDDCCBBAA998877665544332211000011223344556677"; - public static final byte[] NT_RESPONSE_BYTES = hexStringToByteArray(NT_RESPONSE); - - // peer name is the ASCII hex for "peer@android.net" - public static final String PEER_NAME = "7065657240616E64726F69642E6E6574"; - public static final byte[] PEER_NAME_BYTES = hexStringToByteArray(PEER_NAME); - public static final byte[] EAP_MSCHAP_V2_CHALLENGE_RESPONSE = - hexStringToByteArray( - "02" - + ID - + "004631" - + PEER_CHALLENGE - + "0000000000000000" - + NT_RESPONSE - + "00" - + PEER_NAME); - - public static final byte[] SHORT_NT_RESPONSE = hexStringToByteArray("0011223344"); - - public static final String AUTH_STRING = "00112233445566778899AABBCCDDEEFF00112233"; - - // ASCII hex for AUTH_STRING - public static final String AUTH_STRING_HEX = - "30303131323233333434353536363737383839394141424243434444454546463030313132323333"; - public static final byte[] AUTH_BYTES = hexStringToByteArray(AUTH_STRING); - - // hex("S=") + AUTH_STRING_HEX - public static final String FORMATTED_AUTH_STRING = "533D" + AUTH_STRING_HEX; - - public static final String SPACE_HEX = "20"; - - // ASCII hex for: "test Android 1234" - public static final String MESSAGE = "test Android 1234"; - public static final String MESSAGE_HEX = "7465737420416E64726F69642031323334"; - - // hex("M=") + MESSAGE_HEX - public static final String FORMATTED_MESSAGE = "4D3D" + MESSAGE_HEX; - - public static final byte[] EAP_MSCHAP_V2_SUCCESS_REQUEST = - hexStringToByteArray( - "03" + ID + "0042" + FORMATTED_AUTH_STRING + SPACE_HEX + FORMATTED_MESSAGE); - public static final byte[] EAP_MSCHAP_V2_SUCCESS_REQUEST_EMPTY_MESSAGE = - hexStringToByteArray("03" + ID + "0031" + FORMATTED_AUTH_STRING + SPACE_HEX + "4D3D"); - public static final byte[] EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE = - hexStringToByteArray("03" + ID + "002E" + FORMATTED_AUTH_STRING); - public static final byte[] EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE_WITH_SPACE = - hexStringToByteArray("03" + ID + "002F" + FORMATTED_AUTH_STRING + SPACE_HEX); - public static final String MESSAGE_MISSING_TEXT = "<omitted by authenticator>"; - - public static final String SHORT_AUTH_STRING = "001122334455"; - - public static final byte[] SUCCESS_REQUEST_WRONG_OP_CODE = hexStringToByteArray("02"); - - // message format: hex("M=") + AUTH_STRING_HEX + hex("M=") + MESSAGE_HEX - public static final byte[] SUCCESS_REQUEST_WRONG_PREFIX = - hexStringToByteArray("03" + ID + "00314D3D" + AUTH_STRING_HEX + SPACE_HEX + "4D3D"); - - // message format: hex("S=") + SHORT_AUTH_STRING + hex("M=") + MESSAGE_HEX - public static final byte[] SUCCESS_REQUEST_SHORT_AUTH_STRING = - hexStringToByteArray("03" + ID + "0031533D" + SHORT_AUTH_STRING + SPACE_HEX + "4D3D"); - - public static final String INVALID_AUTH_HEX = - "3030313132323333343435353636373738383939414142424343444445454646303031317A7A7979"; - public static final byte[] SUCCESS_REQUEST_INVALID_AUTH_STRING = - hexStringToByteArray("03" + ID + "0031533D" + INVALID_AUTH_HEX + SPACE_HEX + "4D3D"); - - // extra key-value: hex("N=12") - public static final String EXTRA_KEY = "4E3D3132"; - public static final byte[] SUCCESS_REQUEST_EXTRA_ATTRIBUTE = - hexStringToByteArray( - "03" - + ID - + "0042" - + FORMATTED_AUTH_STRING - + SPACE_HEX - + EXTRA_KEY - + SPACE_HEX - + FORMATTED_MESSAGE); - - public static final String SUCCESS_REQUEST = "S=" + AUTH_STRING + " M=" + MESSAGE; - public static final String EXTRA_M_MESSAGE = "M=" + MESSAGE; - public static final String SUCCESS_REQUEST_EXTRA_M = - "S=" + AUTH_STRING + " M=" + EXTRA_M_MESSAGE; - public static final String SUCCESS_REQUEST_MISSING_M = "S=" + AUTH_STRING; - public static final String SUCCESS_REQUEST_INVALID_FORMAT = - "S==" + AUTH_STRING + "M=" + MESSAGE; - public static final String SUCCESS_REQUEST_DUPLICATE_KEY = - "S=" + AUTH_STRING + " S=" + AUTH_STRING + " M=" + MESSAGE; - - public static final byte[] EAP_MSCHAP_V2_SUCCESS_RESPONSE = hexStringToByteArray("03"); - - public static final int ERROR_CODE = 647; // account disabled - - // formatted error code: hex("E=" + ERROR_CODE) - public static final String FORMATTED_ERROR_CODE = "453D363437"; - public static final boolean RETRY_BIT = true; - - // formatted retry bit: hex("R=1") - public static final String FORMATTED_RETRY_BIT = "523D31"; - - // challenge hex: hex(CHALLENGE) - public static final String CHALLENGE_HEX = - "3030303130323033303430353036303730383039304130423043304430453046"; - - // formatted challenge: hex("C=") + CHALLENGE_HEX - public static final String FORMATTED_CHALLENGE = "433D" + CHALLENGE_HEX; - - public static final int PASSWORD_CHANGE_PROTOCOL = 3; - - // formatted password change protocol: hex("V=3") - public static final String FORMATTED_PASSWORD_CHANGE_PROTOCOL = "563D33"; - - public static final byte[] EAP_MSCHAP_V2_FAILURE_REQUEST = - hexStringToByteArray( - "04" - + ID - + "0048" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX - + FORMATTED_MESSAGE); - public static final byte[] EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE = - hexStringToByteArray( - "04" - + ID - + "0034" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL); - public static final byte[] EAP_MSCHAP_V2_FAILURE_REQUEST_MISSING_MESSAGE_WITH_SPACE = - hexStringToByteArray( - "04" - + ID - + "0035" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX); - - // invalid error code: hex("E=abc") - public static final String INVALID_ERROR_CODE = "453D616263"; - public static final byte[] FAILURE_REQUEST_INVALID_ERROR_CODE = - hexStringToByteArray( - "04" - + ID - + "0048" - + INVALID_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX - + FORMATTED_MESSAGE); - - // invalid challenge: hex("C=zyxd") - public static final String INVALID_CHALLENGE = "433D7A797864"; - public static final byte[] FAILURE_REQUEST_INVALID_CHALLENGE = - hexStringToByteArray( - "04" - + ID - + "0032" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + INVALID_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX - + FORMATTED_MESSAGE); - - // short challenge: hex("C=" + SHORT_CHALLENGE) - public static final String FORMATTED_SHORT_CHALLENGE = "433D303031313232333334343535"; - public static final byte[] FAILURE_REQUEST_SHORT_CHALLENGE = - hexStringToByteArray( - "04" - + ID - + "0034" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_SHORT_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX - + FORMATTED_MESSAGE); - - // invalid password change protocol: hex("V=d") - public static final String INVALID_PASSWORD_CHANGE_PROTOCOL = "563D64"; - public static final byte[] FAILURE_REQUEST_INVALID_PASSWORD_CHANGE_PROTOCOL = - hexStringToByteArray( - "04" - + ID - + "0048" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_CHALLENGE - + SPACE_HEX - + INVALID_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX - + FORMATTED_MESSAGE); - - public static final byte[] FAILURE_REQUEST_EXTRA_ATTRIBUTE = - hexStringToByteArray( - "04" - + ID - + "0048" - + FORMATTED_ERROR_CODE - + SPACE_HEX - + FORMATTED_RETRY_BIT - + SPACE_HEX - + FORMATTED_CHALLENGE - + SPACE_HEX - + FORMATTED_PASSWORD_CHANGE_PROTOCOL - + SPACE_HEX - + EXTRA_KEY - + SPACE_HEX - + FORMATTED_MESSAGE); - - public static final byte[] EAP_MSCHAP_V2_FAILURE_RESPONSE = hexStringToByteArray("04"); -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2SuccessRequestTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2SuccessRequestTest.java deleted file mode 100644 index 1e0909ed..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2SuccessRequestTest.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.AUTH_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_SUCCESS_REQUEST; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_SUCCESS_REQUEST_EMPTY_MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE_WITH_SPACE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.ID_INT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE_MISSING_TEXT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_EXTRA_ATTRIBUTE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_INVALID_AUTH_STRING; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_SHORT_AUTH_STRING; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_WRONG_OP_CODE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_WRONG_PREFIX; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_SUCCESS; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2SuccessRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.BufferUnderflowException; - -public class EapMsChapV2SuccessRequestTest { - private static final String TAG = EapMsChapV2SuccessRequestTest.class.getSimpleName(); - - private EapMsChapV2TypeDataDecoder mTypeDataDecoder; - - @Before - public void setUp() { - mTypeDataDecoder = new EapMsChapV2TypeDataDecoder(); - } - - @Test - public void testDecodeSuccessRequest() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, EAP_MSCHAP_V2_SUCCESS_REQUEST); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2SuccessRequest successRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_SUCCESS, successRequest.opCode); - assertEquals(ID_INT, successRequest.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_SUCCESS_REQUEST.length, successRequest.msLength); - assertArrayEquals(AUTH_BYTES, successRequest.authBytes); - assertEquals(MESSAGE, successRequest.message); - } - - @Test - public void testDecodeSuccessRequestEmptyMessage() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest( - TAG, EAP_MSCHAP_V2_SUCCESS_REQUEST_EMPTY_MESSAGE); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2SuccessRequest successRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_SUCCESS, successRequest.opCode); - assertEquals(ID_INT, successRequest.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_SUCCESS_REQUEST_EMPTY_MESSAGE.length, successRequest.msLength); - assertArrayEquals(AUTH_BYTES, successRequest.authBytes); - assertTrue(successRequest.message.isEmpty()); - } - - @Test - public void testDecodeSuccessRequestMissingMessage() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest( - TAG, EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2SuccessRequest successRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_SUCCESS, successRequest.opCode); - assertEquals(ID_INT, successRequest.msChapV2Id); - assertEquals(EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE.length, successRequest.msLength); - assertArrayEquals(AUTH_BYTES, successRequest.authBytes); - assertEquals(MESSAGE_MISSING_TEXT, successRequest.message); - } - - @Test - public void testDecodeSuccessRequestMissingMessageWithSpace() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest( - TAG, EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE_WITH_SPACE); - assertTrue(result.isSuccessfulDecode()); - - EapMsChapV2SuccessRequest successRequest = result.eapTypeData; - assertEquals(EAP_MSCHAP_V2_SUCCESS, successRequest.opCode); - assertEquals(ID_INT, successRequest.msChapV2Id); - assertEquals( - EAP_MSCHAP_V2_SUCCESS_REQUEST_MISSING_MESSAGE_WITH_SPACE.length, - successRequest.msLength); - assertArrayEquals(AUTH_BYTES, successRequest.authBytes); - assertEquals(MESSAGE_MISSING_TEXT, successRequest.message); - } - - @Test - public void testDecodeSuccessRequestWrongOpCode() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, SUCCESS_REQUEST_WRONG_OP_CODE); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeSuccessRequestShortMessage() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, new byte[0]); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof BufferUnderflowException); - } - - @Test - public void testDecodeSuccessRequestInvalidPrefix() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, SUCCESS_REQUEST_WRONG_PREFIX); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeSuccessRequestShortAuthString() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, SUCCESS_REQUEST_SHORT_AUTH_STRING); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testDecodeSuccessRequestInvalidAuthString() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, SUCCESS_REQUEST_INVALID_AUTH_STRING); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof NumberFormatException); - } - - @Test - public void testDecodeSuccessRequestExtraAttribute() { - DecodeResult<EapMsChapV2SuccessRequest> result = - mTypeDataDecoder.decodeSuccessRequest(TAG, SUCCESS_REQUEST_EXTRA_ATTRIBUTE); - assertFalse(result.isSuccessfulDecode()); - EapError eapError = result.eapError; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - } - - @Test - public void testEncodeFails() throws Exception { - EapMsChapV2SuccessRequest successRequest = - new EapMsChapV2SuccessRequest( - ID_INT, EAP_MSCHAP_V2_SUCCESS_REQUEST.length, AUTH_BYTES, MESSAGE); - try { - successRequest.encode(); - fail("Expected UnsupportedOperationException for encoding a request"); - } catch (UnsupportedOperationException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2SuccessResponseTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2SuccessResponseTest.java deleted file mode 100644 index 524b3ecf..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2SuccessResponseTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EAP_MSCHAP_V2_SUCCESS_RESPONSE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_SUCCESS; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2SuccessResponse; - -import org.junit.Test; - -public class EapMsChapV2SuccessResponseTest { - @Test - public void testGetEapMsChapV2SuccessResponse() { - EapMsChapV2SuccessResponse successResponse = - EapMsChapV2SuccessResponse.getEapMsChapV2SuccessResponse(); - assertEquals(EAP_MSCHAP_V2_SUCCESS, successResponse.opCode); - } - - @Test - public void testEncode() { - EapMsChapV2SuccessResponse successResponse = - EapMsChapV2SuccessResponse.getEapMsChapV2SuccessResponse(); - assertArrayEquals(EAP_MSCHAP_V2_SUCCESS_RESPONSE, successResponse.encode()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2TypeDataTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2TypeDataTest.java deleted file mode 100644 index 45f1c641..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/mschapv2/EapMsChapV2TypeDataTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.mschapv2; - -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.AUTH_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.AUTH_STRING; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.EXTRA_M_MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE_MISSING_TEXT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_DUPLICATE_KEY; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_EXTRA_M; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_INVALID_FORMAT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SUCCESS_REQUEST_MISSING_M; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_CHALLENGE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2VariableTypeData; - -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -public class EapMsChapV2TypeDataTest { - private static final int INVALID_OPCODE = -1; - private static final int MSCHAP_V2_ID = 1; - private static final int MS_LENGTH = 32; - private static final String HEX_STRING_INVALID_LENGTH = "00112"; - private static final String HEX_STRING_INVALID_CHARS = "001122z-+x"; - - @Test - public void testEapMsChapV2TypeDataConstructor() throws Exception { - EapMsChapV2TypeData typeData = new EapMsChapV2TypeData(EAP_MSCHAP_V2_CHALLENGE) {}; - assertEquals(EAP_MSCHAP_V2_CHALLENGE, typeData.opCode); - - try { - new EapMsChapV2TypeData(INVALID_OPCODE) {}; - fail("ExpectedEapMsChapV2ParsingException for invalid OpCode"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testEapMsChapV2VariableTypeDataConstructor() throws Exception { - EapMsChapV2VariableTypeData typeData = - new EapMsChapV2VariableTypeData( - EAP_MSCHAP_V2_CHALLENGE, MSCHAP_V2_ID, MS_LENGTH) {}; - assertEquals(EAP_MSCHAP_V2_CHALLENGE, typeData.opCode); - assertEquals(MSCHAP_V2_ID, typeData.msChapV2Id); - assertEquals(MS_LENGTH, typeData.msLength); - - try { - new EapMsChapV2VariableTypeData(INVALID_OPCODE, MSCHAP_V2_ID, MS_LENGTH) {}; - fail("ExpectedEapMsChapV2ParsingException for invalid OpCode"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testDecodeResultIsSuccessfulDecode() throws Exception { - DecodeResult<EapMsChapV2TypeData> result = - new DecodeResult(new EapMsChapV2TypeData(EAP_MSCHAP_V2_CHALLENGE) {}); - assertTrue(result.isSuccessfulDecode()); - - result = new DecodeResult(new EapError(new Exception())); - assertFalse(result.isSuccessfulDecode()); - } - - @Test - public void testGetMessageMappings() throws Exception { - Map<String, String> expectedMappings = new HashMap<>(); - expectedMappings.put("S", AUTH_STRING); - expectedMappings.put("M", MESSAGE); - assertEquals(expectedMappings, EapMsChapV2TypeData.getMessageMappings(SUCCESS_REQUEST)); - - expectedMappings = new HashMap<>(); - expectedMappings.put("S", AUTH_STRING); - expectedMappings.put("M", EXTRA_M_MESSAGE); - assertEquals( - expectedMappings, EapMsChapV2TypeData.getMessageMappings(SUCCESS_REQUEST_EXTRA_M)); - - expectedMappings = new HashMap<>(); - expectedMappings.put("S", AUTH_STRING); - expectedMappings.put("M", MESSAGE_MISSING_TEXT); - assertEquals( - expectedMappings, - EapMsChapV2TypeData.getMessageMappings(SUCCESS_REQUEST_MISSING_M)); - } - - @Test - public void testGetMessageMappingsInvalidFormat() { - try { - EapMsChapV2TypeData.getMessageMappings(SUCCESS_REQUEST_INVALID_FORMAT); - fail("Expected EapMsChapV2ParsingException for extra '='s in message"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testGetMessageMappingDuplicateKey() { - try { - EapMsChapV2TypeData.getMessageMappings(SUCCESS_REQUEST_DUPLICATE_KEY); - fail("Expected EapMsChapV2ParsingException for duplicate key in message"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testHexStringToByteArray() throws Exception { - byte[] result = EapMsChapV2TypeData.hexStringToByteArray(AUTH_STRING); - assertArrayEquals(AUTH_BYTES, result); - } - - @Test - public void testHexStringToByteArrayInvalidLength() { - try { - EapMsChapV2TypeData.hexStringToByteArray(HEX_STRING_INVALID_LENGTH); - fail("Expected EapMsChapV2ParsingException for invalid hex string length"); - } catch (EapMsChapV2ParsingException expected) { - } - } - - @Test - public void testHexStringToByteArrayInvalidChars() throws Exception { - try { - EapMsChapV2TypeData.hexStringToByteArray(HEX_STRING_INVALID_CHARS); - fail("Expected NumberFormatException for invalid hex chars"); - } catch (NumberFormatException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeTypeDataTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeTypeDataTest.java deleted file mode 100644 index 6a9e517f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapAkaPrimeTypeDataTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF_INPUT; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_KDF_INPUT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.KDF_VERSION; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NETWORK_NAME_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NETWORK_NAME_HEX; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData.EapAkaPrimeTypeDataDecoder; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdf; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdfInput; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map.Entry; - -public class EapAkaPrimeTypeDataTest { - private static final String RAND = "7A1FCDC0034BA1227E7B9FCEAFD47D53"; - private static final byte[] RAND_BYTES = hexStringToByteArray(RAND); - private static final String AUTN = "000102030405060708090A0B0C0D0E0F"; - private static final byte[] AUTN_BYTES = hexStringToByteArray(AUTN); - private static final String MAC = "95FEB9E70427F34B4FAC8F2C7A65A302"; - private static final byte[] MAC_BYTES = hexStringToByteArray(MAC); - private static final byte[] EAP_AKA_PRIME_CHALLENGE_REQUEST = - hexStringToByteArray( - "010000" // Challenge | 2B padding - + "01050000" + RAND // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "1704000B" + NETWORK_NAME_HEX + "00" // AT_KDF_INPUT - + "18010001" // AT_KDF - + "0B050000" + MAC); // AT_MAC attribute - private static final byte[] EAP_AKA_PRIME_MULTIPLE_AT_KDF = - hexStringToByteArray( - "010000" // Challenge | 2B padding - + "01050000" + RAND // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "1704000B" + NETWORK_NAME_HEX + "00" // AT_KDF_INPUT - + "18010001" // AT_KDF - + "18010002" // AT_KDF - + "0B050000" + MAC); // AT_MAC attribute - - private EapAkaPrimeTypeDataDecoder mTypeDataDecoder; - - @Before - public void setUp() { - mTypeDataDecoder = EapAkaPrimeTypeData.getEapAkaPrimeTypeDataDecoder(); - } - - @Test - public void testDecode() { - DecodeResult<EapAkaTypeData> result = - mTypeDataDecoder.decode(EAP_AKA_PRIME_CHALLENGE_REQUEST); - - assertTrue(result.isSuccessfulDecode()); - EapAkaPrimeTypeData eapAkaPrimeTypeData = (EapAkaPrimeTypeData) result.eapTypeData; - assertEquals(EAP_AKA_CHALLENGE, eapAkaPrimeTypeData.eapSubtype); - - // also check Map entries (needs to match input order) - Iterator<Entry<Integer, EapSimAkaAttribute>> itr = - eapAkaPrimeTypeData.attributeMap.entrySet().iterator(); - Entry<Integer, EapSimAkaAttribute> entry = itr.next(); - assertEquals(EAP_AT_RAND, (int) entry.getKey()); - assertArrayEquals(RAND_BYTES, ((AtRandAka) entry.getValue()).rand); - - entry = itr.next(); - assertEquals(EAP_AT_AUTN, (int) entry.getKey()); - assertArrayEquals(AUTN_BYTES, ((AtAutn) entry.getValue()).autn); - - entry = itr.next(); - assertEquals(EAP_AT_KDF_INPUT, (int) entry.getKey()); - assertArrayEquals(NETWORK_NAME_BYTES, ((AtKdfInput) entry.getValue()).networkName); - - entry = itr.next(); - assertEquals(EAP_AT_KDF, (int) entry.getKey()); - assertEquals(KDF_VERSION, ((AtKdf) entry.getValue()).kdf); - - entry = itr.next(); - assertEquals(EAP_AT_MAC, (int) entry.getKey()); - assertArrayEquals(MAC_BYTES, ((AtMac) entry.getValue()).mac); - - assertFalse(itr.hasNext()); - } - - @Test - public void testDecodeMultipleAtKdfAttributes() { - DecodeResult<EapAkaTypeData> result = - mTypeDataDecoder.decode(EAP_AKA_PRIME_MULTIPLE_AT_KDF); - - assertFalse(result.isSuccessfulDecode()); - assertEquals(AtClientErrorCode.UNABLE_TO_PROCESS, result.atClientErrorCode); - } - - @Test - public void testEncode() throws Exception { - LinkedHashMap<Integer, EapSimAkaAttribute> attributes = new LinkedHashMap<>(); - attributes.put(EAP_AT_RAND, new AtRandAka(RAND_BYTES)); - attributes.put(EAP_AT_AUTN, new AtAutn(AUTN_BYTES)); - attributes.put(EAP_AT_KDF_INPUT, new AtKdfInput(AT_KDF_INPUT.length, NETWORK_NAME_BYTES)); - attributes.put(EAP_AT_KDF, new AtKdf(KDF_VERSION)); - attributes.put(EAP_AT_MAC, new AtMac(MAC_BYTES)); - EapAkaPrimeTypeData eapAkaPrimeTypeData = - new EapAkaPrimeTypeData(EAP_AKA_CHALLENGE, attributes); - - byte[] result = eapAkaPrimeTypeData.encode(); - assertArrayEquals(EAP_AKA_PRIME_CHALLENGE_REQUEST, result); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapAkaTypeDataTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapAkaTypeDataTest.java deleted file mode 100644 index b2d89d16..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapAkaTypeDataTest.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CHALLENGE_RESPONSE_MAC_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CHALLENGE_RESPONSE_TYPE_DATA; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_IDENTITY_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.INVALID_SUBTYPE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_CHECKCODE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RES_BYTES; - -import static junit.framework.TestCase.fail; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.android.internal.net.eap.message.simaka.EapAkaTypeData.EapAkaTypeDataDecoder; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRes; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EapSimAkaUnsupportedAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map.Entry; - -public class EapAkaTypeDataTest { - private static final int UNABLE_TO_PROCESS_CODE = 0; - private static final int INVALID_SUBTYPE_INT = -1; - - private static final int EAP_AT_TRUST_IND = 139; - private static final String RAND = "7A1FCDC0034BA1227E7B9FCEAFD47D53"; - private static final byte[] RAND_BYTES = hexStringToByteArray(RAND); - private static final String AUTN = "000102030405060708090A0B0C0D0E0F"; - private static final byte[] AUTN_BYTES = hexStringToByteArray(AUTN); - private static final String MAC = "95FEB9E70427F34B4FAC8F2C7A65A302"; - private static final byte[] MAC_BYTES = hexStringToByteArray(MAC); - private static final byte[] EAP_AKA_REQUEST = - hexStringToByteArray( - "010000" // Challenge | 2B padding - + "01050000" + RAND // AT_RAND attribute - + "02050000" + AUTN // AT_AUTN attribute - + "8B010002" // AT_RESULT_IND attribute (TS 124 302#8.2.3.1) - + "0B050000" + MAC // AT_MAC attribute - + "86010000"); // AT_CHECKCODE attribute - - private EapAkaTypeDataDecoder mEapAkaTypeDataDecoder; - - @Before - public void setUp() { - mEapAkaTypeDataDecoder = EapAkaTypeData.getEapAkaTypeDataDecoder(); - } - - @Test - public void testDecode() { - DecodeResult<EapAkaTypeData> result = - mEapAkaTypeDataDecoder.decode(EAP_AKA_CHALLENGE_RESPONSE_TYPE_DATA); - - assertTrue(result.isSuccessfulDecode()); - EapAkaTypeData eapAkaTypeData = result.eapTypeData; - assertEquals(EAP_AKA_CHALLENGE, eapAkaTypeData.eapSubtype); - - // also check Map entries (needs to match input order) - Iterator<Entry<Integer, EapSimAkaAttribute>> itr = - eapAkaTypeData.attributeMap.entrySet().iterator(); - Entry<Integer, EapSimAkaAttribute> entry = itr.next(); - assertEquals(EAP_AT_RES, (int) entry.getKey()); - assertArrayEquals(RES_BYTES, ((AtRes) entry.getValue()).res); - - entry = itr.next(); - assertEquals(EAP_AT_MAC, (int) entry.getKey()); - assertArrayEquals(EAP_AKA_CHALLENGE_RESPONSE_MAC_BYTES, ((AtMac) entry.getValue()).mac); - - assertFalse(itr.hasNext()); - } - - @Test - public void testDecodeWithOptionalAttributes() { - DecodeResult<EapAkaTypeData> result = mEapAkaTypeDataDecoder.decode(EAP_AKA_REQUEST); - - assertTrue(result.isSuccessfulDecode()); - EapAkaTypeData eapAkaTypeData = result.eapTypeData; - assertEquals(EAP_AKA_CHALLENGE, eapAkaTypeData.eapSubtype); - - // also check Map entries (needs to match input order) - Iterator<Entry<Integer, EapSimAkaAttribute>> itr = - eapAkaTypeData.attributeMap.entrySet().iterator(); - Entry<Integer, EapSimAkaAttribute> entry = itr.next(); - assertEquals(EAP_AT_RAND, (int) entry.getKey()); - assertArrayEquals(RAND_BYTES, ((AtRandAka) entry.getValue()).rand); - - entry = itr.next(); - assertEquals(EAP_AT_AUTN, (int) entry.getKey()); - assertArrayEquals(AUTN_BYTES, ((AtAutn) entry.getValue()).autn); - - entry = itr.next(); - assertEquals(EAP_AT_TRUST_IND, (int) entry.getKey()); - assertTrue(entry.getValue() instanceof EapSimAkaUnsupportedAttribute); - - entry = itr.next(); - assertEquals(EAP_AT_MAC, (int) entry.getKey()); - assertArrayEquals(MAC_BYTES, ((AtMac) entry.getValue()).mac); - - entry = itr.next(); - assertEquals(EAP_AT_CHECKCODE, (int) entry.getKey()); - assertTrue(entry.getValue() instanceof EapSimAkaUnsupportedAttribute); - - assertFalse(itr.hasNext()); - } - - @Test - public void testDecodeInvalidSubtype() { - DecodeResult<EapAkaTypeData> result = mEapAkaTypeDataDecoder.decode(INVALID_SUBTYPE); - assertFalse(result.isSuccessfulDecode()); - assertEquals(UNABLE_TO_PROCESS_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testEncode() throws Exception { - LinkedHashMap<Integer, EapSimAkaAttribute> attributes = new LinkedHashMap<>(); - attributes.put(EAP_AT_ANY_ID_REQ, new AtAnyIdReq()); - EapAkaTypeData eapAkaTypeData = new EapAkaTypeData(EAP_AKA_IDENTITY, attributes); - - byte[] result = eapAkaTypeData.encode(); - assertArrayEquals(EAP_AKA_IDENTITY_REQUEST, result); - } - - @Test - public void testConstructorInvalidSubtype() throws Exception { - try { - new EapAkaTypeData(INVALID_SUBTYPE_INT, Arrays.asList(new AtAnyIdReq())); - fail("Expected IllegalArgumentException for invalid subtype"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testConstructorDuplicateAttributes() throws Exception { - try { - new EapAkaTypeData(EAP_AKA_IDENTITY, Arrays.asList(new AtAnyIdReq(), new AtAnyIdReq())); - fail("Expected IllegalArgumentException for duplicate attributes"); - } catch (IllegalArgumentException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttributeFactoryTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttributeFactoryTest.java deleted file mode 100644 index 5b6f5d60..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapSimAkaAttributeFactoryTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SKIPPABLE_DATA; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SKIPPABLE_DATA_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SKIPPABLE_INVALID_ATTRIBUTE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaUnsupportedAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EapSimAkaUnsupportedAttribute; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class EapSimAkaAttributeFactoryTest { - private static final int SKIPPABLE_ATTRIBUTE_TYPE = 0xFF; - private static final int SKIPPABLE_EXPECTED_LENGTH = 8; - - private static final int NON_SKIPPABLE_ATTRIBUTE_TYPE = 0x7F; - private static final int NON_SKIPPABLE_ATTRIBUTE_LENGTH = 4; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecodeInvalidSkippable() throws Exception { - ByteBuffer byteBuffer = ByteBuffer.wrap(SKIPPABLE_DATA_BYTES); - - EapSimAkaAttribute result = mAttributeFactory.getAttribute( - SKIPPABLE_ATTRIBUTE_TYPE, - SKIPPABLE_EXPECTED_LENGTH, - byteBuffer); - assertTrue(result instanceof EapSimAkaUnsupportedAttribute); - EapSimAkaUnsupportedAttribute unsupportedAttribute = (EapSimAkaUnsupportedAttribute) result; - assertEquals(SKIPPABLE_ATTRIBUTE_TYPE, unsupportedAttribute.attributeType); - assertEquals(SKIPPABLE_EXPECTED_LENGTH, unsupportedAttribute.lengthInBytes); - assertArrayEquals(hexStringToByteArray(SKIPPABLE_DATA), unsupportedAttribute.data); - } - - @Test - public void testEncodeInvalidSkippable() throws Exception { - EapSimAkaUnsupportedAttribute unsupportedAttribute = new EapSimAkaUnsupportedAttribute( - SKIPPABLE_ATTRIBUTE_TYPE, - SKIPPABLE_EXPECTED_LENGTH, - hexStringToByteArray(SKIPPABLE_DATA)); - - ByteBuffer result = ByteBuffer.allocate(SKIPPABLE_EXPECTED_LENGTH); - unsupportedAttribute.encode(result); - assertArrayEquals(SKIPPABLE_INVALID_ATTRIBUTE, result.array()); - } - - @Test - public void testDecodeInvalidNonSkippable() throws Exception { - // Unskippable type + length + byte[] represent shortest legitimate attribute: "7F040000" - ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[2]); - - try { - mAttributeFactory.getAttribute( - NON_SKIPPABLE_ATTRIBUTE_TYPE, - NON_SKIPPABLE_ATTRIBUTE_LENGTH, - byteBuffer); - fail("Expected EapSimAkaUnsupportedAttributeException for decoding invalid" - + " non-skippable Attribute"); - } catch (EapSimAkaUnsupportedAttributeException expected) { - } - } - -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapSimTypeDataTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapSimTypeDataTest.java deleted file mode 100644 index 678a812b..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/EapSimTypeDataTest.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka; - -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_START_DUPLICATE_ATTRIBUTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_START_SUBTYPE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.INVALID_SUBTYPE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SHORT_TYPE_DATA; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.TYPE_DATA_INVALID_ATTRIBUTE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.TYPE_DATA_INVALID_AT_RAND; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_VERSION_LIST; - -import static junit.framework.TestCase.fail; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPermanentIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData.EapSimTypeDataDecoder; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map.Entry; - -public class EapSimTypeDataTest { - private static final int UNABLE_TO_PROCESS_CODE = 0; - private static final int INSUFFICIENT_CHALLENGES_CODE = 2; - private static final int EAP_SIM_START = 10; - private static final int INVALID_SUBTYPE_INT = -1; - - private EapSimTypeDataDecoder mEapSimTypeDataDecoder; - - @Before - public void setUp() { - mEapSimTypeDataDecoder = EapSimTypeData.getEapSimTypeDataDecoder(); - } - - @Test - public void testConstructor() throws Exception { - List<EapSimAkaAttribute> attributes = Arrays.asList( - new AtVersionList(8, 1), new AtPermanentIdReq()); - - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, attributes); - assertEquals(EAP_SIM_START, eapSimTypeData.eapSubtype); - - // check order of entries in EapSimTypeData.attributeMap - Iterator<Entry<Integer, EapSimAkaAttribute>> itr = - eapSimTypeData.attributeMap.entrySet().iterator(); - Entry<Integer, EapSimAkaAttribute> pair = itr.next(); - assertEquals(EAP_AT_VERSION_LIST, (int) pair.getKey()); - assertEquals(Arrays.asList(1), ((AtVersionList) pair.getValue()).versions); - - pair = itr.next(); - assertEquals(EAP_AT_PERMANENT_ID_REQ, (int) pair.getKey()); - assertTrue(pair.getValue() instanceof AtPermanentIdReq); - } - - @Test - public void testDecode() { - DecodeResult<EapSimTypeData> result = mEapSimTypeDataDecoder.decode(EAP_SIM_START_SUBTYPE); - - assertTrue(result.isSuccessfulDecode()); - EapSimTypeData eapSimTypeData = result.eapTypeData; - assertEquals(EAP_SIM_START, eapSimTypeData.eapSubtype); - assertTrue(eapSimTypeData.attributeMap.containsKey(EAP_AT_VERSION_LIST)); - AtVersionList atVersionList = (AtVersionList) - eapSimTypeData.attributeMap.get(EAP_AT_VERSION_LIST); - assertEquals(Arrays.asList(1), atVersionList.versions); - assertTrue(eapSimTypeData.attributeMap.containsKey(EAP_AT_PERMANENT_ID_REQ)); - - // also check order of Map entries (needs to match input order) - Iterator<Integer> itr = eapSimTypeData.attributeMap.keySet().iterator(); - assertEquals(EAP_AT_VERSION_LIST, (int) itr.next()); - assertEquals(EAP_AT_PERMANENT_ID_REQ, (int) itr.next()); - assertFalse(itr.hasNext()); - } - - @Test - public void testDecodeNullTypeData() { - DecodeResult<EapSimTypeData> result = mEapSimTypeDataDecoder.decode(null); - assertFalse(result.isSuccessfulDecode()); - assertEquals(UNABLE_TO_PROCESS_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testDecodeInvalidSubtype() { - DecodeResult<EapSimTypeData> result = mEapSimTypeDataDecoder.decode(INVALID_SUBTYPE); - assertFalse(result.isSuccessfulDecode()); - assertEquals(UNABLE_TO_PROCESS_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testDecodeInvalidAtRand() { - DecodeResult<EapSimTypeData> result = - mEapSimTypeDataDecoder.decode(TYPE_DATA_INVALID_AT_RAND); - assertFalse(result.isSuccessfulDecode()); - assertEquals(INSUFFICIENT_CHALLENGES_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testDecodeShortPacket() { - DecodeResult<EapSimTypeData> result = mEapSimTypeDataDecoder.decode(SHORT_TYPE_DATA); - assertFalse(result.isSuccessfulDecode()); - assertEquals(UNABLE_TO_PROCESS_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testDecodeInvalidEapAttribute() { - DecodeResult<EapSimTypeData> result = - mEapSimTypeDataDecoder.decode(TYPE_DATA_INVALID_ATTRIBUTE); - assertFalse(result.isSuccessfulDecode()); - assertEquals(UNABLE_TO_PROCESS_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testEncode() throws Exception { - LinkedHashMap<Integer, EapSimAkaAttribute> attributes = new LinkedHashMap<>(); - attributes.put(EAP_AT_VERSION_LIST, new AtVersionList(8, 1)); - attributes.put(EAP_AT_PERMANENT_ID_REQ, new AtPermanentIdReq()); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, attributes); - - byte[] result = eapSimTypeData.encode(); - assertArrayEquals(EAP_SIM_START_SUBTYPE, result); - } - - @Test - public void testDecodeDuplicateAttributes() { - DecodeResult<EapSimTypeData> result = - mEapSimTypeDataDecoder.decode(EAP_SIM_START_DUPLICATE_ATTRIBUTES); - assertFalse(result.isSuccessfulDecode()); - assertEquals(UNABLE_TO_PROCESS_CODE, result.atClientErrorCode.errorCode); - } - - @Test - public void testConstructorInvalidSubtype() throws Exception { - try { - new EapSimTypeData(INVALID_SUBTYPE_INT, Arrays.asList(new AtPermanentIdReq())); - fail("Expected IllegalArgumentException for invalid subtype"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testConstructorDuplicateAttributes() throws Exception { - try { - new EapSimTypeData( - EAP_SIM_START, Arrays.asList(new AtPermanentIdReq(), new AtPermanentIdReq())); - fail("Expected IllegalArgumentException for duplicate attributes"); - } catch (IllegalArgumentException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtAutnTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtAutnTest.java deleted file mode 100644 index ebf22e4f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtAutnTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_AUTN; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_AUTN_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AUTN_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapAkaAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtAutnTest { - private EapAkaAttributeFactory mEapAkaAttributeFactory; - - @Before - public void setUp() { - mEapAkaAttributeFactory = EapAkaAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_AUTN); - EapSimAkaAttribute result = mEapAkaAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtAutn atAutn = (AtAutn) result; - assertEquals(EAP_AT_AUTN, atAutn.attributeType); - assertEquals(AT_AUTN.length, atAutn.lengthInBytes); - assertArrayEquals(AUTN_BYTES, atAutn.autn); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_AUTN_INVALID_LENGTH); - try { - mEapAkaAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtAutn atAutn = new AtAutn(AUTN_BYTES); - - ByteBuffer result = ByteBuffer.allocate(AT_AUTN.length); - atAutn.encode(result); - assertArrayEquals(AT_AUTN, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtAutsTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtAutsTest.java deleted file mode 100644 index d65a735f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtAutsTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTS; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_AUTS; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_AUTS_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AUTS_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapAkaAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAuts; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtAutsTest { - private EapAkaAttributeFactory mEapAkaAttributeFactory; - - @Before - public void setUp() { - mEapAkaAttributeFactory = EapAkaAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_AUTS); - EapSimAkaAttribute result = mEapAkaAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtAuts atAuts = (AtAuts) result; - assertEquals(EAP_AT_AUTS, atAuts.attributeType); - assertEquals(AT_AUTS.length, atAuts.lengthInBytes); - assertArrayEquals(AUTS_BYTES, atAuts.auts); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_AUTS_INVALID_LENGTH); - try { - mEapAkaAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtAuts atAuts = new AtAuts(AUTS_BYTES); - - ByteBuffer result = ByteBuffer.allocate(AT_AUTS.length); - atAuts.encode(result); - assertArrayEquals(AT_AUTS, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtBiddingTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtBiddingTest.java deleted file mode 100644 index efdfcc2e..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtBiddingTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_BIDDING; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_BIDDING_DOES_NOT_SUPPORT_AKA_PRIME; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_BIDDING_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_BIDDING_SUPPORTS_AKA_PRIME; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapAkaAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtBidding; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtBiddingTest { - private EapAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = EapAkaAttributeFactory.getInstance(); - } - - @Test - public void testDecodeServerSupportsAkaPrime() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_BIDDING_SUPPORTS_AKA_PRIME); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtBidding atBidding = (AtBidding) result; - assertEquals(EAP_AT_BIDDING, atBidding.attributeType); - assertEquals(AT_BIDDING_SUPPORTS_AKA_PRIME.length, atBidding.lengthInBytes); - assertTrue(atBidding.doesServerSupportEapAkaPrime); - } - - @Test - public void testDecodeDoesNotSupportAkaPrime() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_BIDDING_DOES_NOT_SUPPORT_AKA_PRIME); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtBidding atBidding = (AtBidding) result; - assertEquals(EAP_AT_BIDDING, atBidding.attributeType); - assertEquals(AT_BIDDING_DOES_NOT_SUPPORT_AKA_PRIME.length, atBidding.lengthInBytes); - assertFalse(atBidding.doesServerSupportEapAkaPrime); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_BIDDING_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncodeServerSupportsAkaPrime() throws Exception { - AtBidding atBidding = new AtBidding(true); - - ByteBuffer result = ByteBuffer.allocate(AT_BIDDING_SUPPORTS_AKA_PRIME.length); - atBidding.encode(result); - assertArrayEquals(AT_BIDDING_SUPPORTS_AKA_PRIME, result.array()); - } - - @Test - public void testEncodeDoesNotSupportAkaPrime() throws Exception { - AtBidding atBidding = new AtBidding(false); - - ByteBuffer result = ByteBuffer.allocate(AT_BIDDING_DOES_NOT_SUPPORT_AKA_PRIME.length); - atBidding.encode(result); - assertArrayEquals(AT_BIDDING_DOES_NOT_SUPPORT_AKA_PRIME, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtClientErrorCodeTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtClientErrorCodeTest.java deleted file mode 100644 index 2051414c..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtClientErrorCodeTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToInt; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_CLIENT_ERROR_CODE; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_CLIENT_ERROR_CODE; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_CLIENT_ERROR_CODE_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.ERROR_CODE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; - -public class AtClientErrorCodeTest { - private static final int EXPECTED_LENGTH = 4; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_CLIENT_ERROR_CODE); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtClientErrorCode); - AtClientErrorCode atClientErrorCode = (AtClientErrorCode) result; - assertEquals(EAP_AT_CLIENT_ERROR_CODE, atClientErrorCode.attributeType); - assertEquals(EXPECTED_LENGTH, atClientErrorCode.lengthInBytes); - assertEquals(hexStringToInt(ERROR_CODE), atClientErrorCode.errorCode); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_CLIENT_ERROR_CODE_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected BufferUnderflowException for invalid attribute length"); - } catch (BufferUnderflowException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtClientErrorCode atNotification = new AtClientErrorCode( - EXPECTED_LENGTH, hexStringToInt(ERROR_CODE)); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atNotification.encode(result); - assertArrayEquals(AT_CLIENT_ERROR_CODE, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtCounterTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtCounterTest.java deleted file mode 100644 index eb1086d5..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtCounterTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_COUNTER; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_COUNTER_TOO_SMALL; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_COUNTER; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_COUNTER_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_COUNTER_TOO_SMALL; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_COUNTER_TOO_SMALL_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.COUNTER_INT; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtCounter; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtCounterTooSmall; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtCounterTest { - private static final int EXPECTED_LENGTH = 4; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecodeAtCounter() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_COUNTER); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtCounter); - AtCounter atCounter = (AtCounter) result; - assertEquals(EAP_AT_COUNTER, atCounter.attributeType); - assertEquals(EXPECTED_LENGTH, atCounter.lengthInBytes); - assertEquals(COUNTER_INT, atCounter.counter); - } - - @Test - public void testDecodeAtCounterInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_COUNTER_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncodeAtCounter() throws Exception { - AtCounter atCounter = new AtCounter(COUNTER_INT); - - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - atCounter.encode(result); - assertArrayEquals(AT_COUNTER, result.array()); - } - - @Test - public void testAtCounterTooSmallConstructor() throws Exception { - AtCounterTooSmall atCounterTooSmall = new AtCounterTooSmall(); - assertEquals(EAP_AT_COUNTER_TOO_SMALL, atCounterTooSmall.attributeType); - assertEquals(EXPECTED_LENGTH, atCounterTooSmall.lengthInBytes); - } - - @Test - public void testDecodeAtCounterTooSmall() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_COUNTER_TOO_SMALL); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtCounterTooSmall); - AtCounterTooSmall atCounterTooSmall = (AtCounterTooSmall) result; - assertEquals(EAP_AT_COUNTER_TOO_SMALL, atCounterTooSmall.attributeType); - assertEquals(EXPECTED_LENGTH, atCounterTooSmall.lengthInBytes); - } - - @Test - public void testDecodeAtCounterTooSmallInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_COUNTER_TOO_SMALL_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncodeAtCounterTooSmall() throws Exception { - AtCounterTooSmall atCounterTooSmall = new AtCounterTooSmall(); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - atCounterTooSmall.encode(result); - assertArrayEquals(AT_COUNTER_TOO_SMALL, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtIdReqTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtIdReqTest.java deleted file mode 100644 index d006053e..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtIdReqTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_FULLAUTH_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.ANY_ID_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_FULL_AUTH_ID_REQ; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.FULL_AUTH_ID_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.PERMANENT_ID_INVALID_LENGTH; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtFullauthIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPermanentIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtIdReqTest { - private static final int EXPECTED_LENGTH = 4; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecodeAtPermanentIdReq() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_PERMANENT_ID_REQ); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtPermanentIdReq); - AtPermanentIdReq atPermanentIdReq = (AtPermanentIdReq) result; - assertEquals(EAP_AT_PERMANENT_ID_REQ, atPermanentIdReq.attributeType); - assertEquals(EXPECTED_LENGTH, atPermanentIdReq.lengthInBytes); - } - - @Test - public void testDecodeAtPermanentIdReqInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(PERMANENT_ID_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid attribute length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncodeAtPermanentIdReq() throws Exception { - AtPermanentIdReq atPermanentIdReq = new AtPermanentIdReq(); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atPermanentIdReq.encode(result); - assertArrayEquals(AT_PERMANENT_ID_REQ, result.array()); - } - - @Test - public void testDecodeAtAnyIdReq() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_ANY_ID_REQ); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtAnyIdReq); - AtAnyIdReq atAnyIdReq = (AtAnyIdReq) result; - assertEquals(EAP_AT_ANY_ID_REQ, atAnyIdReq.attributeType); - assertEquals(EXPECTED_LENGTH, atAnyIdReq.lengthInBytes); - } - - @Test - public void testDecodeAtAnyIdReqInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(ANY_ID_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid attribute length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncodeAtAnyIdReq() throws Exception { - AtAnyIdReq atPermanentIdReq = new AtAnyIdReq(); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atPermanentIdReq.encode(result); - assertArrayEquals(AT_ANY_ID_REQ, result.array()); - } - - @Test - public void testDecodeAtFullauthIdReq() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_FULL_AUTH_ID_REQ); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtFullauthIdReq); - AtFullauthIdReq atFullauthIdReq = (AtFullauthIdReq) result; - assertEquals(EAP_AT_FULLAUTH_ID_REQ, atFullauthIdReq.attributeType); - assertEquals(EXPECTED_LENGTH, atFullauthIdReq.lengthInBytes); - } - - @Test - public void testDecodeAtFullauthIdReqInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(FULL_AUTH_ID_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid attribute length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncodeAtFullauthIdReq() throws Exception { - AtFullauthIdReq atPermanentIdReq = new AtFullauthIdReq(); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atPermanentIdReq.encode(result); - assertArrayEquals(AT_FULL_AUTH_ID_REQ, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtIdentityTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtIdentityTest.java deleted file mode 100644 index cf8e8803..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtIdentityTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IDENTITY; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_IDENTITY; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.IDENTITY; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtIdentityTest { - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_IDENTITY); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtIdentity); - AtIdentity atIdentity = (AtIdentity) result; - assertEquals(EAP_AT_IDENTITY, atIdentity.attributeType); - assertEquals(AT_IDENTITY.length, atIdentity.lengthInBytes); - assertArrayEquals(IDENTITY, atIdentity.identity); - } - - @Test - public void testEncode() throws Exception { - AtIdentity atIdentity = new AtIdentity(AT_IDENTITY.length, IDENTITY); - ByteBuffer result = ByteBuffer.allocate(AT_IDENTITY.length); - atIdentity.encode(result); - - assertArrayEquals(AT_IDENTITY, result.array()); - } - - @Test - public void testGetAtIdentity() throws Exception { - AtIdentity atIdentity = AtIdentity.getAtIdentity(IDENTITY); - - assertArrayEquals(IDENTITY, atIdentity.identity); - - ByteBuffer buffer = ByteBuffer.allocate(atIdentity.lengthInBytes); - atIdentity.encode(buffer); - buffer.rewind(); - - EapSimAkaAttribute eapSimAkaAttribute = - EapSimAttributeFactory.getInstance().getAttribute(buffer); - assertTrue(eapSimAkaAttribute instanceof AtIdentity); - AtIdentity newAtIdentity = (AtIdentity) eapSimAkaAttribute; - assertEquals(atIdentity.lengthInBytes, newAtIdentity.lengthInBytes); - assertArrayEquals(atIdentity.identity, newAtIdentity.identity); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtKdfInputTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtKdfInputTest.java deleted file mode 100644 index 51ea3f14..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtKdfInputTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF_INPUT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_KDF_INPUT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_KDF_INPUT_EMPTY_NETWORK_NAME; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NETWORK_NAME_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import com.android.internal.net.eap.message.simaka.EapAkaPrimeAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdfInput; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtKdfInputTest { - private EapAkaPrimeAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = EapAkaPrimeAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_KDF_INPUT); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtKdfInput atKdfInput = (AtKdfInput) result; - assertEquals(EAP_AT_KDF_INPUT, atKdfInput.attributeType); - assertEquals(AT_KDF_INPUT.length, atKdfInput.lengthInBytes); - assertArrayEquals(NETWORK_NAME_BYTES, atKdfInput.networkName); - } - - @Test - public void testDecodeEmptyNetworkName() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_KDF_INPUT_EMPTY_NETWORK_NAME); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtKdfInput atKdfInput = (AtKdfInput) result; - assertEquals(EAP_AT_KDF_INPUT, atKdfInput.attributeType); - assertEquals(AT_KDF_INPUT_EMPTY_NETWORK_NAME.length, atKdfInput.lengthInBytes); - assertArrayEquals(new byte[0], atKdfInput.networkName); - } - - @Test - public void testEncode() throws Exception { - AtKdfInput atKdfInput = new AtKdfInput(AT_KDF_INPUT.length, NETWORK_NAME_BYTES); - ByteBuffer result = ByteBuffer.allocate(AT_KDF_INPUT.length); - - atKdfInput.encode(result); - assertArrayEquals(AT_KDF_INPUT, result.array()); - assertFalse(result.hasRemaining()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtKdfTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtKdfTest.java deleted file mode 100644 index 0bb07326..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtKdfTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_KDF; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_KDF; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_KDF_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.KDF_VERSION; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdf; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtKdfTest { - private EapAkaPrimeAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = EapAkaPrimeAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_KDF); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtKdf atKdf = (AtKdf) result; - assertEquals(EAP_AT_KDF, atKdf.attributeType); - assertEquals(AT_KDF.length, atKdf.lengthInBytes); - assertEquals(KDF_VERSION, atKdf.kdf); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_KDF_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtKdf atKdf = new AtKdf(KDF_VERSION); - ByteBuffer result = ByteBuffer.allocate(AT_KDF.length); - - atKdf.encode(result); - assertArrayEquals(AT_KDF, result.array()); - assertFalse(result.hasRemaining()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtMacTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtMacTest.java deleted file mode 100644 index 82b066d5..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtMacTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_MAC; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_MAC_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.MAC; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtMacTest { - private static final int EXPECTED_LENGTH = 20; - private static final int MAC_LENGTH = 16; - private static final byte[] MAC_BYTES = hexStringToByteArray(MAC); - private static final byte[] INVALID_MAC = {(byte) 1, (byte) 2, (byte) 3}; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testConstructor() throws Exception { - AtMac atMac = new AtMac(); - assertEquals(EAP_AT_MAC, atMac.attributeType); - assertEquals(EXPECTED_LENGTH, atMac.lengthInBytes); - assertArrayEquals(new byte[MAC_LENGTH], atMac.mac); - } - - @Test - public void testParameterizedConstructor() throws Exception { - AtMac atMac = new AtMac(MAC_BYTES); - assertEquals(EAP_AT_MAC, atMac.attributeType); - assertEquals(EXPECTED_LENGTH, atMac.lengthInBytes); - assertArrayEquals(MAC_BYTES, atMac.mac); - } - - @Test - public void testParameterizedConstructorInvalidMac() { - try { - AtMac atMac = new AtMac(INVALID_MAC); - fail("Expected EapSimAkaInvalidAttributeException for invalid MAC length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_MAC); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtMac); - AtMac atMac = (AtMac) result; - assertEquals(EAP_AT_MAC, atMac.attributeType); - assertEquals(EXPECTED_LENGTH, atMac.lengthInBytes); - assertArrayEquals(MAC_BYTES, atMac.mac); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_MAC_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtMac atMac = new AtMac(MAC_BYTES); - - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - atMac.encode(result); - assertArrayEquals(AT_MAC, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNonceMtTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNonceMtTest.java deleted file mode 100644 index 751908a2..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNonceMtTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NONCE_MT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NONCE_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NONCE_MT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_MT; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceMt; -import com.android.internal.net.eap.message.simaka.EapSimAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtNonceMtTest { - private static final byte[] INVALID_NONCE = new byte[10]; - private static final int EXPECTED_LENGTH = 20; - - private EapSimAttributeFactory mEapSimAttributeFactory; - - @Before - public void setUp() { - mEapSimAttributeFactory = EapSimAttributeFactory.getInstance(); - } - - @Test - public void testConstructorInvalidNonceLength() { - try { - new AtNonceMt(INVALID_NONCE); - fail("Expected EapSimAkaInvalidAttributeException for invalid NonceMt length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NONCE_MT); - EapSimAkaAttribute result = mEapSimAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtNonceMt); - AtNonceMt atNonceMt = (AtNonceMt) result; - assertEquals(EAP_AT_NONCE_MT, atNonceMt.attributeType); - assertEquals(EXPECTED_LENGTH, atNonceMt.lengthInBytes); - assertArrayEquals(NONCE_MT, atNonceMt.nonceMt); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NONCE_INVALID_LENGTH); - try { - mEapSimAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid attribute length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws EapSimAkaInvalidAttributeException { - AtNonceMt atNonceMt = new AtNonceMt(NONCE_MT); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atNonceMt.encode(result); - assertArrayEquals(AT_NONCE_MT, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNonceSTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNonceSTest.java deleted file mode 100644 index 1ad64669..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNonceSTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NONCE_S; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NONCE_S; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NONCE_S_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_S; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceS; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtNonceSTest { - private static final byte[] INVALID_NONCE = new byte[10]; - private static final int EXPECTED_LENGTH = 20; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testConstructorInvalidNonceLength() { - try { - new AtNonceS(INVALID_NONCE); - fail("Expected EapSimAkaInvalidAttributeException for invalid NonceMt length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NONCE_S); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtNonceS); - AtNonceS atNonceS = (AtNonceS) result; - assertEquals(EAP_AT_NONCE_S, atNonceS.attributeType); - assertEquals(EXPECTED_LENGTH, atNonceS.lengthInBytes); - assertArrayEquals(hexStringToByteArray(NONCE_S), atNonceS.nonceS); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NONCE_S_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtNonceS atNonceS = new AtNonceS(hexStringToByteArray(NONCE_S)); - - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - atNonceS.encode(result); - assertArrayEquals(AT_NONCE_S, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNotificationTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNotificationTest.java deleted file mode 100644 index 2db3cbb2..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtNotificationTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToInt; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_POST_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NOTIFICATION_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_NOTIFICATION_INVALID_STATE; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NOTIFICATION_CODE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtNotificationTest { - private static final int EXPECTED_LENGTH = 4; - private static final int UNKNOWN_CODE = 0xA0FF; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NOTIFICATION); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtNotification); - AtNotification atNotification = (AtNotification) result; - assertEquals(EAP_AT_NOTIFICATION, atNotification.attributeType); - assertEquals(EXPECTED_LENGTH, atNotification.lengthInBytes); - assertTrue(atNotification.isSuccessCode); - assertFalse(atNotification.isPreSuccessfulChallenge); - assertEquals(hexStringToInt(NOTIFICATION_CODE), atNotification.notificationCode); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NOTIFICATION_INVALID_LENGTH); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid attribute length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testDecodeInvalidState() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_NOTIFICATION_INVALID_STATE); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid state"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtNotification atNotification = new AtNotification(hexStringToInt(NOTIFICATION_CODE)); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atNotification.encode(result); - assertArrayEquals(AT_NOTIFICATION, result.array()); - } - - @Test - public void testToString() throws Exception { - AtNotification knownCode = new AtNotification(GENERAL_FAILURE_POST_CHALLENGE); - AtNotification unknownCode = new AtNotification(UNKNOWN_CODE); - - assertNotNull(knownCode.toString()); - assertNotNull(unknownCode.toString()); - assertNotEquals(knownCode.toString(), unknownCode.toString()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtPaddingTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtPaddingTest.java deleted file mode 100644 index d310d504..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtPaddingTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PADDING; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_PADDING; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_PADDING_INVALID_PADDING; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAtPaddingException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPadding; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtPaddingTest { - private static final int EXPECTED_LENGTH = 8; - - private EapSimAkaAttributeFactory mAttributeFactory; - - @Before - public void setUp() { - mAttributeFactory = new EapSimAkaAttributeFactory() {}; - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_PADDING); - EapSimAkaAttribute result = mAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtPadding); - AtPadding atPadding = (AtPadding) result; - assertEquals(EAP_AT_PADDING, atPadding.attributeType); - assertEquals(EXPECTED_LENGTH, atPadding.lengthInBytes); - } - - @Test - public void testDecodeInvalidPadding() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_PADDING_INVALID_PADDING); - try { - mAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAtPaddingException for nonzero padding bytes"); - } catch (EapSimAkaInvalidAtPaddingException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtPadding atPadding = new AtPadding(EXPECTED_LENGTH); - - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - atPadding.encode(result); - - assertFalse(result.hasRemaining()); - assertArrayEquals(AT_PADDING, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtRandAkaTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtRandAkaTest.java deleted file mode 100644 index bdffdda9..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtRandAkaTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RAND_AKA; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RAND_AKA_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapAkaAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtRandAkaTest { - private EapAkaAttributeFactory mEapAkaAttributeFactory; - - @Before - public void setUp() { - mEapAkaAttributeFactory = EapAkaAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RAND_AKA); - EapSimAkaAttribute result = mEapAkaAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtRandAka atRandAka = (AtRandAka) result; - assertEquals(EAP_AT_RAND, atRandAka.attributeType); - assertEquals(AT_RAND_AKA.length, atRandAka.lengthInBytes); - assertArrayEquals(RAND_1_BYTES, atRandAka.rand); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RAND_AKA_INVALID_LENGTH); - try { - mEapAkaAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - - ByteBuffer result = ByteBuffer.allocate(AT_RAND_AKA.length); - atRandAka.encode(result); - assertArrayEquals(AT_RAND_AKA, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtRandSimTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtRandSimTest.java deleted file mode 100644 index 7456be6c..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtRandSimTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RAND_SIM; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RAND_SIM_DUPLICATE_RANDS; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RAND_SIM_INVALID_NUM_RANDS; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_2; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimInvalidAtRandException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandSim; -import com.android.internal.net.eap.message.simaka.EapSimAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtRandSimTest { - private static final int EXPECTED_NUM_RANDS = 2; - - private EapSimAttributeFactory mEapSimAttributeFactory; - - @Before - public void setUp() { - mEapSimAttributeFactory = EapSimAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RAND_SIM); - EapSimAkaAttribute result = mEapSimAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtRandSim); - AtRandSim atRandSim = (AtRandSim) result; - assertEquals(EAP_AT_RAND, atRandSim.attributeType); - assertEquals(AT_RAND_SIM.length, atRandSim.lengthInBytes); - assertEquals(EXPECTED_NUM_RANDS, atRandSim.rands.size()); - assertArrayEquals(hexStringToByteArray(RAND_1), atRandSim.rands.get(0)); - assertArrayEquals(hexStringToByteArray(RAND_2), atRandSim.rands.get(1)); - } - - @Test - public void testDecodeInvalidNumRands() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RAND_SIM_INVALID_NUM_RANDS); - try { - mEapSimAttributeFactory.getAttribute(input); - fail("Expected EapSimInvalidAtRandException for invalid number of RANDs"); - } catch (EapSimInvalidAtRandException expected) { - } - } - - @Test - public void testDecodeDuplicateRands() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RAND_SIM_DUPLICATE_RANDS); - try { - mEapSimAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for duplicate RANDs"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - byte[][] expectedRands = new byte[][] { - hexStringToByteArray(RAND_1), - hexStringToByteArray(RAND_2) - }; - AtRandSim atRandSim = new AtRandSim(AT_RAND_SIM.length, expectedRands); - - ByteBuffer result = ByteBuffer.allocate(AT_RAND_SIM.length); - atRandSim.encode(result); - assertArrayEquals(AT_RAND_SIM, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtResTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtResTest.java deleted file mode 100644 index 34c2ff39..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtResTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RES_INVALID_RES_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RES_LONG_RES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_RES_SHORT_RES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RES_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapAkaAttributeFactory; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRes; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtResTest { - private EapAkaAttributeFactory mEapAkaAttributeFactory; - - @Before - public void setUp() { - mEapAkaAttributeFactory = EapAkaAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RES); - EapSimAkaAttribute result = mEapAkaAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - AtRes atRes = (AtRes) result; - assertEquals(EAP_AT_RES, atRes.attributeType); - assertEquals(AT_RES.length, atRes.lengthInBytes); - assertArrayEquals(RES_BYTES, atRes.res); - } - - @Test - public void testDecodeInvalidResLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RES_INVALID_RES_LENGTH); - try { - mEapAkaAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid RES length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testDecodeShortResLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RES_SHORT_RES); - try { - mEapAkaAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for too short RES"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testDecodeLongResLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_RES_LONG_RES); - try { - mEapAkaAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for too long RES"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtRes atRes = new AtRes(AT_RES.length, RES_BYTES); - - ByteBuffer result = ByteBuffer.allocate(AT_RES.length); - atRes.encode(result); - assertArrayEquals(AT_RES, result.array()); - } - - @Test - public void testGetAtRes() throws Exception { - AtRes atRes = AtRes.getAtRes(RES_BYTES); - - ByteBuffer result = ByteBuffer.allocate(AT_RES.length); - atRes.encode(result); - assertArrayEquals(AT_RES, result.array()); - } - - @Test - public void testIsValidResLen() { - // valid RES length: 4 <= RES length <= 16 - assertTrue(AtRes.isValidResLen(5)); - assertFalse(AtRes.isValidResLen(0)); - assertFalse(AtRes.isValidResLen(20)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtSelectedVersionTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtSelectedVersionTest.java deleted file mode 100644 index 659fe9a8..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtSelectedVersionTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_SELECTED_VERSION; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_SELECTED_VERSION; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_SELECTED_VERSION_INVALID_LENGTH; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtSelectedVersion; -import com.android.internal.net.eap.message.simaka.EapSimAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class AtSelectedVersionTest { - private static final int EXPECTED_LENGTH = 4; - private static final int EXPECTED_VERSION = 1; - - private EapSimAttributeFactory mEapSimAttributeFactory; - - @Before - public void setUp() { - mEapSimAttributeFactory = EapSimAttributeFactory.getInstance(); - } - - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_SELECTED_VERSION); - EapSimAkaAttribute result = mEapSimAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtSelectedVersion); - AtSelectedVersion atSelectedVersion = (AtSelectedVersion) result; - assertEquals(EAP_AT_SELECTED_VERSION, atSelectedVersion.attributeType); - assertEquals(EXPECTED_LENGTH, atSelectedVersion.lengthInBytes); - assertEquals(EXPECTED_VERSION, atSelectedVersion.selectedVersion); - } - - @Test - public void testDecodeInvalidLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_SELECTED_VERSION_INVALID_LENGTH); - try { - mEapSimAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid actual list length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtSelectedVersion atSelectedVersion = new AtSelectedVersion( - EXPECTED_LENGTH, EXPECTED_VERSION); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atSelectedVersion.encode(result); - assertArrayEquals(AT_SELECTED_VERSION, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtVersionListTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtVersionListTest.java deleted file mode 100644 index 96bb7ca3..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/AtVersionListTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToInt; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_VERSION_LIST; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_VERSION_LIST; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_VERSION_LIST_INVALID_LENGTH; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.VERSION; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; -import com.android.internal.net.eap.message.simaka.EapSimAttributeFactory; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; - -public class AtVersionListTest { - private static final int EXPECTED_LENGTH = 8; - private static final List<Integer> EXPECTED_VERSIONS = Arrays.asList(1); - - private EapSimAttributeFactory mEapSimAttributeFactory; - - @Before - public void setUp() { - mEapSimAttributeFactory = EapSimAttributeFactory.getInstance(); - } - - @Test - public void testDecode() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_VERSION_LIST); - EapSimAkaAttribute result = mEapSimAttributeFactory.getAttribute(input); - - assertFalse(input.hasRemaining()); - assertTrue(result instanceof AtVersionList); - AtVersionList atVersionList = (AtVersionList) result; - assertEquals(EAP_AT_VERSION_LIST, atVersionList.attributeType); - assertEquals(EXPECTED_LENGTH, atVersionList.lengthInBytes); - assertEquals(EXPECTED_VERSIONS, atVersionList.versions); - } - - @Test - public void testDecodeInvalidActualLength() throws Exception { - ByteBuffer input = ByteBuffer.wrap(AT_VERSION_LIST_INVALID_LENGTH); - try { - mEapSimAttributeFactory.getAttribute(input); - fail("Expected EapSimAkaInvalidAttributeException for invalid actual list length"); - } catch (EapSimAkaInvalidAttributeException expected) { - } - } - - @Test - public void testEncode() throws Exception { - AtVersionList atVersionList = new AtVersionList(EXPECTED_LENGTH, hexStringToInt(VERSION)); - ByteBuffer result = ByteBuffer.allocate(EXPECTED_LENGTH); - - atVersionList.encode(result); - assertArrayEquals(AT_VERSION_LIST, result.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/EapSimAkaAttributeTest.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/EapSimAkaAttributeTest.java deleted file mode 100644 index 98ea222c..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/EapSimAkaAttributeTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; - -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; - -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class EapSimAkaAttributeTest { - private static final int EXPECTED_ATTRIBUTE_TYPE = 1; - private static final int EXPECTED_LENGTH_IN_BYTES = 4; - private static final int BUFFER_LENGTH = 2; - private static final int EXPECTED_LENGTH_ENCODED = 1; - private static final byte[] EXPECTED_ENCODING = { - (byte) EXPECTED_ATTRIBUTE_TYPE, - (byte) EXPECTED_LENGTH_ENCODED - }; - - @Test - public void testEncode() throws Exception { - EapSimAkaAttribute eapSimAkaAttribute = new EapSimAkaAttribute( - EXPECTED_ATTRIBUTE_TYPE, - EXPECTED_LENGTH_IN_BYTES) { - public void encode(ByteBuffer byteBuffer) { - encodeAttributeHeader(byteBuffer); - } - }; - - ByteBuffer result = ByteBuffer.allocate(BUFFER_LENGTH); - eapSimAkaAttribute.encode(result); - assertArrayEquals(EXPECTED_ENCODING, result.array()); - assertFalse(result.hasRemaining()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/EapTestAttributeDefinitions.java b/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/EapTestAttributeDefinitions.java deleted file mode 100644 index 60397e1f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/message/simaka/attributes/EapTestAttributeDefinitions.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.message.simaka.attributes; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; - -/** - * EapTestAttributeDefinitions provides byte[] encodings of commonly used EAP Messages. - * - * @ee <a href="https://tools.ietf.org/html/rfc4186#section-10">RFC 4186, EAP-SIM Authentication, - * Section 10</a> - * @see <a href="https://tools.ietf.org/html/rfc4187#section-10">RFC 4187, EAP-AKA Authentication, - * Section 10</a> - */ -public class EapTestAttributeDefinitions { - public static final String VERSION = "0001"; - public static final String AT_VERSION_LIST_DATA = "0002" + VERSION + "0000"; - public static final byte[] AT_VERSION_LIST = - hexStringToByteArray("0F02" + AT_VERSION_LIST_DATA); - public static final byte[] AT_SELECTED_VERSION = hexStringToByteArray("10010001"); - public static final String NONCE_MT_STRING = "0123456789ABCDEFFEDCBA9876543210"; - public static final byte[] NONCE_MT = hexStringToByteArray(NONCE_MT_STRING); - public static final byte[] AT_NONCE_MT = hexStringToByteArray("07050000" + NONCE_MT_STRING); - public static final byte[] AT_PERMANENT_ID_REQ = hexStringToByteArray("0A010000"); - public static final byte[] AT_ANY_ID_REQ = hexStringToByteArray("0D010000"); - public static final byte[] AT_FULL_AUTH_ID_REQ = hexStringToByteArray("11010000"); - - // Identity = "test1@android.net" - public static final String IDENTITY_STRING = "746573743140616E64726F69642E6E6574"; - public static final byte[] IDENTITY = hexStringToByteArray(IDENTITY_STRING); - public static final byte[] AT_IDENTITY = - hexStringToByteArray("0E060011" + IDENTITY_STRING + "000000"); - public static final String RAND_1 = "00112233445566778899AABBCCDDEEFF"; - public static final byte[] RAND_1_BYTES = hexStringToByteArray(RAND_1); - public static final String RAND_2 = "FFEEDDCCBBAA99887766554433221100"; - public static final byte[] RAND_2_BYTES = hexStringToByteArray(RAND_2); - public static final byte[] AT_RAND_SIM = hexStringToByteArray("01090000" + RAND_1 + RAND_2); - public static final byte[] AT_RAND_AKA = hexStringToByteArray("01050000" + RAND_1); - public static final byte[] AT_PADDING = hexStringToByteArray("0602000000000000"); - public static final String MAC = "112233445566778899AABBCCDDEEFF11"; - public static final byte[] MAC_BYTES = hexStringToByteArray(MAC); - public static final byte[] AT_MAC = hexStringToByteArray("0B050000" + MAC); - public static final String COUNTER = "000A"; - public static final int COUNTER_INT = Integer.parseInt(COUNTER, 16 /* radix */); - public static final byte[] AT_COUNTER = hexStringToByteArray("1301" + COUNTER); - public static final byte[] AT_COUNTER_TOO_SMALL = hexStringToByteArray("14010000"); - public static final String NONCE_S = "0123456789ABCDEFFEDCBA9876543210"; - public static final byte[] AT_NONCE_S = hexStringToByteArray("15050000" + NONCE_S); - public static final String NOTIFICATION_CODE = "8000"; - public static final byte[] AT_NOTIFICATION = hexStringToByteArray("0C01" + NOTIFICATION_CODE); - public static final String ERROR_CODE = "0001"; - public static final byte[] AT_CLIENT_ERROR_CODE = hexStringToByteArray("1601" + ERROR_CODE); - public static final String AUTN = "0123456789ABCDEFFEDCBA9876543210"; - public static final byte[] AUTN_BYTES = hexStringToByteArray(AUTN); - public static final byte[] AT_AUTN = hexStringToByteArray("02050000" + AUTN); - public static final String RES = "1122334455"; - public static final byte[] RES_BYTES = hexStringToByteArray(RES); - public static final byte[] AT_RES = hexStringToByteArray("03030028" + RES + "000000"); - public static final String AUTS = "112233445566778899AABBCCDDEE"; - public static final byte[] AUTS_BYTES = hexStringToByteArray(AUTS); - public static final byte[] AT_AUTS = hexStringToByteArray("0404" + AUTS); - public static final byte[] AT_BIDDING_SUPPORTS_AKA_PRIME = hexStringToByteArray("88018000"); - public static final byte[] AT_BIDDING_DOES_NOT_SUPPORT_AKA_PRIME = - hexStringToByteArray("88010000"); - - // Network Name = "android.net" - public static final String NETWORK_NAME_HEX = "616E64726F69642E6E6574"; - public static final byte[] NETWORK_NAME_BYTES = hexStringToByteArray(NETWORK_NAME_HEX); - public static final byte[] AT_KDF_INPUT = - hexStringToByteArray("1704000B" + NETWORK_NAME_HEX + "00"); - public static final byte[] AT_KDF_INPUT_EMPTY_NETWORK_NAME = hexStringToByteArray("17010000"); - public static final int KDF_VERSION = 1; - public static final byte[] AT_KDF = hexStringToByteArray("18010001"); - - public static final byte[] AT_VERSION_LIST_INVALID_LENGTH = hexStringToByteArray("0F020003"); - public static final byte[] AT_SELECTED_VERSION_INVALID_LENGTH = - hexStringToByteArray("10020001"); - public static final byte[] AT_NONCE_INVALID_LENGTH = - hexStringToByteArray("07060000" + NONCE_MT_STRING); - public static final byte[] PERMANENT_ID_INVALID_LENGTH = hexStringToByteArray("0A020000"); - public static final byte[] ANY_ID_INVALID_LENGTH = hexStringToByteArray("0D020000"); - public static final byte[] FULL_AUTH_ID_INVALID_LENGTH = hexStringToByteArray("11020000"); - public static final byte[] AT_RAND_SIM_INVALID_NUM_RANDS = - hexStringToByteArray("01050000" + RAND_1); - public static final byte[] AT_RAND_SIM_DUPLICATE_RANDS = - hexStringToByteArray("01090000" + RAND_1 + RAND_1); - public static final byte[] AT_RAND_AKA_INVALID_LENGTH = hexStringToByteArray("01010000"); - public static final byte[] AT_PADDING_INVALID_PADDING = hexStringToByteArray("0601FFFF"); - public static final byte[] AT_MAC_INVALID_LENGTH = hexStringToByteArray("0B06"); - public static final byte[] AT_COUNTER_INVALID_LENGTH = hexStringToByteArray("1302"); - public static final byte[] AT_COUNTER_TOO_SMALL_INVALID_LENGTH = hexStringToByteArray("1402"); - public static final byte[] AT_NONCE_S_INVALID_LENGTH = hexStringToByteArray("1506"); - public static final byte[] AT_NOTIFICATION_INVALID_LENGTH = hexStringToByteArray("0C02"); - public static final byte[] AT_NOTIFICATION_INVALID_STATE = hexStringToByteArray("0C01C000"); - public static final byte[] AT_CLIENT_ERROR_CODE_INVALID_LENGTH = hexStringToByteArray("1602"); - public static final byte[] AT_AUTN_INVALID_LENGTH = hexStringToByteArray("02010000"); - public static final byte[] AT_RES_INVALID_RES_LENGTH = - hexStringToByteArray("030300241122334450000000"); - public static final byte[] AT_RES_SHORT_RES = - hexStringToByteArray("0302000811000000"); - public static final byte[] AT_RES_LONG_RES = - hexStringToByteArray("0306008800112233445566778899AABBCCDDEEFF11000000"); - public static final byte[] AT_AUTS_INVALID_LENGTH = hexStringToByteArray("03010000"); - public static final byte[] AT_KDF_INVALID_LENGTH = hexStringToByteArray("18020001"); - public static final byte[] AT_BIDDING_INVALID_LENGTH = hexStringToByteArray("88020000"); -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/CreatedStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/CreatedStateTest.java deleted file mode 100644 index a452fa6f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/CreatedStateTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_IDENTITY_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.statemachine.EapStateMachine.IdentityState; -import com.android.internal.net.eap.statemachine.EapStateMachine.MethodState; - -import org.junit.Before; -import org.junit.Test; - -public class CreatedStateTest extends EapStateTest { - private EapStateMachine mEapStateMachineSpy; - - @Before - @Override - public void setUp() { - super.setUp(); - - mEapStateMachineSpy = spy(mEapStateMachine); - mEapState = mEapStateMachineSpy.new CreatedState(); - } - - @Test - public void testProcessIdentityRequest() { - mEapState.process(EAP_REQUEST_IDENTITY_PACKET); - - verify(mEapStateMachineSpy).transitionAndProcess( - any(IdentityState.class), eq(EAP_REQUEST_IDENTITY_PACKET)); - } - - @Test - public void testProcessNotificationRequest() { - EapResult eapResult = mEapState.process(EAP_REQUEST_NOTIFICATION_PACKET); - - // state shouldn't change after Notification request - assertTrue(eapResult instanceof EapResponse); - EapResponse eapResponse = (EapResponse) eapResult; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - verify(mEapStateMachineSpy, never()).transitionAndProcess(any(), any()); - } - - @Test - public void testProcessSimStart() { - mEapState.process(EAP_REQUEST_SIM_START_PACKET); - - // EapStateMachine should change to MethodState for method-type packet - verify(mEapStateMachineSpy).transitionAndProcess( - any(MethodState.class), eq(EAP_REQUEST_SIM_START_PACKET)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaChallengeStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaChallengeStateTest.java deleted file mode 100644 index b07d1ff3..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaChallengeStateTest.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_IDENTITY; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.CK_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_AUTHENTICATION_REJECT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CHALLENGE_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_SYNCHRONIZATION_FAILURE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_UICC_RESP_INVALID_TAG; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_UICC_RESP_SUCCESS_BASE_64; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_UICC_RESP_SYNCHRONIZE_BASE_64; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EMSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IK_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSK; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AUTN_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AUTS_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.IDENTITY; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RES_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.simaka.EapAkaInvalidAuthenticationResponse; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidLengthException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtBidding; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.ChallengeState; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.ChallengeState.RandChallengeResult; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; - -public class EapAkaChallengeStateTest extends EapAkaStateTest { - private ChallengeState mChallengeState; - - // '10' + RAND_1_BYTES + '10' + AUTN_BYTES - private static final String BASE_64_CHALLENGE = - "EAARIjNEVWZ3iJmqu8zd7v8QASNFZ4mrze/+3LqYdlQyEA=="; - - /** - * Process to generate MAC: - * - * message = 01100044 | EAP-Request, ID, length in bytes - * 17010000 | EAP-AKA, AKA-Challenge, padding - * 0105000000112233445566778899AABBCCDDEEFF | AT_RAND - * 020500000123456789ABCDEFFEDCBA9876543210 | AT_AUTN - * 0B05000000000000000000000000000000000000 | AT_MAC (zeroed out) - * - * MK = SHA-1(Identity | IK | CK) - * K_encr, K_aut, MSK, EMSK = PRF(MK) - * MAC = HMAC-SHA-1(K_aut, message) - */ - private static final byte[] REQUEST_MAC_BYTES = - hexStringToByteArray("3EB97A1D0E62894FD0DA384D24D8983C"); - - /** - * message = 01100048 | EAP-Request, ID, length in bytes - * 17010000 | EAP-AKA, AKA-Challenge, padding - * 0105000000112233445566778899AABBCCDDEEFF | AT_RAND - * 020500000123456789ABCDEFFEDCBA9876543210 | AT_AUTN - * 88018000 | AT_BIDDING - * 0B05000000000000000000000000000000000000 | AT_MAC (zeroed out) - */ - private static final byte[] BIDDING_DOWN_MAC = - hexStringToByteArray("9CB543894A5EFDC32DF6A6CE1AB0E01A"); - - @Before - public void setUp() { - super.setUp(); - - mChallengeState = mEapAkaMethodStateMachine.new ChallengeState(IDENTITY); - mEapAkaMethodStateMachine.transitionTo(mChallengeState); - } - - @Test - public void testProcessIncorrectEapMethodType() throws Exception { - EapData eapData = new EapData(EAP_IDENTITY, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapResult result = mChallengeState.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessSuccess() throws Exception { - System.arraycopy(MSK, 0, mEapAkaMethodStateMachine.mMsk, 0, MSK.length); - System.arraycopy(EMSK, 0, mEapAkaMethodStateMachine.mEmsk, 0, EMSK.length); - - mChallengeState.mHadSuccessfulChallenge = true; - EapMessage input = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - - EapSuccess eapSuccess = (EapSuccess) mEapAkaMethodStateMachine.process(input); - assertArrayEquals(MSK, eapSuccess.msk); - assertArrayEquals(EMSK, eapSuccess.emsk); - assertTrue(mEapAkaMethodStateMachine.getState() instanceof FinalState); - } - - @Test - public void testProcessInvalidSuccess() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - - EapError eapError = (EapError) mEapAkaMethodStateMachine.process(input); - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessFailure() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_FAILURE, ID_INT, null); - EapResult result = mEapAkaMethodStateMachine.process(input); - assertTrue(mEapAkaMethodStateMachine.getState() instanceof FinalState); - - assertTrue(result instanceof EapFailure); - } - - @Test - public void testProcessMissingAtRand() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(REQUEST_MAC_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData(EAP_AKA_CHALLENGE, Arrays.asList(atAutn, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessMissingAtAutn() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtMac atMac = new AtMac(REQUEST_MAC_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData(EAP_AKA_CHALLENGE, Arrays.asList(atRandAka, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessMissingAtMac() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData(EAP_AKA_CHALLENGE, Arrays.asList(atRandAka, atAutn))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testRandChallengeResultConstructor() throws Exception { - RandChallengeResult result = - mChallengeState.new RandChallengeResult(RES_BYTES, IK_BYTES, CK_BYTES); - assertArrayEquals(RES_BYTES, result.res); - assertArrayEquals(IK_BYTES, result.ik); - assertArrayEquals(CK_BYTES, result.ck); - assertNull(result.auts); - - result = mChallengeState.new RandChallengeResult(AUTS_BYTES); - assertArrayEquals(AUTS_BYTES, result.auts); - assertNull(result.res); - assertNull(result.ik); - assertNull(result.ck); - - try { - mChallengeState.new RandChallengeResult(new byte[0], IK_BYTES, CK_BYTES); - fail("Expected EapSimAkaInvalidLengthException for invalid RES length"); - } catch (EapSimAkaInvalidLengthException ex) { - } - - try { - mChallengeState.new RandChallengeResult(RES_BYTES, new byte[0], CK_BYTES); - fail("Expected EapSimAkaInvalidLengthException for invalid IK length"); - } catch (EapSimAkaInvalidLengthException ex) { - } - - try { - mChallengeState.new RandChallengeResult(RES_BYTES, IK_BYTES, new byte[0]); - fail("Expected EapSimAkaInvalidLengthException for invalid CK length"); - } catch (EapSimAkaInvalidLengthException ex) { - } - - try { - mChallengeState.new RandChallengeResult(new byte[0]); - fail("Expected EapSimAkaInvalidLengthException for invalid AUTS length"); - } catch (EapSimAkaInvalidLengthException ex) { - } - } - - @Test - public void testProcessIccAuthenticationNullResponse() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(REQUEST_MAC_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)) - .thenReturn(null); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_AUTHENTICATION_REJECT, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessIccAuthenticationInvalidTag() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(REQUEST_MAC_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)) - .thenReturn(EAP_AKA_UICC_RESP_INVALID_TAG); - - EapError eapError = (EapError) mEapAkaMethodStateMachine.process(eapMessage); - assertTrue(eapError.cause instanceof EapAkaInvalidAuthenticationResponse); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessIccAuthenticationSynchronizeTag() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(REQUEST_MAC_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)) - .thenReturn(EAP_AKA_UICC_RESP_SYNCHRONIZE_BASE_64); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_SYNCHRONIZATION_FAILURE, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessValidChallenge() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(REQUEST_MAC_BYTES); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData( - EAP_AKA_CHALLENGE, Arrays.asList(atRandAka, atAutn, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)) - .thenReturn(EAP_AKA_UICC_RESP_SUCCESS_BASE_64); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_CHALLENGE_RESPONSE, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessBiddingDownAttack() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtBidding atBidding = new AtBidding(true); - AtMac atMac = new AtMac(BIDDING_DOWN_MAC); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atBidding, atMac))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)) - .thenReturn(EAP_AKA_UICC_RESP_SUCCESS_BASE_64); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_AUTHENTICATION_REJECT, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaCreatedStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaCreatedStateTest.java deleted file mode 100644 index 6e100dbf..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaCreatedStateTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.ChallengeState; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.IdentityState; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; - -import org.junit.Test; - -import java.util.LinkedHashMap; - -public class EapAkaCreatedStateTest extends EapAkaStateTest { - @Test - public void testProcessTransitionToIdentityState() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // Don't actually need any attributes in the attributeMap, since we only care about the - // state transition here. - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaTypeData(EAP_AKA_IDENTITY, new LinkedHashMap<>())); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mEapAkaMethodStateMachine.process(eapMessage); - - assertTrue(mEapAkaMethodStateMachine.getState() instanceof IdentityState); - - // decoded in CreatedState and IdentityState - verify(mMockEapAkaTypeDataDecoder, times(2)).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - } - - @Test - public void testProcessTransitionToChallengeState() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // Don't actually need any attributes in the attributeMap, since we only care about the - // state transition here. - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaTypeData(EAP_AKA_CHALLENGE, new LinkedHashMap<>())); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mEapAkaMethodStateMachine.process(eapMessage); - - ChallengeState challengeState = (ChallengeState) mEapAkaMethodStateMachine.getState(); - assertArrayEquals(EAP_IDENTITY_BYTES, challengeState.mIdentity); - - // decoded in CreatedState and ChallengeState - verify(mMockEapAkaTypeDataDecoder, times(2)).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - } - - @Test - public void testProcessSuccess() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - EapResult result = mEapAkaMethodStateMachine.process(input); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessFailure() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_FAILURE, ID_INT, null); - EapResult result = mEapAkaMethodStateMachine.process(input); - assertTrue(mEapAkaMethodStateMachine.getState() instanceof FinalState); - - assertTrue(result instanceof EapFailure); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaIdentityStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaIdentityStateTest.java deleted file mode 100644 index 20175f95..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaIdentityStateTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_IDENTITY_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IMSI; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaIdentityUnavailableException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPermanentIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.ChallengeState; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.IdentityState; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.LinkedHashMap; - -public class EapAkaIdentityStateTest extends EapAkaStateTest { - private IdentityState mIdentityState; - - @Before - public void setUp() { - super.setUp(); - - mIdentityState = mEapAkaMethodStateMachine.new IdentityState(); - mEapAkaMethodStateMachine.transitionTo(mIdentityState); - } - - @Test - public void testProcessIdentityRequest() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData(EAP_AKA_IDENTITY, Arrays.asList(new AtAnyIdReq()))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(IMSI); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_IDENTITY_RESPONSE, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager).getSubscriberId(); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessWithoutIdentityRequestAttributes() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaTypeData(EAP_AKA_IDENTITY, new LinkedHashMap<>())); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessMultipleIdentityRequestAttributes() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData( - EAP_AKA_IDENTITY, - Arrays.asList(new AtAnyIdReq(), new AtPermanentIdReq()))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessImsiUnavailable() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaTypeData(EAP_AKA_IDENTITY, Arrays.asList(new AtAnyIdReq()))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(null); - - EapError eapError = (EapError) mEapAkaMethodStateMachine.process(eapMessage); - assertTrue(eapError.cause instanceof EapSimAkaIdentityUnavailableException); - - verify(mMockEapAkaTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager).getSubscriberId(); - verifyNoMoreInteractions(mMockEapAkaTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessTransitionToChallengeState() throws Exception { - // transition to IdentityState so we can verify the transition to ChallengeState - IdentityState identityState = mEapAkaMethodStateMachine.new IdentityState(); - mEapAkaMethodStateMachine.transitionTo(identityState); - - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // Don't actually need any attributes in the attributeMap, since we only care about the - // state transition here. - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaTypeData(EAP_AKA_CHALLENGE, new LinkedHashMap<>())); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mEapAkaMethodStateMachine.process(eapMessage); - - assertTrue(mEapAkaMethodStateMachine.getState() instanceof ChallengeState); - - // decoded in IdentityState and ChallengeState - verify(mMockEapAkaTypeDataDecoder, times(2)).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaMethodStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaMethodStateMachineTest.java deleted file mode 100644 index db2899b6..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaMethodStateMachineTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_NOTIFICATION_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IMSI; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_PRE_CHALLENGE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig.EapAkaConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData.EapAkaTypeDataDecoder; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.CreatedState; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; - -public class EapAkaMethodStateMachineTest { - private static final int SUB_ID = 1; - private static final byte[] DUMMY_EAP_TYPE_DATA = hexStringToByteArray("112233445566"); - - // EAP-Identity = hex("test@android.net") - protected static final byte[] EAP_IDENTITY_BYTES = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - protected TelephonyManager mMockTelephonyManager; - private EapAkaTypeDataDecoder mMockEapAkaTypeDataDecoder; - - private EapAkaConfig mEapAkaConfig = new EapAkaConfig(SUB_ID, APPTYPE_USIM); - private EapAkaMethodStateMachine mEapAkaMethodStateMachine; - - @Before - public void setUp() { - mMockTelephonyManager = mock(TelephonyManager.class); - mMockEapAkaTypeDataDecoder = mock(EapAkaTypeDataDecoder.class); - - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - - mEapAkaMethodStateMachine = - new EapAkaMethodStateMachine( - mMockTelephonyManager, - EAP_IDENTITY_BYTES, - mEapAkaConfig, - mMockEapAkaTypeDataDecoder, - false); - - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - } - - @Test - public void testEapAkaMethodStateMachineStartState() { - assertTrue(mEapAkaMethodStateMachine.getState() instanceof CreatedState); - } - - @Test - public void testGetEapMethod() { - assertEquals(EAP_TYPE_AKA, mEapAkaMethodStateMachine.getEapMethod()); - } - - @Test - public void testEapAkaFailsOnMultipleAkaNotifications() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // First EAP-AKA/Notification - EapAkaTypeData notificationTypeData = - new EapAkaTypeData( - EAP_AKA_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE))); - DecodeResult<EapAkaTypeData> decodeResult = new DecodeResult<>(notificationTypeData); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_NOTIFICATION_RESPONSE, eapResponse.packet); - verify(mMockEapAkaTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - - // Transition to IdentityState - decodeResult = - new DecodeResult<>( - new EapAkaTypeData(EAP_AKA_IDENTITY, Arrays.asList(new AtAnyIdReq()))); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(IMSI); - - eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertFalse( - "EAP-Request/AKA-Identity returned a Client-Error response", - Arrays.equals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet)); - - // decoded in: previous 1 time + in CreatedState and IdentityState - verify(mMockEapAkaTypeDataDecoder, times(3)).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager).getSubscriberId(); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - - // Second EAP-AKA/Notification - decodeResult = new DecodeResult<>(notificationTypeData); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapError eapError = (EapError) mEapAkaMethodStateMachine.process(eapMessage); - assertTrue(eapError.cause instanceof EapInvalidRequestException); - - // decoded previous 3 times + 1 - verify(mMockEapAkaTypeDataDecoder, times(4)).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeChallengeStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeChallengeStateTest.java deleted file mode 100644 index 731eb6bd..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeChallengeStateTest.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.CK_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_PRIME_AUTHENTICATION_REJECT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_PRIME_IDENTITY_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_PRIME_IDENTITY_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IK_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IMSI; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AUTN_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.MAC_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RES_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig; - -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdf; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtKdfInput; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.ChallengeState.RandChallengeResult; -import com.android.internal.net.eap.statemachine.EapAkaPrimeMethodStateMachine.ChallengeState; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; - -public class EapAkaPrimeChallengeStateTest extends EapAkaPrimeStateTest { - private static final String SERVER_NETWORK_NAME_STRING = "foo:bar:buzz"; - private static final byte[] SERVER_NETWORK_NAME = - SERVER_NETWORK_NAME_STRING.getBytes(StandardCharsets.UTF_8); - private static final String INCORRECT_NETWORK_NAME = "foo:buzz"; - private static final byte[] INCORRECT_SERVER_NETWORK_NAME = - INCORRECT_NETWORK_NAME.getBytes(StandardCharsets.UTF_8); - private static final int VALID_KDF = 1; - private static final int INVALID_KDF = 10; - - private static final byte[] EXPECTED_CK_IK_PRIME = - hexStringToByteArray( - "A0B37E7C7E9CC4F37A5C0AAA55DC87BE51FDA70A9D8F37E62E23B15F1B3941E6"); - private static final byte[] K_ENCR = hexStringToByteArray("15a5bb098528210cde9e8d4a1bd63850"); - private static final byte[] K_AUT = - hexStringToByteArray( - "957b3d518ac9ff028f2cc5177fedad841f5f812cb06e2b88aceaa98129680f35"); - private static final byte[] K_RE = - hexStringToByteArray( - "3c15cf7112935a8170d0904622ecbb67c49dcba5d50814bdd81958e045e42f9c"); - private static final byte[] MSK = - hexStringToByteArray( - "1dcca0351a58d2b858e6cf2380551470d67cc8749d1915409793171abd360118" - + "e3ae271bf088ca5a41bb1b9b8f7028bcba888298bfbf64d7b8a4f53a6c2cdf18"); - private static final byte[] EMSK = - hexStringToByteArray( - "a5e6b66a9cb2daa9fe3867d41145848e7bf50d749bfd1bb0d090257402e6a555" - + "da6d538e76b71e9f80afe60709965a63a355bdccc4e3a8b358e098e41545fa67"); - - private ChallengeState mState; - - @Before - public void setUp() { - super.setUp(); - - mState = mStateMachine.new ChallengeState(); - mStateMachine.transitionTo(mState); - } - - @Test - public void testTransitionWithEapIdentity() throws Exception { - mStateMachine.transitionTo(mStateMachine.new CreatedState()); - - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaPrimeTypeData(EAP_AKA_CHALLENGE, new ArrayList<>())); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mStateMachine.process(eapMessage); - - ChallengeState challengeState = (ChallengeState) mStateMachine.getState(); - assertArrayEquals(EAP_IDENTITY_BYTES, challengeState.mIdentity); - - // decode() is called in CreatedState and ChallengeState - verify(mMockTypeDataDecoder, times(2)).decode(eq(DUMMY_EAP_TYPE_DATA)); - } - - @Test - public void testTransitionWithEapAkaPrimeIdentity() throws Exception { - mStateMachine.transitionTo(mStateMachine.new CreatedState()); - - // Process AKA' Identity Request - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaPrimeTypeData(EAP_AKA_IDENTITY, Arrays.asList(new AtAnyIdReq()))); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(IMSI); - - EapResponse eapResponse = (EapResponse) mStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_PRIME_IDENTITY_RESPONSE, eapResponse.packet); - - // decode() is called in CreatedState and IdentityState - verify(mMockTypeDataDecoder, times(2)).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager).getSubscriberId(); - - // Process AKA' Challenge Request - decodeResult = - new DecodeResult<>(new EapAkaPrimeTypeData(EAP_AKA_CHALLENGE, new ArrayList<>())); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mStateMachine.process(eapMessage); - - ChallengeState challengeState = (ChallengeState) mStateMachine.getState(); - assertArrayEquals(EAP_AKA_PRIME_IDENTITY_BYTES, challengeState.mIdentity); - - // decode() called again in IdentityState and ChallengeState - verify(mMockTypeDataDecoder, times(4)).decode(eq(DUMMY_EAP_TYPE_DATA)); - } - - @Test - public void testProcessMissingAtKdf() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(MAC_BYTES); - AtKdfInput atKdfInput = new AtKdfInput(0, SERVER_NETWORK_NAME); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaPrimeTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac, atKdfInput))); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_PRIME_AUTHENTICATION_REJECT, eapResponse.packet); - verify(mMockTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - } - - @Test - public void testProcessMissingAtKdfInput() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(MAC_BYTES); - AtKdf atKdf = new AtKdf(VALID_KDF); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaPrimeTypeData( - EAP_AKA_CHALLENGE, Arrays.asList(atRandAka, atAutn, atMac, atKdf))); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_PRIME_AUTHENTICATION_REJECT, eapResponse.packet); - verify(mMockTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - } - - @Test - public void testProcessUnsupportedKdf() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(MAC_BYTES); - AtKdfInput atKdfInput = new AtKdfInput(0, SERVER_NETWORK_NAME); - AtKdf atKdf = new AtKdf(INVALID_KDF); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaPrimeTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac, atKdfInput, atKdf))); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_PRIME_AUTHENTICATION_REJECT, eapResponse.packet); - verify(mMockTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - } - - @Test - public void testProcessIncorrectNetworkName() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(MAC_BYTES); - AtKdfInput atKdfInput = new AtKdfInput(0, INCORRECT_SERVER_NETWORK_NAME); - AtKdf atKdf = new AtKdf(VALID_KDF); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaPrimeTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac, atKdfInput, atKdf))); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_PRIME_AUTHENTICATION_REJECT, eapResponse.packet); - verify(mMockTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - } - - @Test - public void testProcessIncorrectNetworkNameIsIgnored() throws Exception { - // Create state machine with configs allowing invalid network name to be ignored - mStateMachine = - new EapAkaPrimeMethodStateMachine( - mMockContext, - EAP_IDENTITY_BYTES, - new EapSessionConfig.EapAkaPrimeConfig( - SUB_ID, APPTYPE_USIM, PEER_NETWORK_NAME, true), - mMockTypeDataDecoder); - mState = mStateMachine.new ChallengeState(); - mStateMachine.transitionTo(mState); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(MAC_BYTES); - AtKdfInput atKdfInput = new AtKdfInput(0, INCORRECT_SERVER_NETWORK_NAME); - AtKdf atKdf = new AtKdf(VALID_KDF); - - EapAkaPrimeTypeData eapAkaPrimeTypeData = - new EapAkaPrimeTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac, atKdfInput, atKdf)); - assertTrue( - "Incorrect network names should be ignored", - mState.isValidChallengeAttributes(eapAkaPrimeTypeData)); - } - - @Test - public void testHasMatchingNetworkNames() { - // "" should match anything - assertTrue(mState.hasMatchingNetworkNames("", SERVER_NETWORK_NAME_STRING)); - assertTrue(mState.hasMatchingNetworkNames(SERVER_NETWORK_NAME_STRING, "")); - - // "foo:bar" should match "foo:bar:buzz" - assertTrue(mState.hasMatchingNetworkNames(PEER_NETWORK_NAME, SERVER_NETWORK_NAME_STRING)); - assertTrue(mState.hasMatchingNetworkNames(SERVER_NETWORK_NAME_STRING, PEER_NETWORK_NAME)); - - // "foo:buzz" shouldn't match "foo:bar:buzz" - assertFalse( - mState.hasMatchingNetworkNames(SERVER_NETWORK_NAME_STRING, INCORRECT_NETWORK_NAME)); - assertFalse( - mState.hasMatchingNetworkNames(INCORRECT_NETWORK_NAME, SERVER_NETWORK_NAME_STRING)); - } - - @Test - public void testDeriveCkIkPrime() throws Exception { - RandChallengeResult randChallengeResult = - mState.new RandChallengeResult(RES_BYTES, IK_BYTES, CK_BYTES); - AtKdfInput atKdfInput = - new AtKdfInput(0, PEER_NETWORK_NAME.getBytes(StandardCharsets.UTF_8)); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - - // S = FC | Network Name | len(Network Name) | SQN ^ AK | len(SQN ^ AK) - // = 20666F6F3A62617200070123456789AB0006 - // K = CK | IK - // = FFEEDDCCBBAA9988776655443322110000112233445566778899AABBCCDDEEFF - // CK' | IK' = HMAC-SHA256(K, S) - // = A0B37E7C7E9CC4F37A5C0AAA55DC87BE51FDA70A9D8F37E62E23B15F1B3941E6 - byte[] result = mState.deriveCkIkPrime(randChallengeResult, atKdfInput, atAutn); - assertArrayEquals(EXPECTED_CK_IK_PRIME, result); - } - - @Test - public void testGenerateAndPersistEapAkaKeys() throws Exception { - RandChallengeResult randChallengeResult = - mState.new RandChallengeResult(RES_BYTES, IK_BYTES, CK_BYTES); - - AtRandAka atRandAka = new AtRandAka(RAND_1_BYTES); - AtAutn atAutn = new AtAutn(AUTN_BYTES); - AtMac atMac = new AtMac(MAC_BYTES); - AtKdfInput atKdfInput = - new AtKdfInput(0, PEER_NETWORK_NAME.getBytes(StandardCharsets.UTF_8)); - AtKdf atKdf = new AtKdf(VALID_KDF); - - EapAkaPrimeTypeData eapAkaPrimeTypeData = - new EapAkaPrimeTypeData( - EAP_AKA_CHALLENGE, - Arrays.asList(atRandAka, atAutn, atMac, atKdfInput, atKdf)); - - // CK' | IK' = A0B37E7C7E9CC4F37A5C0AAA55DC87BE51FDA70A9D8F37E62E23B15F1B3941E6 - // data = "EAP-AKA'" | Identity - // = 4541502D414B41277465737440616E64726F69642E6E6574 - // prf+(CK' | IK', data) = T1 | T2 | T3 | T4 | T5 | T6 | T7 - // T1 = 15a5bb098528210cde9e8d4a1bd63850957b3d518ac9ff028f2cc5177fedad84 - // T2 = 1f5f812cb06e2b88aceaa98129680f353c15cf7112935a8170d0904622ecbb67 - // T3 = c49dcba5d50814bdd81958e045e42f9c1dcca0351a58d2b858e6cf2380551470 - // T4 = d67cc8749d1915409793171abd360118e3ae271bf088ca5a41bb1b9b8f7028bc - // T5 = ba888298bfbf64d7b8a4f53a6c2cdf18a5e6b66a9cb2daa9fe3867d41145848e - // T6 = 7bf50d749bfd1bb0d090257402e6a555da6d538e76b71e9f80afe60709965a63 - // T7 = a355bdccc4e3a8b358e098e41545fa677897d8341c4a107a2343f393ec966181 - // K_encr | K_aut | K_re | MSK | EMSK = prf+(CK' | IK', data) - assertNull( - mState.generateAndPersistEapAkaKeys(randChallengeResult, 0, eapAkaPrimeTypeData)); - assertArrayEquals(K_ENCR, mStateMachine.mKEncr); - assertArrayEquals(K_AUT, mStateMachine.mKAut); - assertArrayEquals(K_RE, mStateMachine.mKRe); - assertArrayEquals(MSK, mStateMachine.mMsk); - assertArrayEquals(EMSK, mStateMachine.mEmsk); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeCreatedStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeCreatedStateTest.java deleted file mode 100644 index 90d0bf66..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeCreatedStateTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaPrimeMethodStateMachine.ChallengeState; - -import org.junit.Test; - -import java.util.ArrayList; - -public class EapAkaPrimeCreatedStateTest extends EapAkaPrimeStateTest { - @Test - public void testProcessTransitionToIdentityState() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // Don't actually need any attributes in the attributeMap, since we only care about the - // state transition here. - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaPrimeTypeData(EAP_AKA_IDENTITY, new ArrayList<>())); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mStateMachine.process(eapMessage); - - assertTrue(mStateMachine.getState() instanceof EapAkaMethodStateMachine.IdentityState); - - // decoded in CreatedState and IdentityState - verify(mMockTypeDataDecoder, times(2)).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockTelephonyManager, mMockTypeDataDecoder); - } - - @Test - public void testProcessTransitionToChallengeState() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // Don't actually need any attributes in the attributeMap, since we only care about the - // state transition here. - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaPrimeTypeData(EAP_AKA_CHALLENGE, new ArrayList<>())); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mStateMachine.process(eapMessage); - - ChallengeState challengeState = (ChallengeState) mStateMachine.getState(); - assertArrayEquals(EAP_IDENTITY_BYTES, challengeState.mIdentity); - - // decoded in CreatedState and ChallengeState - verify(mMockTypeDataDecoder, times(2)).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockTypeDataDecoder); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeIdentityStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeIdentityStateTest.java deleted file mode 100644 index 73191fb0..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeIdentityStateTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_PRIME_IDENTITY_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IMSI; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapAkaPrimeMethodStateMachine.ChallengeState; - -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; - -public class EapAkaPrimeIdentityStateTest extends EapAkaPrimeStateTest { - @Before - public void setUp() { - super.setUp(); - - mStateMachine.transitionTo(mStateMachine.new IdentityState()); - } - - @Test - public void testProcessIdentityRequest() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>( - new EapAkaPrimeTypeData(EAP_AKA_IDENTITY, Arrays.asList(new AtAnyIdReq()))); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(IMSI); - - EapResponse eapResponse = (EapResponse) mStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_PRIME_IDENTITY_RESPONSE, eapResponse.packet); - - verify(mMockTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager).getSubscriberId(); - verifyNoMoreInteractions(mMockTypeDataDecoder, mMockTelephonyManager); - } - - @Test - public void testProcessTransitionToChallengeState() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // Don't actually need any attributes in the attributeMap, since we only care about the - // state transition here. - DecodeResult<EapAkaTypeData> decodeResult = - new DecodeResult<>(new EapAkaPrimeTypeData(EAP_AKA_CHALLENGE, new ArrayList<>())); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mStateMachine.process(eapMessage); - - assertTrue(mStateMachine.getState() instanceof ChallengeState); - - // decoded in IdentityState and ChallengeState - verify(mMockTypeDataDecoder, times(2)).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockTypeDataDecoder); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeMethodStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeMethodStateMachineTest.java deleted file mode 100644 index 125b3235..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeMethodStateMachineTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; -import static com.android.internal.net.eap.statemachine.EapAkaPrimeMethodStateMachine.K_AUT_LEN; -import static com.android.internal.net.eap.statemachine.EapAkaPrimeMethodStateMachine.K_RE_LEN; -import static com.android.internal.net.eap.statemachine.EapSimAkaMethodStateMachine.KEY_LEN; -import static com.android.internal.net.eap.statemachine.EapSimAkaMethodStateMachine.SESSION_KEY_LENGTH; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.statemachine.EapAkaMethodStateMachine.CreatedState; - -import org.junit.Test; - -import java.util.Arrays; - -public class EapAkaPrimeMethodStateMachineTest extends EapAkaPrimeTest { - private static final String TAG = EapAkaPrimeMethodStateMachineTest.class.getSimpleName(); - private static final byte[] K_AUT = - hexStringToByteArray( - "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"); - private static final byte[] MAC = hexStringToByteArray("0322b08b59cae2df8f766162ac76f30b"); - - @Test - public void testEapAkaPrimeMethodStateMachineStartState() { - assertTrue(mStateMachine.getState() instanceof CreatedState); - } - - @Test - public void testKeyLengths() { - assertEquals(KEY_LEN, mStateMachine.getKEncrLength()); - assertEquals(K_AUT_LEN, mStateMachine.getKAutLength()); - assertEquals(K_RE_LEN, mStateMachine.getKReLen()); - assertEquals(SESSION_KEY_LENGTH, mStateMachine.getMskLength()); - assertEquals(SESSION_KEY_LENGTH, mStateMachine.getEmskLength()); - } - - @Test - public void testIsValidMacUsesHmacSha256() throws Exception { - System.arraycopy(K_AUT, 0, mStateMachine.mKAut, 0, K_AUT.length); - - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, new byte[0]); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapAkaPrimeTypeData eapAkaPrimeTypeData = - new EapAkaPrimeTypeData(EAP_AKA_CHALLENGE, Arrays.asList(new AtMac(MAC))); - - assertTrue(mStateMachine.isValidMac(TAG, eapMessage, eapAkaPrimeTypeData, new byte[0])); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeStateTest.java deleted file mode 100644 index 18fce265..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeStateTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_PRIME_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.EapMethodState; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; - -import org.junit.Test; - -public class EapAkaPrimeStateTest extends EapAkaPrimeTest { - protected static final String NOTIFICATION_MESSAGE = "test"; - - @Test - public void testProcessNotification() throws Exception { - EapData eapData = new EapData(EAP_NOTIFICATION, NOTIFICATION_MESSAGE.getBytes()); - EapMessage notification = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preNotification = (EapMethodState) mStateMachine.getState(); - - EapResult result = mStateMachine.process(notification); - assertEquals(preNotification, mStateMachine.getState()); - verifyNoMoreInteractions(mMockTelephonyManager, mMockTypeDataDecoder); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - } - - @Test - public void testProcessInvalidDecodeResult() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA_PRIME, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preProcess = (EapMethodState) mStateMachine.getState(); - - AtClientErrorCode atClientErrorCode = AtClientErrorCode.UNABLE_TO_PROCESS; - DecodeResult<EapAkaTypeData> decodeResult = new DecodeResult<>(atClientErrorCode); - when(mMockTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResult result = mStateMachine.process(eapMessage); - assertEquals(preProcess, mStateMachine.getState()); - verify(mMockTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockTypeDataDecoder); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_AKA_PRIME_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - } - - @Test - public void testHandleEapFailure() throws Exception { - EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_FAILURE, ID_INT, null)); - assertTrue(result instanceof EapFailure); - assertTrue(mStateMachine.getState() instanceof FinalState); - } - - @Test - public void testHandleEapSuccess() throws Exception { - EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_SUCCESS, ID_INT, null)); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeTest.java deleted file mode 100644 index 48371bbd..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaPrimeTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.eap.EapSessionConfig.EapAkaPrimeConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.message.simaka.EapAkaPrimeTypeData; - -import org.junit.Before; - -public class EapAkaPrimeTest { - - // newtork name example in RFC 5448#3.1 - protected static final int SUB_ID = 1; - protected static final String PEER_NETWORK_NAME = "foo:bar"; - protected static final boolean ALLOW_MISMATCHED_NETWORK_NAMES = false; - protected static final EapAkaPrimeConfig EAP_AKA_PRIME_CONFIG = - new EapAkaPrimeConfig( - SUB_ID, APPTYPE_USIM, PEER_NETWORK_NAME, ALLOW_MISMATCHED_NETWORK_NAMES); - protected static final byte[] DUMMY_EAP_TYPE_DATA = hexStringToByteArray("112233445566"); - - // EAP-Identity = hex("test@android.net") - protected static final byte[] EAP_IDENTITY_BYTES = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - protected Context mMockContext; - protected TelephonyManager mMockTelephonyManager; - protected EapAkaPrimeTypeData.EapAkaPrimeTypeDataDecoder mMockTypeDataDecoder; - - protected EapAkaPrimeMethodStateMachine mStateMachine; - - @Before - public void setUp() { - mMockContext = mock(Context.class); - mMockTelephonyManager = mock(TelephonyManager.class); - mMockTypeDataDecoder = mock(EapAkaPrimeTypeData.EapAkaPrimeTypeDataDecoder.class); - - when(mMockContext.getSystemService(eq(Context.TELEPHONY_SERVICE))) - .thenReturn(mMockTelephonyManager); - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - - mStateMachine = - new EapAkaPrimeMethodStateMachine( - mMockContext, - EAP_IDENTITY_BYTES, - EAP_AKA_PRIME_CONFIG, - mMockTypeDataDecoder); - - verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE)); - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaStateTest.java deleted file mode 100644 index 23b240b9..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapAkaStateTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_NOTIFICATION_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_PRE_CHALLENGE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig.EapAkaConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapAkaTypeData.EapAkaTypeDataDecoder; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.EapMethodState; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; - -public class EapAkaStateTest { - protected static final int SUB_ID = 1; - protected static final String NOTIFICATION_MESSAGE = "test"; - protected static final byte[] DUMMY_EAP_TYPE_DATA = hexStringToByteArray("112233445566"); - - // EAP-Identity = hex("test@android.net") - protected static final byte[] EAP_IDENTITY_BYTES = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - protected TelephonyManager mMockTelephonyManager; - protected EapAkaTypeDataDecoder mMockEapAkaTypeDataDecoder; - - protected EapAkaConfig mEapAkaConfig = new EapAkaConfig(SUB_ID, APPTYPE_USIM); - protected EapAkaMethodStateMachine mEapAkaMethodStateMachine; - - @Before - public void setUp() { - mMockTelephonyManager = mock(TelephonyManager.class); - mMockEapAkaTypeDataDecoder = mock(EapAkaTypeDataDecoder.class); - - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - - mEapAkaMethodStateMachine = - new EapAkaMethodStateMachine( - mMockTelephonyManager, - EAP_IDENTITY_BYTES, - mEapAkaConfig, - mMockEapAkaTypeDataDecoder, - true); - - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - } - - @Test - public void testProcessNotification() throws Exception { - EapData eapData = new EapData(EAP_NOTIFICATION, NOTIFICATION_MESSAGE.getBytes()); - EapMessage notification = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preNotification = (EapMethodState) mEapAkaMethodStateMachine.getState(); - - EapResult result = mEapAkaMethodStateMachine.process(notification); - assertEquals(preNotification, mEapAkaMethodStateMachine.getState()); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - - assertTrue(result instanceof EapResult.EapResponse); - EapResult.EapResponse eapResponse = (EapResult.EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - } - - @Test - public void testProcessEapAkaNotification() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preProcess = (EapMethodState) mEapAkaMethodStateMachine.getState(); - EapAkaTypeData typeData = - new EapAkaTypeData( - EAP_AKA_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE))); - - DecodeResult<EapAkaTypeData> decodeResult = new DecodeResult<>(typeData); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapAkaMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_AKA_NOTIFICATION_RESPONSE, eapResponse.packet); - assertEquals(preProcess, mEapAkaMethodStateMachine.getState()); - verify(mMockEapAkaTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - } - - @Test - public void testProcessInvalidDecodeResult() throws Exception { - EapData eapData = new EapData(EAP_TYPE_AKA, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preProcess = (EapMethodState) mEapAkaMethodStateMachine.getState(); - - AtClientErrorCode atClientErrorCode = AtClientErrorCode.UNABLE_TO_PROCESS; - DecodeResult<EapAkaTypeData> decodeResult = new DecodeResult<>(atClientErrorCode); - when(mMockEapAkaTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResult result = mEapAkaMethodStateMachine.process(eapMessage); - assertEquals(preProcess, mEapAkaMethodStateMachine.getState()); - verify(mMockEapAkaTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapAkaTypeDataDecoder); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_AKA_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2AwaitingEapSuccessStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2AwaitingEapSuccessStateTest.java deleted file mode 100644 index d4377cb6..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2AwaitingEapSuccessStateTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_MSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_NT_RESPONSE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; - -import org.junit.Before; -import org.junit.Test; - -public class EapMsChapV2AwaitingEapSuccessStateTest extends EapMsChapV2StateTest { - @Before - @Override - public void setUp() { - super.setUp(); - - mStateMachine.transitionTo( - mStateMachine.new AwaitingEapSuccessState(MSCHAP_V2_NT_RESPONSE)); - } - - @Test - @Override - public void testHandleEapSuccess() throws Exception { - EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_SUCCESS, ID_INT, null)); - EapSuccess eapSuccess = (EapSuccess) result; - assertArrayEquals(MSCHAP_V2_MSK, eapSuccess.msk); - assertArrayEquals(new byte[0], eapSuccess.emsk); - assertTrue(mStateMachine.getState() instanceof FinalState); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2ChallengeStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2ChallengeStateTest.java deleted file mode 100644 index ec442dab..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2ChallengeStateTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_MSCHAP_V2_CHALLENGE_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_CHALLENGE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PEER_CHALLENGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SERVER_NAME_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeRequest.TYPE_DATA_HEADER_SIZE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeRequest.VALUE_SIZE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; -import com.android.internal.net.eap.statemachine.EapMsChapV2MethodStateMachine.ValidateAuthenticatorState; - -import org.junit.Before; -import org.junit.Test; - -public class EapMsChapV2ChallengeStateTest extends EapMsChapV2StateTest { - @Before - @Override - public void setUp() { - super.setUp(); - - mStateMachine.transitionTo(mStateMachine.new ChallengeState()); - } - - @Test - public void testProcessChallenge() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapMsChapV2ChallengeRequest challengeRequest = - new EapMsChapV2ChallengeRequest( - MSCHAP_V2_ID_INT, - TYPE_DATA_HEADER_SIZE + VALUE_SIZE + SERVER_NAME_BYTES.length, - MSCHAP_V2_AUTHENTICATOR_CHALLENGE, - SERVER_NAME_BYTES); - when(mMockTypeDataDecoder.decodeChallengeRequest(any(String.class), eq(DUMMY_TYPE_DATA))) - .thenReturn(new DecodeResult<>(challengeRequest)); - - doAnswer(invocation -> { - byte[] dst = invocation.getArgument(0); - System.arraycopy(MSCHAP_V2_PEER_CHALLENGE, 0, dst, 0, MSCHAP_V2_PEER_CHALLENGE.length); - return null; - }).when(mMockSecureRandom).nextBytes(eq(new byte[MSCHAP_V2_PEER_CHALLENGE.length])); - - EapResult result = mStateMachine.process(eapMessage); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_MSCHAP_V2_CHALLENGE_RESPONSE, eapResponse.packet); - assertTrue(mStateMachine.getState() instanceof ValidateAuthenticatorState); - verify(mMockSecureRandom).nextBytes(any(byte[].class)); - verify(mMockTypeDataDecoder).decodeChallengeRequest(any(String.class), eq(DUMMY_TYPE_DATA)); - } - - @Test - public void testIncorrectTypeData() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - when(mMockTypeDataDecoder.decodeChallengeRequest(any(String.class), eq(DUMMY_TYPE_DATA))) - .thenReturn( - new DecodeResult<>( - new EapError( - new EapMsChapV2ParsingException("incorrect type data")))); - - EapResult result = mStateMachine.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - verify(mMockTypeDataDecoder).decodeChallengeRequest(any(String.class), eq(DUMMY_TYPE_DATA)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2CreatedStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2CreatedStateTest.java deleted file mode 100644 index 633e0ebd..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2CreatedStateTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.SERVER_NAME_BYTES; - -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.exceptions.mschapv2.EapMsChapV2ParsingException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2ChallengeRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; -import com.android.internal.net.eap.statemachine.EapMsChapV2MethodStateMachine.ValidateAuthenticatorState; - -import org.junit.Test; - -public class EapMsChapV2CreatedStateTest extends EapMsChapV2StateTest { - @Test - public void testProcessChallengeRequest() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - when(mMockTypeDataDecoder.decodeChallengeRequest(any(String.class), eq(DUMMY_TYPE_DATA))) - .thenReturn( - new DecodeResult<>( - new EapMsChapV2ChallengeRequest( - 0, 0, CHALLENGE_BYTES, SERVER_NAME_BYTES))); - - mStateMachine.process(eapMessage); - - assertTrue(mStateMachine.getState() instanceof ValidateAuthenticatorState); - verify(mMockTypeDataDecoder, times(2)) - .decodeChallengeRequest(any(String.class), eq(DUMMY_TYPE_DATA)); - } - - @Test - public void testIncorrectTypeData() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - when(mMockTypeDataDecoder.decodeChallengeRequest( - eq(CREATED_STATE_TAG), eq(DUMMY_TYPE_DATA))) - .thenReturn( - new DecodeResult<>( - new EapError( - new EapMsChapV2ParsingException("incorrect type data")))); - - EapResult result = mStateMachine.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapMsChapV2ParsingException); - verify(mMockTypeDataDecoder) - .decodeChallengeRequest(eq(CREATED_STATE_TAG), eq(DUMMY_TYPE_DATA)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2MethodStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2MethodStateMachineTest.java deleted file mode 100644 index b24c3f82..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2MethodStateMachineTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_CHALLENGE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_CHALLENGE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_MASTER_KEY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_MSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_NT_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_HASH; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_HASH_HASH; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_UTF_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PEER_CHALLENGE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_RECEIVE_START_KEY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_SEND_START_KEY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME_ASCII_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.net.eap.EapSessionConfig.EapMsChapV2Config; - -import com.android.internal.net.eap.statemachine.EapMsChapV2MethodStateMachine.CreatedState; -import com.android.internal.net.utils.Log; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; - -public class EapMsChapV2MethodStateMachineTest { - private EapMsChapV2Config mEapMsChapV2Config; - private EapMsChapV2MethodStateMachine mStateMachine; - - @Before - public void setUp() { - mEapMsChapV2Config = new EapMsChapV2Config(MSCHAP_V2_USERNAME, MSCHAP_V2_PASSWORD); - mStateMachine = new EapMsChapV2MethodStateMachine(mEapMsChapV2Config, new SecureRandom()); - } - - @Test - public void testGetEapMethod() { - assertEquals(EAP_TYPE_MSCHAP_V2, mStateMachine.getEapMethod()); - } - - @Test - public void testStartsOnCreatedState() { - assertTrue(mStateMachine.getState() instanceof CreatedState); - } - - // Tests for MS CHAPv2 authentication utils. Test vectors from RFC 2759#9.2. - - @Test - public void testUsernameToBytes() { - assertArrayEquals( - MSCHAP_V2_USERNAME_ASCII_BYTES, - EapMsChapV2MethodStateMachine.usernameToBytes(MSCHAP_V2_USERNAME)); - } - - @Test - public void testPasswordToBytes() { - assertArrayEquals( - MSCHAP_V2_PASSWORD_UTF_BYTES, - EapMsChapV2MethodStateMachine.passwordToBytes(MSCHAP_V2_PASSWORD)); - } - - @Test - public void testGenerateNtResponse() throws Exception { - byte[] ntResponse = - EapMsChapV2MethodStateMachine.generateNtResponse( - MSCHAP_V2_AUTHENTICATOR_CHALLENGE, - MSCHAP_V2_PEER_CHALLENGE, - MSCHAP_V2_USERNAME, - MSCHAP_V2_PASSWORD); - assertArrayEquals(MSCHAP_V2_NT_RESPONSE, ntResponse); - } - - @Test - public void testChallengeHash() throws Exception { - byte[] challenge = - EapMsChapV2MethodStateMachine.challengeHash( - MSCHAP_V2_PEER_CHALLENGE, - MSCHAP_V2_AUTHENTICATOR_CHALLENGE, - MSCHAP_V2_USERNAME); - assertArrayEquals(MSCHAP_V2_CHALLENGE, challenge); - } - - @Test - public void testNtPasswordHash() { - byte[] passwordHash = EapMsChapV2MethodStateMachine.ntPasswordHash(MSCHAP_V2_PASSWORD); - assertArrayEquals(MSCHAP_V2_PASSWORD_HASH, passwordHash); - } - - @Test - public void testHashNtPasswordHash() { - byte[] passwordHashHash = - EapMsChapV2MethodStateMachine.hashNtPasswordHash(MSCHAP_V2_PASSWORD_HASH); - assertArrayEquals(MSCHAP_V2_PASSWORD_HASH_HASH, passwordHashHash); - } - - @Test - public void testChallengeResponse() throws Exception { - byte[] challengeResponse = - EapMsChapV2MethodStateMachine.challengeResponse( - MSCHAP_V2_CHALLENGE, MSCHAP_V2_PASSWORD_HASH); - assertArrayEquals(MSCHAP_V2_NT_RESPONSE, challengeResponse); - } - - @Test - public void testGenerateAuthenticatorResponse() throws Exception { - byte[] authenticatorResponse = - EapMsChapV2MethodStateMachine.generateAuthenticatorResponse( - MSCHAP_V2_PASSWORD, - MSCHAP_V2_NT_RESPONSE, - MSCHAP_V2_PEER_CHALLENGE, - MSCHAP_V2_AUTHENTICATOR_CHALLENGE, - MSCHAP_V2_USERNAME); - assertArrayEquals(MSCHAP_V2_AUTHENTICATOR_RESPONSE, authenticatorResponse); - } - - @Test - public void testCheckAuthenticatorResponse() throws Exception { - assertTrue( - "AuthenticatorResponse didn't match computed response", - EapMsChapV2MethodStateMachine.checkAuthenticatorResponse( - MSCHAP_V2_PASSWORD, - MSCHAP_V2_NT_RESPONSE, - MSCHAP_V2_PEER_CHALLENGE, - MSCHAP_V2_AUTHENTICATOR_CHALLENGE, - MSCHAP_V2_USERNAME, - MSCHAP_V2_AUTHENTICATOR_RESPONSE)); - } - - @Test - public void testGetMasterKey() throws Exception { - byte[] masterKey = - EapMsChapV2MethodStateMachine.getMasterKey( - MSCHAP_V2_PASSWORD_HASH_HASH, MSCHAP_V2_NT_RESPONSE); - assertArrayEquals(MSCHAP_V2_MASTER_KEY, masterKey); - } - - @Test - public void testGetAsymmetricStartKeySendKey() throws Exception { - byte[] startKey = - EapMsChapV2MethodStateMachine.getAsymmetricStartKey(MSCHAP_V2_MASTER_KEY, true); - assertArrayEquals(Log.byteArrayToHexString(startKey), MSCHAP_V2_SEND_START_KEY, startKey); - } - - @Test - public void testGetAsymmetricStartKeyReceiveKey() throws Exception { - byte[] receiveKey = - EapMsChapV2MethodStateMachine.getAsymmetricStartKey(MSCHAP_V2_MASTER_KEY, false); - assertArrayEquals(MSCHAP_V2_RECEIVE_START_KEY, receiveKey); - } - - @Test - public void testGenerateMsk() throws Exception { - byte[] msk = - EapMsChapV2MethodStateMachine.generateMsk( - MSCHAP_V2_PASSWORD, MSCHAP_V2_NT_RESPONSE); - assertArrayEquals(MSCHAP_V2_MSK, msk); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2StateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2StateTest.java deleted file mode 100644 index 1e4a0c58..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2StateTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - -import android.net.eap.EapSessionConfig.EapMsChapV2Config; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.EapMethodState; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; - -public class EapMsChapV2StateTest { - protected static final String CREATED_STATE_TAG = "CreatedState"; - protected static final String NOTIFICATION_MESSAGE = "test"; - protected static final byte[] DUMMY_TYPE_DATA = hexStringToByteArray("00112233"); - - protected SecureRandom mMockSecureRandom; - protected EapMsChapV2TypeDataDecoder mMockTypeDataDecoder; - - protected EapMsChapV2Config mEapMsChapV2Config; - protected EapMsChapV2MethodStateMachine mStateMachine; - - @Before - public void setUp() { - mMockSecureRandom = mock(SecureRandom.class); - mMockTypeDataDecoder = mock(EapMsChapV2TypeDataDecoder.class); - - mEapMsChapV2Config = new EapMsChapV2Config(MSCHAP_V2_USERNAME, MSCHAP_V2_PASSWORD); - mStateMachine = - new EapMsChapV2MethodStateMachine( - mEapMsChapV2Config, mMockSecureRandom, mMockTypeDataDecoder); - } - - @Test - public void testHandleEapFailure() throws Exception { - EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_FAILURE, ID_INT, null)); - assertTrue(result instanceof EapFailure); - assertTrue(mStateMachine.getState() instanceof FinalState); - } - - @Test - public void testHandleEapSuccess() throws Exception { - EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_SUCCESS, ID_INT, null)); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testHandleEapNotification() throws Exception { - EapData eapData = new EapData(EAP_NOTIFICATION, NOTIFICATION_MESSAGE.getBytes()); - EapMessage notification = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preNotification = (EapMethodState) mStateMachine.getState(); - - EapResult result = mStateMachine.process(notification); - assertEquals(preNotification, mStateMachine.getState()); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2ValidateAuthenticatorStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2ValidateAuthenticatorStateTest.java deleted file mode 100644 index dda7d5bc..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapMsChapV2ValidateAuthenticatorStateTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_MSCHAP_V2_FAILURE_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_MSCHAP_V2_SUCCESS_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.INVALID_AUTHENTICATOR_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_CHALLENGE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_NT_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PEER_CHALLENGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.CHALLENGE_BYTES; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.ERROR_CODE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.MESSAGE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.PASSWORD_CHANGE_PROTOCOL; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2PacketDefinitions.RETRY_BIT; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_FAILURE; -import static com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EAP_MSCHAP_V2_SUCCESS; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2FailureRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2SuccessRequest; -import com.android.internal.net.eap.message.mschapv2.EapMsChapV2TypeData.EapMsChapV2TypeDataDecoder.DecodeResult; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; -import com.android.internal.net.eap.statemachine.EapMsChapV2MethodStateMachine.AwaitingEapFailureState; -import com.android.internal.net.eap.statemachine.EapMsChapV2MethodStateMachine.AwaitingEapSuccessState; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.BufferUnderflowException; - -public class EapMsChapV2ValidateAuthenticatorStateTest extends EapMsChapV2StateTest { - private static final int INVALID_OP_CODE = -1; - - @Before - @Override - public void setUp() { - super.setUp(); - - mStateMachine.transitionTo( - mStateMachine.new ValidateAuthenticatorState( - MSCHAP_V2_AUTHENTICATOR_CHALLENGE, - MSCHAP_V2_PEER_CHALLENGE, - MSCHAP_V2_NT_RESPONSE)); - } - - @Test - public void testProcessSuccessRequest() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapMsChapV2SuccessRequest successRequest = - new EapMsChapV2SuccessRequest( - MSCHAP_V2_ID_INT, 0, MSCHAP_V2_AUTHENTICATOR_RESPONSE, MESSAGE); - - when(mMockTypeDataDecoder.getOpCode(eq(DUMMY_TYPE_DATA))).thenReturn(EAP_MSCHAP_V2_SUCCESS); - when(mMockTypeDataDecoder.decodeSuccessRequest(any(String.class), eq(DUMMY_TYPE_DATA))) - .thenReturn(new DecodeResult<>(successRequest)); - - EapResult result = mStateMachine.process(eapMessage); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_MSCHAP_V2_SUCCESS_RESPONSE, eapResponse.packet); - assertTrue(mStateMachine.getState() instanceof AwaitingEapSuccessState); - verify(mMockTypeDataDecoder).getOpCode(eq(DUMMY_TYPE_DATA)); - verify(mMockTypeDataDecoder).decodeSuccessRequest(any(String.class), eq(DUMMY_TYPE_DATA)); - } - - @Test - public void testProcessInvalidSuccessRequest() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapMsChapV2SuccessRequest successRequest = - new EapMsChapV2SuccessRequest( - MSCHAP_V2_ID_INT, 0, INVALID_AUTHENTICATOR_RESPONSE, MESSAGE); - - when(mMockTypeDataDecoder.getOpCode(eq(DUMMY_TYPE_DATA))).thenReturn(EAP_MSCHAP_V2_SUCCESS); - when(mMockTypeDataDecoder.decodeSuccessRequest(any(String.class), eq(DUMMY_TYPE_DATA))) - .thenReturn(new DecodeResult<>(successRequest)); - - EapResult result = mStateMachine.process(eapMessage); - assertTrue(result instanceof EapFailure); - assertTrue(mStateMachine.getState() instanceof FinalState); - verify(mMockTypeDataDecoder).getOpCode(eq(DUMMY_TYPE_DATA)); - verify(mMockTypeDataDecoder).decodeSuccessRequest(any(String.class), eq(DUMMY_TYPE_DATA)); - } - - @Test - public void testProcessFailureRequest() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapMsChapV2FailureRequest failureRequest = - new EapMsChapV2FailureRequest( - MSCHAP_V2_ID_INT, - 0, - ERROR_CODE, - RETRY_BIT, - CHALLENGE_BYTES, - PASSWORD_CHANGE_PROTOCOL, - MESSAGE); - - when(mMockTypeDataDecoder.getOpCode(eq(DUMMY_TYPE_DATA))).thenReturn(EAP_MSCHAP_V2_FAILURE); - when(mMockTypeDataDecoder.decodeFailureRequest(any(String.class), eq(DUMMY_TYPE_DATA))) - .thenReturn(new DecodeResult<>(failureRequest)); - - EapResult result = mStateMachine.process(eapMessage); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_MSCHAP_V2_FAILURE_RESPONSE, eapResponse.packet); - assertTrue(mStateMachine.getState() instanceof AwaitingEapFailureState); - verify(mMockTypeDataDecoder).getOpCode(eq(DUMMY_TYPE_DATA)); - verify(mMockTypeDataDecoder).decodeFailureRequest(any(String.class), eq(DUMMY_TYPE_DATA)); - } - - @Test - public void testProcessEmptyTypeData() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, new byte[0]); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - when(mMockTypeDataDecoder.getOpCode(eq(new byte[0]))) - .thenThrow(new BufferUnderflowException()); - - EapResult result = mStateMachine.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof BufferUnderflowException); - verify(mMockTypeDataDecoder).getOpCode(eq(new byte[0])); - } - - @Test - public void testProcessInvalidPacket() throws Exception { - EapData eapData = new EapData(EAP_TYPE_MSCHAP_V2, DUMMY_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - when(mMockTypeDataDecoder.getOpCode(eq(DUMMY_TYPE_DATA))).thenReturn(INVALID_OP_CODE); - - EapResult result = mStateMachine.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - verify(mMockTypeDataDecoder).getOpCode(eq(DUMMY_TYPE_DATA)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimAkaMethodStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimAkaMethodStateMachineTest.java deleted file mode 100644 index 7adc9f75..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimAkaMethodStateMachineTest.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.COMPUTED_MAC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_CHALLENGE_RESPONSE_MAC_INPUT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_CHALLENGE_RESPONSE_WITH_MAC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_CLIENT_ERROR_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_IDENTITY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_NOTIFICATION_REQUEST_WITH_EMPTY_MAC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_NOTIFICATION_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_NOTIFICATION_RESPONSE_WITH_EMPTY_MAC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_NOTIFICATION_RESPONSE_WITH_MAC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_RESPONSE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EMSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EMSK_STRING; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.KC_1; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.KC_2; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.K_AUT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.K_AUT_STRING; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.K_ENCR; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.K_ENCR_STRING; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MAC_INPUT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSK_STRING; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ORIGINAL_MAC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SRES_1; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SRES_BYTES; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_POST_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_PRE_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_CLIENT_ERROR; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_START; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.AT_IDENTITY; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.IDENTITY; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_MT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_MT_STRING; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_2_BYTES; -import static com.android.internal.net.eap.statemachine.EapSimAkaMethodStateMachine.KEY_LEN; -import static com.android.internal.net.eap.statemachine.EapSimAkaMethodStateMachine.MAC_ALGORITHM_STRING; -import static com.android.internal.net.eap.statemachine.EapSimAkaMethodStateMachine.MASTER_KEY_GENERATION_ALG; -import static com.android.internal.net.eap.statemachine.EapSimAkaMethodStateMachine.SESSION_KEY_LENGTH; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig.EapSimConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.crypto.Fips186_2Prf; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandSim; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtSelectedVersion; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.EapMethodState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -public class EapSimAkaMethodStateMachineTest { - private static final String TAG = EapSimAkaMethodStateMachineTest.class.getSimpleName(); - private static final int SUB_ID = 1; - private static final int AT_RAND_LEN = 36; - private static final String VERSIONS_STRING = "0001"; - private static final String SELECTED_VERSION = "0001"; - private static final byte[] SHA_1_INPUT = hexStringToByteArray("0123456789ABCDEF"); - private static final byte[] FORMATTED_UICC_CHALLENGE = - hexStringToByteArray("1000112233445566778899AABBCCDDEEFF"); - private static final String BASE_64_CHALLENGE = "EAARIjNEVWZ3iJmqu8zd7v8="; - private static final String BASE_64_RESPONSE = "BBEiM0QIAQIDBAUGBwg="; - private static final byte[] UICC_RESPONSE = - hexStringToByteArray("04" + SRES_1 + "08" + KC_1); - - // EAP-Identity = hex("test@android.net") - protected static final byte[] EAP_IDENTITY_BYTES = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - // K_encr + K_aut + MSK + EMSK - private static final int PRF_OUTPUT_BYTES = (2 * KEY_LEN) + (2 * SESSION_KEY_LENGTH); - - private TelephonyManager mMockTelephonyManager; - private EapSimConfig mEapSimConfig; - private EapSimAkaMethodStateMachine mStateMachine; - - @Before - public void setUp() { - mMockTelephonyManager = mock(TelephonyManager.class); - mEapSimConfig = new EapSimConfig(SUB_ID, TelephonyManager.APPTYPE_USIM); - - mStateMachine = - new EapSimAkaMethodStateMachine( - mMockTelephonyManager, EAP_IDENTITY_BYTES, mEapSimConfig) { - @Override - EapSimAkaTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode) { - return new EapSimTypeData( - EAP_SIM_CLIENT_ERROR, Arrays.asList(clientErrorCode)); - } - - @Override - EapSimAkaTypeData getEapSimAkaTypeData( - int eapSubtype, List<EapSimAkaAttribute> attributes) { - return new EapSimTypeData(eapSubtype, attributes); - } - - @Override - int getEapMethod() { - return EAP_TYPE_SIM; - } - }; - mStateMachine = spy(mStateMachine); - } - - @Test - public void testBuildClientErrorResponse() { - AtClientErrorCode errorCode = AtClientErrorCode.UNSUPPORTED_VERSION; - - EapResult result = - mStateMachine.buildClientErrorResponse(ID_INT, EAP_TYPE_SIM, errorCode); - assertTrue(result instanceof EapResult.EapResponse); - EapResult.EapResponse eapResponse = (EapResult.EapResponse) result; - assertArrayEquals(EAP_SIM_CLIENT_ERROR_RESPONSE, eapResponse.packet); - } - - @Test - public void testBuildResponseMessage() throws Exception { - List<EapSimAkaAttribute> attributes = new ArrayList<>(); - attributes.add(new AtSelectedVersion(1)); - attributes.add(new AtIdentity(AT_IDENTITY.length, IDENTITY)); - int identifier = ID_INT; - - EapResult result = - mStateMachine.buildResponseMessage( - EAP_TYPE_SIM, - EAP_SIM_START, - identifier, - attributes); - assertTrue(result instanceof EapResult); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_SIM_RESPONSE_PACKET, eapResponse.packet); - } - - @Test - public void testGenerateAndPersistKeys() { - byte[] mkInput = hexStringToByteArray( - EAP_SIM_IDENTITY - + KC_1 - + KC_2 - + NONCE_MT_STRING - + VERSIONS_STRING - + SELECTED_VERSION); - MessageDigest mockSha1 = mock(MessageDigest.class); - when(mockSha1.digest(eq(mkInput))).thenReturn(MK); - - byte[] keys = hexStringToByteArray(K_ENCR_STRING + K_AUT_STRING + MSK_STRING + EMSK_STRING); - Fips186_2Prf mockFips186_2Prf = mock(Fips186_2Prf.class); - when(mockFips186_2Prf.getRandom(eq(MK), eq(PRF_OUTPUT_BYTES))).thenReturn(keys); - - mStateMachine.generateAndPersistKeys(TAG, mockSha1, mockFips186_2Prf, mkInput); - assertArrayEquals(K_ENCR, mStateMachine.mKEncr); - assertArrayEquals(K_AUT, mStateMachine.mKAut); - assertArrayEquals(MSK, mStateMachine.mMsk); - assertArrayEquals(EMSK, mStateMachine.mEmsk); - - verify(mockSha1).digest(eq(mkInput)); - verify(mockFips186_2Prf).getRandom(eq(MK), eq(PRF_OUTPUT_BYTES)); - verifyNoMoreInteractions(mockSha1, mockFips186_2Prf); - } - - /** - * Test that we can actually instantiate and use the SHA-1algorithm. - */ - @Test - public void testCreateSha1() throws Exception { - MessageDigest sha1 = MessageDigest.getInstance(MASTER_KEY_GENERATION_ALG); - byte[] sha1Result = sha1.digest(SHA_1_INPUT); - assertFalse(Arrays.equals(SHA_1_INPUT, sha1Result)); - } - - /** - * Test that we can actually instantiate and use the HMAC-SHA-1 algorithm. - */ - @Test - public void testCreateHmacSha1() throws Exception { - Mac macAlgorithm = Mac.getInstance(MAC_ALGORITHM_STRING); - macAlgorithm.init(new SecretKeySpec(K_AUT, MAC_ALGORITHM_STRING)); - byte[] mac = macAlgorithm.doFinal(MAC_INPUT); - assertFalse(Arrays.equals(MAC_INPUT, mac)); - } - - @Test - public void testProcessUiccAuthentication() throws Exception { - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)).thenReturn(BASE_64_RESPONSE); - - byte[] result = - mStateMachine.processUiccAuthentication( - TAG, - TelephonyManager.AUTHTYPE_EAP_AKA, - FORMATTED_UICC_CHALLENGE); - - assertArrayEquals(UICC_RESPONSE, result); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testProcessUiccAuthenticationNullResponse() throws Exception { - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE)).thenReturn(null); - - try { - mStateMachine.processUiccAuthentication( - TAG, - TelephonyManager.AUTHTYPE_EAP_AKA, - FORMATTED_UICC_CHALLENGE); - fail("EapSimAkaAuthenticationFailureException expected for null TelMan response"); - } catch (EapSimAkaAuthenticationFailureException expected) { - } - - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - BASE_64_CHALLENGE); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testGetMac() throws Exception { - AtMac atMac = new AtMac(ORIGINAL_MAC); - AtRandSim atRandSim = new AtRandSim(AT_RAND_LEN, RAND_1_BYTES, RAND_2_BYTES); - EapSimTypeData eapSimTypeData = - new EapSimTypeData(EAP_SIM_CHALLENGE, Arrays.asList(atRandSim, atMac)); - - Mac mockMac = mock(Mac.class); - when(mockMac.doFinal(eq(MAC_INPUT))).thenReturn(COMPUTED_MAC); - mStateMachine.mMacAlgorithm = mockMac; - - byte[] mac = mStateMachine.getMac(EAP_CODE_REQUEST, ID_INT, eapSimTypeData, NONCE_MT); - assertArrayEquals(COMPUTED_MAC, mac); - AtMac postCalculationAtMac = (AtMac) eapSimTypeData.attributeMap.get(EAP_AT_MAC); - assertArrayEquals(ORIGINAL_MAC, postCalculationAtMac.mac); - - verify(mockMac).doFinal(eq(MAC_INPUT)); - verifyNoMoreInteractions(mockMac); - } - - @Test - public void testGetMacNoMacAlgorithm() throws Exception { - try { - mStateMachine.getMac(EAP_CODE_REQUEST, ID_INT, null, null); - fail("Expected IllegalStateException if Mac not set"); - } catch (IllegalStateException expected) { - } - } - - @Test - public void testReceivedValidMac() throws Exception { - AtMac atMac = new AtMac(ORIGINAL_MAC); - AtRandSim atRandSim = new AtRandSim(AT_RAND_LEN, RAND_1_BYTES, RAND_2_BYTES); - EapSimTypeData eapSimTypeData = - new EapSimTypeData(EAP_SIM_CHALLENGE, Arrays.asList(atRandSim, atMac)); - EapData eapData = new EapData(EAP_TYPE_SIM, new byte[0]); - EapMessage message = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - doReturn(ORIGINAL_MAC) - .when(mStateMachine) - .getMac(eq(EAP_CODE_REQUEST), eq(ID_INT), eq(eapSimTypeData), eq(NONCE_MT)); - - assertTrue(mStateMachine.isValidMac(TAG, message, eapSimTypeData, NONCE_MT)); - - doReturn(new byte[0]) - .when(mStateMachine) - .getMac(eq(EAP_CODE_REQUEST), eq(ID_INT), eq(eapSimTypeData), eq(NONCE_MT)); - - assertFalse(mStateMachine.isValidMac(TAG, message, eapSimTypeData, NONCE_MT)); - - verify(mStateMachine, times(2)) - .getMac(eq(EAP_CODE_REQUEST), eq(ID_INT), eq(eapSimTypeData), eq(NONCE_MT)); - } - - @Test - public void testBuildResponseMessageWithMac() { - Mac mockMac = mock(Mac.class); - when(mockMac.doFinal(eq(EAP_SIM_CHALLENGE_RESPONSE_MAC_INPUT))).thenReturn(COMPUTED_MAC); - mStateMachine.mMacAlgorithm = mockMac; - - EapResult result = - mStateMachine.buildResponseMessageWithMac(ID_INT, EAP_SIM_CHALLENGE, SRES_BYTES); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_SIM_CHALLENGE_RESPONSE_WITH_MAC, eapResponse.packet); - verify(mockMac).doFinal(eq(EAP_SIM_CHALLENGE_RESPONSE_MAC_INPUT)); - verifyNoMoreInteractions(mockMac); - } - - @Test - public void testHandleEapSimNotificationPreChallenge() throws Exception { - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE))); - - EapResponse eapResponse = - (EapResponse) - mStateMachine.handleEapSimAkaNotification(TAG, true, ID_INT, typeData); - assertArrayEquals(EAP_SIM_NOTIFICATION_RESPONSE, eapResponse.packet); - assertTrue(mStateMachine.mHasReceivedSimAkaNotification); - verify(mStateMachine, never()).transitionTo(any(EapMethodState.class)); - } - - @Test - public void testHandleEapSimNotificationPreChallengeInvalidPBit() throws Exception { - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_POST_CHALLENGE))); - - EapResponse eapResponse = - (EapResponse) - mStateMachine.handleEapSimAkaNotification(TAG, true, ID_INT, typeData); - assertArrayEquals(EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - verify(mStateMachine, never()) - .transitionTo(any(EapMethodStateMachine.EapMethodState.class)); - } - - @Test - public void testHandleEapSimNotificationMultipleNotifications() throws Exception { - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE))); - - mStateMachine.handleEapSimAkaNotification(TAG, true, ID_INT, typeData); - - EapError eapError = - (EapError) mStateMachine.handleEapSimAkaNotification(TAG, true, ID_INT, typeData); - assertTrue(eapError.cause instanceof EapInvalidRequestException); - assertTrue(mStateMachine.mHasReceivedSimAkaNotification); - verify(mStateMachine, never()) - .transitionTo(any(EapMethodStateMachine.EapMethodState.class)); - } - - @Test - public void testHandleEapSimNotificationInvalidAtMac() throws Exception { - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList( - new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE), new AtMac())); - - EapResponse eapResponse = - (EapResponse) - mStateMachine.handleEapSimAkaNotification(TAG, true, ID_INT, typeData); - assertArrayEquals(EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - verify(mStateMachine, never()) - .transitionTo(any(EapMethodStateMachine.EapMethodState.class)); - } - - @Test - public void testHandleEapSimNotificationPostChallenge() throws Exception { - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList( - new AtNotification(GENERAL_FAILURE_POST_CHALLENGE), - new AtMac(ORIGINAL_MAC))); - - Mac mockMac = mock(Mac.class); - when(mockMac.doFinal(eq(EAP_SIM_NOTIFICATION_REQUEST_WITH_EMPTY_MAC))) - .thenReturn(ORIGINAL_MAC); - when(mockMac.doFinal(eq(EAP_SIM_NOTIFICATION_RESPONSE_WITH_EMPTY_MAC))) - .thenReturn(COMPUTED_MAC); - mStateMachine.mMacAlgorithm = mockMac; - - EapResponse eapResponse = - (EapResponse) - mStateMachine.handleEapSimAkaNotification(TAG, false, ID_INT, typeData); - assertArrayEquals(EAP_SIM_NOTIFICATION_RESPONSE_WITH_MAC, eapResponse.packet); - assertTrue(mStateMachine.mHasReceivedSimAkaNotification); - verify(mStateMachine, never()).transitionTo(any(EapMethodState.class)); - - verify(mockMac).doFinal(eq(EAP_SIM_NOTIFICATION_REQUEST_WITH_EMPTY_MAC)); - verify(mockMac).doFinal(eq(EAP_SIM_NOTIFICATION_RESPONSE_WITH_EMPTY_MAC)); - verifyNoMoreInteractions(mockMac); - } - - @Test - public void testHandleEapSimNotificationPostChallengeInvalidAtMac() throws Exception { - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_POST_CHALLENGE))); - - EapResponse eapResponse = - (EapResponse) - mStateMachine.handleEapSimAkaNotification(TAG, false, ID_INT, typeData); - assertArrayEquals(EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet); - verify(mStateMachine, never()).transitionTo(any(EapMethodState.class)); - } - - @Test - public void testKeyLengths() { - assertEquals(KEY_LEN, mStateMachine.getKEncrLength()); - assertEquals(KEY_LEN, mStateMachine.getKAutLength()); - assertEquals(SESSION_KEY_LENGTH, mStateMachine.getMskLength()); - assertEquals(SESSION_KEY_LENGTH, mStateMachine.getEmskLength()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimChallengeStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimChallengeStateTest.java deleted file mode 100644 index bffc03ab..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimChallengeStateTest.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_IDENTITY; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.CHALLENGE_RESPONSE_INVALID_KC; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.CHALLENGE_RESPONSE_INVALID_SRES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_IDENTITY_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EMSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.KC_1_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.KC_2_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SRES_1_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SRES_2_BYTES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.VALID_CHALLENGE_RESPONSE; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_MT; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_1_BYTES; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_2; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.RAND_2_BYTES; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidLengthException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceMt; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandSim; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.ChallengeState; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.ChallengeState.RandChallengeResult; - -import org.junit.Test; - -import java.nio.BufferUnderflowException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; - -public class EapSimChallengeStateTest extends EapSimStateTest { - private static final int VALID_SRES_LENGTH = 4; - private static final int INVALID_SRES_LENGTH = 5; - private static final int VALID_KC_LENGTH = 8; - private static final int INVALID_KC_LENGTH = 9; - private static final int AT_RAND_LENGTH = 36; - private static final List<Integer> VERSIONS = Arrays.asList(1); - - // Base64 of {@link EapTestAttributeDefinitions#RAND_1} - private static final String BASE_64_RAND_1 = "EAARIjNEVWZ3iJmqu8zd7v8="; - - // Base64 of {@link EapTestAttributeDefinitions#RAND_2} - private static final String BASE_64_RAND_2 = "EP/u3cy7qpmId2ZVRDMiEQA="; - - // Base64 of "04" + SRES_1 + "08" + KC_1 - private static final String BASE_64_RESP_1 = "BBEiM0QIAQIDBAUGBwg="; - - // Base64 of "04" + SRES_2 + "08" + KC_2 - private static final String BASE_64_RESP_2 = "BEQzIhEICAcGBQQDAgE="; - - // Base64 of "04" + SRES_1 + '081122" - private static final String BASE_64_INVALID_RESP = "BBEiM0QIESI="; - - private AtNonceMt mAtNonceMt; - private ChallengeState mChallengeState; - - @Override - public void setUp() { - super.setUp(); - - try { - mAtNonceMt = new AtNonceMt(NONCE_MT); - } catch (EapSimAkaInvalidAttributeException ex) { - // this will never happen - } - mChallengeState = mEapSimMethodStateMachine - .new ChallengeState(VERSIONS, mAtNonceMt, EAP_SIM_IDENTITY_BYTES); - mEapSimMethodStateMachine.transitionTo(mChallengeState); - } - - @Test - public void testProcessIncorrectEapMethodType() throws Exception { - EapData eapData = new EapData(EAP_IDENTITY, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapResult result = mChallengeState.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessSuccess() throws Exception { - System.arraycopy(MSK, 0, mEapSimMethodStateMachine.mMsk, 0, MSK.length); - System.arraycopy(EMSK, 0, mEapSimMethodStateMachine.mEmsk, 0, EMSK.length); - - EapMessage input = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - EapResult result = mEapSimMethodStateMachine.process(input); - assertTrue(mEapSimMethodStateMachine.getState() instanceof FinalState); - - EapSuccess eapSuccess = (EapSuccess) result; - assertArrayEquals(MSK, eapSuccess.msk); - assertArrayEquals(EMSK, eapSuccess.emsk); - } - - @Test - public void testProcessFailure() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_FAILURE, ID_INT, null); - EapResult result = mEapSimMethodStateMachine.process(input); - assertTrue(mEapSimMethodStateMachine.getState() instanceof FinalState); - - assertTrue(result instanceof EapFailure); - } - - @Test - public void testIsValidChallengeAttributes() { - LinkedHashMap<Integer, EapSimAkaAttribute> attributeMap = new LinkedHashMap<>(); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_CHALLENGE, attributeMap); - assertFalse(mChallengeState.isValidChallengeAttributes(eapSimTypeData)); - - attributeMap.put(EAP_AT_RAND, null); // value doesn't matter, just need key - eapSimTypeData = new EapSimTypeData(EAP_SIM_CHALLENGE, attributeMap); - assertFalse(mChallengeState.isValidChallengeAttributes(eapSimTypeData)); - - attributeMap.put(EAP_AT_MAC, null); // value doesn't matter, just need key - eapSimTypeData = new EapSimTypeData(EAP_SIM_CHALLENGE, attributeMap); - assertTrue(mChallengeState.isValidChallengeAttributes(eapSimTypeData)); - } - - @Test - public void testRandChallengeResultConstructor() { - try { - mChallengeState.new RandChallengeResult( - new byte[VALID_SRES_LENGTH], new byte[INVALID_KC_LENGTH]); - fail("EapSimAkaInvalidLengthException expected for invalid SRES lengths"); - } catch (EapSimAkaInvalidLengthException expected) { - } - - try { - mChallengeState.new RandChallengeResult( - new byte[INVALID_SRES_LENGTH], new byte[VALID_KC_LENGTH]); - fail("EapSimAkaInvalidLengthException expected for invalid Kc lengths"); - } catch (EapSimAkaInvalidLengthException expected) { - } - } - - @Test - public void testRandChallengeResultEquals() throws Exception { - RandChallengeResult resultA = - mChallengeState.new RandChallengeResult(SRES_1_BYTES, KC_1_BYTES); - RandChallengeResult resultB = - mChallengeState.new RandChallengeResult(SRES_1_BYTES, KC_1_BYTES); - RandChallengeResult resultC = - mChallengeState.new RandChallengeResult(SRES_2_BYTES, KC_2_BYTES); - - assertEquals(resultA, resultB); - assertNotEquals(resultA, resultC); - } - - @Test - public void testRandChallengeResultHashCode() throws Exception { - RandChallengeResult resultA = - mChallengeState.new RandChallengeResult(SRES_1_BYTES, KC_1_BYTES); - RandChallengeResult resultB = - mChallengeState.new RandChallengeResult(SRES_1_BYTES, KC_1_BYTES); - RandChallengeResult resultC = - mChallengeState.new RandChallengeResult(SRES_2_BYTES, KC_2_BYTES); - - assertEquals(resultA.hashCode(), resultB.hashCode()); - assertNotEquals(resultA.hashCode(), resultC.hashCode()); - } - - @Test - public void testGetRandChallengeResultFromResponse() throws Exception { - RandChallengeResult result = - mChallengeState.getRandChallengeResultFromResponse(VALID_CHALLENGE_RESPONSE); - - assertArrayEquals(SRES_1_BYTES, result.sres); - assertArrayEquals(KC_1_BYTES, result.kc); - } - - @Test - public void testGetRandChallengeResultFromResponseInvalidSres() { - try { - mChallengeState.getRandChallengeResultFromResponse(CHALLENGE_RESPONSE_INVALID_SRES); - fail("EapSimAkaInvalidLengthException expected for invalid SRES_1 length"); - } catch (EapSimAkaInvalidLengthException expected) { - } - } - - @Test - public void testGetRandChallengeResultFromResponseInvalidKc() { - try { - mChallengeState.getRandChallengeResultFromResponse(CHALLENGE_RESPONSE_INVALID_KC); - fail("EapSimAkaInvalidLengthException expected for invalid KC length"); - } catch (EapSimAkaInvalidLengthException expected) { - } - } - - @Test - public void testGetRandChallengeResults() throws Exception { - EapSimTypeData eapSimTypeData = - new EapSimTypeData(EAP_SIM_CHALLENGE, Arrays.asList( - new AtRandSim(AT_RAND_LENGTH, - hexStringToByteArray(RAND_1), - hexStringToByteArray(RAND_2)))); - - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_1)) - .thenReturn(BASE_64_RESP_1); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_2)) - .thenReturn(BASE_64_RESP_2); - - List<RandChallengeResult> actualResult = - mChallengeState.getRandChallengeResults(eapSimTypeData); - - List<RandChallengeResult> expectedResult = Arrays.asList( - mChallengeState.new RandChallengeResult(SRES_1_BYTES, KC_1_BYTES), - mChallengeState.new RandChallengeResult(SRES_2_BYTES, KC_2_BYTES)); - assertEquals(expectedResult, actualResult); - - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_1); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_2); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testGetRandChallengeResultsBufferUnderflow() throws Exception { - EapSimTypeData eapSimTypeData = - new EapSimTypeData(EAP_SIM_CHALLENGE, Arrays.asList( - new AtRandSim(AT_RAND_LENGTH, - hexStringToByteArray(RAND_1), - hexStringToByteArray(RAND_2)))); - - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_1)) - .thenReturn(BASE_64_RESP_1); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_2)) - .thenReturn(BASE_64_INVALID_RESP); - - try { - mChallengeState.getRandChallengeResults(eapSimTypeData); - fail("BufferUnderflowException expected for short Kc value"); - } catch (BufferUnderflowException ex) { - } - - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_1); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_2); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testProcessUiccAuthenticationNullResponse() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - AtRandSim atRandSim = new AtRandSim(AT_RAND_LENGTH, RAND_1_BYTES, RAND_2_BYTES); - - DecodeResult<EapSimTypeData> decodeResult = - new DecodeResult<>( - new EapSimTypeData( - EAP_SIM_CHALLENGE, - Arrays.asList(atRandSim, new AtMac()))); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - when(mMockTelephonyManager - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_1)) - .thenReturn(null); - - EapError eapError = (EapError) mEapSimMethodStateMachine.process(eapMessage); - assertTrue(eapError.cause instanceof EapSimAkaAuthenticationFailureException); - - verify(mMockEapSimTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - verify(mMockTelephonyManager) - .getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_SIM, - BASE_64_RAND_1); - verifyNoMoreInteractions(mMockEapSimTypeDataDecoder, mMockTelephonyManager); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimCreatedStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimCreatedStateTest.java deleted file mode 100644 index bdd19201..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimCreatedStateTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_IDENTITY; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; - -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPermanentIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.CreatedState; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.StartState; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class EapSimCreatedStateTest extends EapSimStateTest { - private static final int EAP_SIM_START = 10; - - private CreatedState mCreatedState; - - @Before - public void setUp() { - super.setUp(); - mCreatedState = mEapSimMethodStateMachine.new CreatedState(); - } - - @Test - public void testProcessSuccess() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - EapResult result = mCreatedState.process(input); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessFailure() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_FAILURE, ID_INT, null); - EapResult result = mCreatedState.process(input); - assertTrue(mEapSimMethodStateMachine.getState() instanceof FinalState); - - assertTrue(result instanceof EapFailure); - } - - @Test - public void testTransitionToStartState() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - List<EapSimAkaAttribute> attributes = Arrays.asList( - new AtVersionList(8, 1), new AtPermanentIdReq()); - DecodeResult<EapSimTypeData> decodeResult = - new DecodeResult<>(new EapSimTypeData(EAP_SIM_START, attributes)); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - mEapSimMethodStateMachine.process(eapMessage); - assertTrue(mEapSimMethodStateMachine.getState() instanceof StartState); - - // decoded in CreatedState and StartState - verify(mMockEapSimTypeDataDecoder, times(2)).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockEapSimTypeDataDecoder); - } - - @Test - public void testProcessIncorrectEapMethodType() throws Exception { - EapData eapData = new EapData(EAP_IDENTITY, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapResult result = mEapSimMethodStateMachine.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimMethodStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimMethodStateMachineTest.java deleted file mode 100644 index ac1d01ca..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimMethodStateMachineTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_NOTIFICATION_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_PRE_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_NOTIFICATION; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_START; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig.EapSimConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.message.simaka.EapSimTypeData.EapSimTypeDataDecoder; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.CreatedState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; -import java.util.Arrays; - -public class EapSimMethodStateMachineTest { - private static final int SUB_ID = 1; - private static final byte[] DUMMY_EAP_TYPE_DATA = hexStringToByteArray("112233445566"); - - // EAP-Identity = hex("test@android.net") - protected static final byte[] EAP_IDENTITY_BYTES = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - private TelephonyManager mMockTelephonyManager; - private EapSimTypeDataDecoder mMockEapSimTypeDataDecoder; - - private EapSimConfig mEapSimConfig = new EapSimConfig(SUB_ID, APPTYPE_USIM); - private EapSimMethodStateMachine mEapSimMethodStateMachine; - - - @Before - public void setUp() { - mMockTelephonyManager = mock(TelephonyManager.class); - mMockEapSimTypeDataDecoder = mock(EapSimTypeDataDecoder.class); - - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - - mEapSimMethodStateMachine = - new EapSimMethodStateMachine( - mMockTelephonyManager, - EAP_IDENTITY_BYTES, - mEapSimConfig, - new SecureRandom(), - mMockEapSimTypeDataDecoder); - - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - } - - @Test - public void testEapSimMethodStateMachineStartState() { - assertTrue(mEapSimMethodStateMachine.getState() instanceof CreatedState); - } - - @Test - public void testGetMethod() { - assertEquals(EAP_TYPE_SIM, mEapSimMethodStateMachine.getEapMethod()); - } - - @Test - public void testEapSimFailsOnMultipleSimNotifications() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - // First EAP-SIM/Notification - EapSimTypeData notificationTypeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE))); - DecodeResult<EapSimTypeData> decodeResult = new DecodeResult<>(notificationTypeData); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapSimMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_SIM_NOTIFICATION_RESPONSE, eapResponse.packet); - verify(mMockEapSimTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapSimTypeDataDecoder); - - // Transition to StartState - decodeResult = - new DecodeResult<>( - new EapSimTypeData(EAP_SIM_START, Arrays.asList(new AtVersionList(8, 1)))); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - eapResponse = (EapResponse) mEapSimMethodStateMachine.process(eapMessage); - assertFalse( - "EAP-Request/SIM-Start returned a Client-Error response", - Arrays.equals(EAP_SIM_CLIENT_ERROR_UNABLE_TO_PROCESS, eapResponse.packet)); - - // decoded in: previous 1 time + in CreatedState and StartState - verify(mMockEapSimTypeDataDecoder, times(3)).decode(eq(DUMMY_EAP_TYPE_DATA)); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapSimTypeDataDecoder); - - // Second EAP-SIM/Notification - decodeResult = new EapSimAkaTypeData.DecodeResult<>(notificationTypeData); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapError eapError = (EapError) mEapSimMethodStateMachine.process(eapMessage); - assertTrue(eapError.cause instanceof EapInvalidRequestException); - - // decoded previous 3 times + 1 - verify(mMockEapSimTypeDataDecoder, times(4)).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapSimTypeDataDecoder); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimStartStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimStartStateTest.java deleted file mode 100644 index ccb746ab..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimStartStateTest.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.message.EapData.EAP_IDENTITY; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_IDENTITY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_RESPONSE_WITHOUT_IDENTITY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.IMSI; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ENCR_DATA; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IV; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_VERSION_LIST; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_START; -import static com.android.internal.net.eap.message.simaka.attributes.EapTestAttributeDefinitions.NONCE_MT; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.exceptions.simaka.EapSimAkaIdentityUnavailableException; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAnyIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtMac; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNonceMt; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtPermanentIdReq; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtVersionList; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.FinalState; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.ChallengeState; -import com.android.internal.net.eap.statemachine.EapSimMethodStateMachine.StartState; -import com.android.internal.net.utils.Log; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.LinkedHashMap; - -public class EapSimStartStateTest extends EapSimStateTest { - - private StartState mStartState; - private LinkedHashMap<Integer, EapSimAkaAttribute> mAttributes; - - @Before - public void setUp() { - super.setUp(); - - AtNonceMt atNonceMt = null; - try { - atNonceMt = new AtNonceMt(NONCE_MT); - } catch (Exception e) { - fail("Failed to create AtNonceMt attribute in setUp()"); - } - - mStartState = mEapSimMethodStateMachine.new StartState(atNonceMt); - mEapSimMethodStateMachine.transitionTo(mStartState); - - mAttributes = new LinkedHashMap<>(); - } - - @Test - public void testProcessSuccess() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_SUCCESS, ID_INT, null); - EapResult result = mStartState.process(input); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessFailure() throws Exception { - EapMessage input = new EapMessage(EAP_CODE_FAILURE, ID_INT, null); - EapResult result = mStartState.process(input); - assertTrue(mEapSimMethodStateMachine.getState() instanceof FinalState); - - assertTrue(result instanceof EapFailure); - } - - @Test - public void testProcessIncorrectEapMethodType() throws Exception { - EapData eapData = new EapData(EAP_IDENTITY, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - - EapResult result = mStartState.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testIsValidStartAttributes() throws Exception { - mAttributes.put(EAP_AT_VERSION_LIST, new AtVersionList(8, 1)); - mAttributes.put(EAP_AT_PERMANENT_ID_REQ, new AtPermanentIdReq()); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - assertTrue(mStartState.isValidStartAttributes(eapSimTypeData)); - } - - @Test - public void testIsValidStartAttributesMissingVersionList() throws Exception { - mAttributes.put(EAP_AT_PERMANENT_ID_REQ, new AtPermanentIdReq()); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - assertFalse(mStartState.isValidStartAttributes(eapSimTypeData)); - } - - @Test - public void testIsValidStartAttributesMultipleIdRequests() throws Exception { - mAttributes.put(EAP_AT_VERSION_LIST, new AtVersionList(8, 1)); - mAttributes.put(EAP_AT_PERMANENT_ID_REQ, new AtPermanentIdReq()); - mAttributes.put(EAP_AT_ANY_ID_REQ, new AtAnyIdReq()); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - assertFalse(mStartState.isValidStartAttributes(eapSimTypeData)); - } - - @Test - public void testIsValidStartAttributesInvalidAttributes() throws Exception { - mAttributes.put(EAP_AT_VERSION_LIST, new AtVersionList(8, 1)); - mAttributes.put(EAP_AT_PERMANENT_ID_REQ, new AtPermanentIdReq()); - mAttributes.put(EAP_AT_MAC, new AtMac()); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - assertFalse(mStartState.isValidStartAttributes(eapSimTypeData)); - - mAttributes.remove(EAP_AT_MAC); - mAttributes.put(EAP_AT_IV, null); // just need <K, V> pair in the map - eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - assertFalse(mStartState.isValidStartAttributes(eapSimTypeData)); - - mAttributes.remove(EAP_AT_IV); - mAttributes.put(EAP_AT_ENCR_DATA, null); // just need <K, V> pair in the map - eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - assertFalse(mStartState.isValidStartAttributes(eapSimTypeData)); - } - - @Test - public void testAddIdentityAttributeToResponse() throws Exception { - EapSimTypeData eapSimTypeData = new EapSimTypeData( - EAP_SIM_START, Arrays.asList(new AtPermanentIdReq())); - - when(mMockTelephonyManager.getSubscriberId()).thenReturn(IMSI); - - AtIdentity atIdentity = mStartState.getIdentityResponse(eapSimTypeData); - assertArrayEquals(EAP_SIM_IDENTITY.getBytes(), mStartState.mIdentity); - verify(mMockTelephonyManager).getSubscriberId(); - assertArrayEquals(EAP_SIM_IDENTITY.getBytes(), atIdentity.identity); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testAddIdentityAttributeToResponseImsiUnavailable() throws Exception { - EapMessage eapMessage = new EapMessage( - EAP_CODE_REQUEST, - ID_INT, - new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA)); - mAttributes.put(EAP_AT_VERSION_LIST, new AtVersionList(8, 1)); - mAttributes.put(EAP_AT_PERMANENT_ID_REQ, new AtPermanentIdReq()); - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, mAttributes); - DecodeResult decodeResult = new DecodeResult(eapSimTypeData); - - when(mMockEapSimTypeDataDecoder.decode(DUMMY_EAP_TYPE_DATA)).thenReturn(decodeResult); - when(mMockTelephonyManager.getSubscriberId()).thenReturn(null); - - EapResult result = mStartState.process(eapMessage); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapSimAkaIdentityUnavailableException); - - verify(mMockTelephonyManager).getSubscriberId(); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testAddIdentityAttributeToResponseNoIdRequest() throws Exception { - EapSimTypeData eapSimTypeData = new EapSimTypeData(EAP_SIM_START, Arrays.asList()); - - AtIdentity atIdentity = mStartState.getIdentityResponse(eapSimTypeData); - assertNull(atIdentity); - verifyNoMoreInteractions(mMockTelephonyManager); - } - - @Test - public void testProcessWithoutIdentityRequest() throws Exception { - EapMessage eapMessage = - new EapMessage( - EAP_CODE_REQUEST, ID_INT, new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA)); - - // Send EAP-SIM/Start message without Identity request - mAttributes.put(EAP_AT_VERSION_LIST, new AtVersionList(8, 1)); - DecodeResult eapSimStartDecodeResult = - new DecodeResult(new EapSimTypeData(EAP_SIM_START, mAttributes)); - when(mMockEapSimTypeDataDecoder.decode(DUMMY_EAP_TYPE_DATA)) - .thenReturn(eapSimStartDecodeResult); - - EapResult result = mEapSimMethodStateMachine.process(eapMessage); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals( - Log.byteArrayToHexString(eapResponse.packet), - EAP_SIM_RESPONSE_WITHOUT_IDENTITY, - eapResponse.packet); - - verify(mMockEapSimTypeDataDecoder).decode(eq(DUMMY_EAP_TYPE_DATA)); - - // Send EAP-SIM/Challenge message - DecodeResult eapSimChallengeDecodeResult = - new DecodeResult(new EapSimTypeData(EAP_SIM_CHALLENGE, new LinkedHashMap<>())); - when(mMockEapSimTypeDataDecoder.decode(DUMMY_EAP_TYPE_DATA)) - .thenReturn(eapSimChallengeDecodeResult); - - // We only care about the transition to ChallengeState - the response doesn't matter - mEapSimMethodStateMachine.process(eapMessage); - ChallengeState challengeState = (ChallengeState) mEapSimMethodStateMachine.getState(); - assertArrayEquals(EAP_IDENTITY_BYTES, challengeState.mIdentity); - - // verify decode called 3x times: - // 1. decode in EAP-SIM/Start test above - // 2. decode in EAP-SIM/Challenge test for StartState - // 3. decode in EAP-SIM/Challenge test for ChallengeState - verify(mMockEapSimTypeDataDecoder, times(3)).decode(eq(DUMMY_EAP_TYPE_DATA)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimStateTest.java deleted file mode 100644 index d8d18416..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapSimStateTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; -import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; -import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_CLIENT_ERROR_INSUFFICIENT_CHALLENGES; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SIM_NOTIFICATION_RESPONSE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification.GENERAL_FAILURE_PRE_CHALLENGE; -import static com.android.internal.net.eap.message.simaka.EapSimTypeData.EAP_SIM_NOTIFICATION; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig.EapSimConfig; -import android.telephony.TelephonyManager; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.message.EapData; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; -import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtNotification; -import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; -import com.android.internal.net.eap.message.simaka.EapSimTypeData; -import com.android.internal.net.eap.message.simaka.EapSimTypeData.EapSimTypeDataDecoder; -import com.android.internal.net.eap.statemachine.EapMethodStateMachine.EapMethodState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; -import java.util.Arrays; - -public class EapSimStateTest { - protected static final int SUB_ID = 1; - protected static final String NOTIFICATION_MESSAGE = "test"; - protected static final byte[] DUMMY_EAP_TYPE_DATA = hexStringToByteArray("112233445566"); - - // EAP-Identity = hex("test@android.net") - protected static final byte[] EAP_IDENTITY_BYTES = - hexStringToByteArray("7465737440616E64726F69642E6E6574"); - - protected TelephonyManager mMockTelephonyManager; - protected EapSimTypeDataDecoder mMockEapSimTypeDataDecoder; - - protected EapSimConfig mEapSimConfig = new EapSimConfig(SUB_ID, APPTYPE_USIM); - protected EapSimMethodStateMachine mEapSimMethodStateMachine; - - @Before - public void setUp() { - mMockTelephonyManager = mock(TelephonyManager.class); - mMockEapSimTypeDataDecoder = mock(EapSimTypeDataDecoder.class); - - when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) - .thenReturn(mMockTelephonyManager); - - mEapSimMethodStateMachine = - new EapSimMethodStateMachine( - mMockTelephonyManager, - EAP_IDENTITY_BYTES, - mEapSimConfig, - new SecureRandom(), - mMockEapSimTypeDataDecoder); - - verify(mMockTelephonyManager).createForSubscriptionId(SUB_ID); - } - - @Test - public void testProcessNotification() throws Exception { - EapData eapData = new EapData(EAP_NOTIFICATION, NOTIFICATION_MESSAGE.getBytes()); - EapMessage notification = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preNotification = (EapMethodState) mEapSimMethodStateMachine.getState(); - - EapResult result = mEapSimMethodStateMachine.process(notification); - assertEquals(preNotification, mEapSimMethodStateMachine.getState()); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapSimTypeDataDecoder); - - assertTrue(result instanceof EapResponse); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - } - - @Test - public void testProcessEapSimNotification() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preProcess = (EapMethodState) mEapSimMethodStateMachine.getState(); - EapSimTypeData typeData = - new EapSimTypeData( - EAP_SIM_NOTIFICATION, - Arrays.asList(new AtNotification(GENERAL_FAILURE_PRE_CHALLENGE))); - - DecodeResult<EapSimTypeData> decodeResult = new DecodeResult<>(typeData); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResponse eapResponse = (EapResponse) mEapSimMethodStateMachine.process(eapMessage); - assertArrayEquals(EAP_SIM_NOTIFICATION_RESPONSE, eapResponse.packet); - assertEquals(preProcess, mEapSimMethodStateMachine.getState()); - verify(mMockEapSimTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapSimTypeDataDecoder); - } - - @Test - public void testProcessInvalidDecodeResult() throws Exception { - EapData eapData = new EapData(EAP_TYPE_SIM, DUMMY_EAP_TYPE_DATA); - EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData); - EapMethodState preProcess = (EapMethodState) mEapSimMethodStateMachine.getState(); - - AtClientErrorCode atClientErrorCode = AtClientErrorCode.INSUFFICIENT_CHALLENGES; - DecodeResult<EapSimTypeData> decodeResult = new DecodeResult<>(atClientErrorCode); - when(mMockEapSimTypeDataDecoder.decode(eq(DUMMY_EAP_TYPE_DATA))).thenReturn(decodeResult); - - EapResult result = mEapSimMethodStateMachine.process(eapMessage); - assertEquals(preProcess, mEapSimMethodStateMachine.getState()); - verify(mMockEapSimTypeDataDecoder).decode(DUMMY_EAP_TYPE_DATA); - verifyNoMoreInteractions(mMockTelephonyManager, mMockEapSimTypeDataDecoder); - - assertTrue(result instanceof EapResponse); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_SIM_CLIENT_ERROR_INSUFFICIENT_CHALLENGES, eapResponse.packet); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapStateMachineTest.java deleted file mode 100644 index 288fc85f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapStateMachineTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static androidx.test.InstrumentationRegistry.getInstrumentation; - -import static com.android.internal.net.eap.EapTestUtils.getDummyEapSessionConfig; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SUCCESS_PACKET; - -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.net.eap.EapSessionConfig; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.statemachine.EapStateMachine.CreatedState; -import com.android.internal.net.eap.statemachine.EapStateMachine.FailureState; -import com.android.internal.net.eap.statemachine.EapStateMachine.SuccessState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; - -public class EapStateMachineTest { - private Context mContext; - private EapSessionConfig mEapSessionConfig; - - @Before - public void setUp() { - mContext = getInstrumentation().getContext(); - mEapSessionConfig = getDummyEapSessionConfig(); - } - - @Test - public void testEapStateMachineStartState() { - EapStateMachine eapStateMachine = - new EapStateMachine(mContext, mEapSessionConfig, new SecureRandom()); - assertTrue(eapStateMachine.getState() instanceof CreatedState); - } - - @Test - public void testSuccessStateProcessFails() { - SuccessState successState = - new EapStateMachine(mContext, mEapSessionConfig, new SecureRandom()) - .new SuccessState(); - EapResult result = successState.process(EAP_SUCCESS_PACKET); - assertTrue(result instanceof EapError); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testFailureStateProcessFails() { - FailureState failureState = - new EapStateMachine(mContext, mEapSessionConfig, new SecureRandom()) - .new FailureState(); - EapResult result = failureState.process(EAP_SUCCESS_PACKET); - assertTrue(result instanceof EapError); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapStateTest.java deleted file mode 100644 index 0193cb4e..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/EapStateTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static androidx.test.InstrumentationRegistry.getInstrumentation; - -import static com.android.internal.net.eap.EapTestUtils.getDummyEapSimSessionConfig; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_MD5_CHALLENGE; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_NAK_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NAK_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.REQUEST_UNSUPPORTED_TYPE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.SHORT_PACKET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.net.eap.EapSessionConfig; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.exceptions.EapInvalidPacketLengthException; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.statemachine.EapStateMachine.EapState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; - -/** - * EapStateTest is a test template for testing EapState implementations. - * - * <p>Specifically, testing for CreatedState, IdentityState, and MethodState should subclass - * EapStateTest - */ -public class EapStateTest { - protected Context mContext; - protected EapSessionConfig mEapSessionConfig; - protected EapStateMachine mEapStateMachine; - protected EapState mEapState; - - @Before - public void setUp() { - mContext = getInstrumentation().getContext(); - mEapSessionConfig = getDummyEapSimSessionConfig(); - mEapStateMachine = new EapStateMachine(mContext, mEapSessionConfig, new SecureRandom()); - - // this EapState definition is used to make sure all non-Success/Failure EAP states - // produce the same results for error cases. - mEapState = mEapStateMachine.new EapState() { - @Override - public EapResult process(byte[] msg) { - return decode(msg).eapResult; - } - }; - } - - @Test - public void testProcessNullPacket() { - EapResult result = mEapState.process(null); - assertTrue(result instanceof EapError); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof IllegalArgumentException); - } - - @Test - public void testProcessUnsupportedEapDataType() { - EapResult result = mEapState.process(REQUEST_UNSUPPORTED_TYPE_PACKET); - assertTrue(result instanceof EapResponse); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NAK_PACKET, eapResponse.packet); - } - - @Test - public void testProcessDecodeFailure() { - EapResult result = mEapState.process(SHORT_PACKET); - assertTrue(result instanceof EapError); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidPacketLengthException); - } - - @Test - public void testProcessResponse() { - EapResult result = mEapState.process(EAP_RESPONSE_NOTIFICATION_PACKET); - assertTrue(result instanceof EapError); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessNakRequest() { - EapResult result = mEapState.process(EAP_REQUEST_NAK_PACKET); - assertTrue(result instanceof EapError); - - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - } - - @Test - public void testProcessMd5Challenge() { - EapResult result = mEapState.process(EAP_REQUEST_MD5_CHALLENGE); - assertTrue(result instanceof EapResponse); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NAK_PACKET, eapResponse.packet); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/IdentityStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/IdentityStateTest.java deleted file mode 100644 index abe4e824..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/IdentityStateTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static com.android.internal.net.eap.EapTestUtils.getDummyEapSessionConfig; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_IDENTITY; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_IDENTITY_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_IDENTITY_DEFAULT_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_IDENTITY_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.statemachine.EapStateMachine.MethodState; - -import org.junit.Before; -import org.junit.Test; - -import java.security.SecureRandom; - -public class IdentityStateTest extends EapStateTest { - private EapStateMachine mEapStateMachineSpy; - - @Before - @Override - public void setUp() { - super.setUp(); - - mEapStateMachineSpy = spy(mEapStateMachine); - mEapState = mEapStateMachineSpy.new IdentityState(); - } - - @Test - public void testProcess() { - mEapSessionConfig = getDummyEapSessionConfig(EAP_IDENTITY); - mEapStateMachineSpy = spy( - new EapStateMachine(mContext, mEapSessionConfig, new SecureRandom())); - mEapState = mEapStateMachineSpy.new IdentityState(); - - EapResult eapResult = mEapState.process(EAP_REQUEST_IDENTITY_PACKET); - - assertTrue(eapResult instanceof EapResponse); - EapResponse eapResponse = (EapResponse) eapResult; - assertArrayEquals(EAP_RESPONSE_IDENTITY_PACKET, eapResponse.packet); - verify(mEapStateMachineSpy, never()).transitionAndProcess(any(), any()); - } - - @Test - public void testProcessDefaultIdentity() { - EapResult eapResult = mEapState.process(EAP_REQUEST_IDENTITY_PACKET); - - assertTrue(eapResult instanceof EapResponse); - EapResponse eapResponse = (EapResponse) eapResult; - assertArrayEquals(EAP_RESPONSE_IDENTITY_DEFAULT_PACKET, eapResponse.packet); - verify(mEapStateMachineSpy, never()).transitionAndProcess(any(), any()); - } - - @Test - public void testProcessNotificationRequest() { - EapResult eapResult = mEapState.process(EAP_REQUEST_NOTIFICATION_PACKET); - - // state shouldn't change after Notification request - assertTrue(eapResult instanceof EapResponse); - EapResponse eapResponse = (EapResponse) eapResult; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - verify(mEapStateMachineSpy, never()).transitionAndProcess(any(), any()); - } - - @Test - public void testProcessSimStart() { - mEapState.process(EAP_REQUEST_SIM_START_PACKET); - - // EapStateMachine should change to MethodState for method-type packet - verify(mEapStateMachineSpy).transitionAndProcess( - any(MethodState.class), eq(EAP_REQUEST_SIM_START_PACKET)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/MethodStateTest.java b/tests/iketests/src/java/com/android/internal/net/eap/statemachine/MethodStateTest.java deleted file mode 100644 index 9df06e56..00000000 --- a/tests/iketests/src/java/com/android/internal/net/eap/statemachine/MethodStateTest.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.eap.statemachine; - -import static android.telephony.TelephonyManager.APPTYPE_USIM; - -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; -import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; -import static com.android.internal.net.eap.message.EapMessage.EAP_HEADER_LENGTH; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_AKA_PRIME_REQUEST; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_FAILURE_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_AKA; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_IDENTITY_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_MSCHAP_V2; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NAK_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EAP_SUCCESS_PACKET; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.EMSK; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.ID_INT; -import static com.android.internal.net.eap.message.EapTestMessageDefinitions.MSK; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.eap.EapSessionConfig; - -import com.android.internal.net.eap.EapResult; -import com.android.internal.net.eap.EapResult.EapError; -import com.android.internal.net.eap.EapResult.EapFailure; -import com.android.internal.net.eap.EapResult.EapResponse; -import com.android.internal.net.eap.EapResult.EapSuccess; -import com.android.internal.net.eap.exceptions.EapInvalidRequestException; -import com.android.internal.net.eap.message.EapMessage; -import com.android.internal.net.eap.statemachine.EapStateMachine.FailureState; -import com.android.internal.net.eap.statemachine.EapStateMachine.MethodState; -import com.android.internal.net.eap.statemachine.EapStateMachine.SuccessState; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentMatcher; - -import java.security.SecureRandom; - -public class MethodStateTest extends EapStateTest { - private static final String USERNAME = "username"; - private static final String PASSWORD = "password"; - private static final String NETWORK_NAME = "android.net"; - private static final boolean ALLOW_MISMATCHED_NETWORK_NAMES = true; - - @Before - @Override - public void setUp() { - super.setUp(); - mEapState = mEapStateMachine.new MethodState(); - mEapStateMachine.transitionTo(mEapState); - } - - @Test - public void testProcessUnsupportedEapType() { - mEapState = mEapStateMachine.new MethodState(); - EapResult result = mEapState.process(EAP_REQUEST_IDENTITY_PACKET); - - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NAK_PACKET, eapResponse.packet); - } - - @Test - public void testProcessTransitionsToEapSim() { - mEapStateMachine.process(EAP_REQUEST_SIM_START_PACKET); - - assertTrue(mEapStateMachine.getState() instanceof MethodState); - MethodState methodState = (MethodState) mEapStateMachine.getState(); - assertTrue(methodState.mEapMethodStateMachine instanceof EapSimMethodStateMachine); - } - - @Test - public void testProcessTransitionToEapAka() { - // make EapStateMachine with EAP-AKA configurations - EapSessionConfig eapSessionConfig = new EapSessionConfig.Builder() - .setEapAkaConfig(0, APPTYPE_USIM).build(); - mEapStateMachine = new EapStateMachine(mContext, eapSessionConfig, new SecureRandom()); - - mEapStateMachine.process(EAP_REQUEST_AKA); - - assertTrue(mEapStateMachine.getState() instanceof MethodState); - MethodState methodState = (MethodState) mEapStateMachine.getState(); - assertTrue(methodState.mEapMethodStateMachine instanceof EapAkaMethodStateMachine); - } - - @Test - public void testProcessTransitionToEapAkaPrime() { - // make EapStateMachine with EAP-AKA' configurations - EapSessionConfig eapSessionConfig = - new EapSessionConfig.Builder() - .setEapAkaPrimeConfig( - 0, APPTYPE_USIM, NETWORK_NAME, ALLOW_MISMATCHED_NETWORK_NAMES) - .build(); - mEapStateMachine = new EapStateMachine(mContext, eapSessionConfig, new SecureRandom()); - - mEapStateMachine.process(EAP_AKA_PRIME_REQUEST); - - assertTrue(mEapStateMachine.getState() instanceof MethodState); - MethodState methodState = (MethodState) mEapStateMachine.getState(); - assertTrue(methodState.mEapMethodStateMachine instanceof EapAkaPrimeMethodStateMachine); - } - - @Test - public void testProcessTransitionToEapMsChapV2() { - // make EapStateMachine with EAP MSCHAPv2 configurations - EapSessionConfig eapSessionConfig = - new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build(); - mEapStateMachine = new EapStateMachine(mContext, eapSessionConfig, new SecureRandom()); - - mEapStateMachine.process(EAP_REQUEST_MSCHAP_V2); - - assertTrue(mEapStateMachine.getState() instanceof MethodState); - MethodState methodState = (MethodState) mEapStateMachine.getState(); - assertTrue(methodState.mEapMethodStateMachine instanceof EapMsChapV2MethodStateMachine); - } - - @Test - public void testProcessTransitionToSuccessState() { - EapSuccess eapSuccess = new EapSuccess(MSK, EMSK); - - ArgumentMatcher<EapMessage> eapSuccessMatcher = msg -> - msg.eapCode == EAP_CODE_SUCCESS - && msg.eapIdentifier == ID_INT - && msg.eapLength == EAP_HEADER_LENGTH - && msg.eapData == null; - - EapMethodStateMachine mockEapMethodStateMachine = mock(EapMethodStateMachine.class); - when(mockEapMethodStateMachine.process(argThat(eapSuccessMatcher))).thenReturn(eapSuccess); - ((MethodState) mEapState).mEapMethodStateMachine = mockEapMethodStateMachine; - - mEapState.process(EAP_SUCCESS_PACKET); - verify(mockEapMethodStateMachine).process(argThat(eapSuccessMatcher)); - assertTrue(mEapStateMachine.getState() instanceof SuccessState); - verifyNoMoreInteractions(mockEapMethodStateMachine); - } - - @Test - public void testProcessTransitionToFailureState() { - EapFailure eapFailure = new EapFailure(); - - ArgumentMatcher<EapMessage> eapSuccessMatcher = msg -> - msg.eapCode == EAP_CODE_FAILURE - && msg.eapIdentifier == ID_INT - && msg.eapLength == EAP_HEADER_LENGTH - && msg.eapData == null; - - EapMethodStateMachine mockEapMethodStateMachine = mock(EapMethodStateMachine.class); - when(mockEapMethodStateMachine.process(argThat(eapSuccessMatcher))).thenReturn(eapFailure); - ((MethodState) mEapState).mEapMethodStateMachine = mockEapMethodStateMachine; - - mEapState.process(EAP_FAILURE_PACKET); - verify(mockEapMethodStateMachine).process(argThat(eapSuccessMatcher)); - assertTrue(mEapStateMachine.getState() instanceof FailureState); - verifyNoMoreInteractions(mockEapMethodStateMachine); - } - - @Test - public void testProcessEapFailureWithNoEapMethodState() { - EapResult result = mEapStateMachine.process(EAP_FAILURE_PACKET); - assertTrue(result instanceof EapFailure); - assertTrue(mEapStateMachine.getState() instanceof FailureState); - } - - @Test - public void testProcessEapSuccessWithNoEapMethodState() { - EapResult result = mEapStateMachine.process(EAP_SUCCESS_PACKET); - EapError eapError = (EapError) result; - assertTrue(eapError.cause instanceof EapInvalidRequestException); - assertTrue(mEapStateMachine.getState() instanceof MethodState); - } - - @Test - public void testProcessEapNotificationWithNoEapMethodState() { - EapResult result = mEapStateMachine.process(EAP_REQUEST_NOTIFICATION_PACKET); - EapResponse eapResponse = (EapResponse) result; - assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet); - assertTrue(mEapStateMachine.getState() instanceof MethodState); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java deleted file mode 100644 index f6c32eef..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java +++ /dev/null @@ -1,1646 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; -import static android.system.OsConstants.AF_INET; - -import static com.android.internal.net.ipsec.ike.ChildSessionStateMachine.CMD_FORCE_TRANSITION; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.REKEY_DELETE_TIMEOUT_MS; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_KE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NONCE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PROTOCOL_ID_ESP; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.IpSecTransform; -import android.net.LinkAddress; -import android.net.ipsec.ike.ChildSaProposal; -import android.net.ipsec.ike.ChildSessionCallback; -import android.net.ipsec.ike.ChildSessionConfiguration; -import android.net.ipsec.ike.ChildSessionOptions; -import android.net.ipsec.ike.IkeManager; -import android.net.ipsec.ike.IkeTrafficSelector; -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.TunnelModeChildSessionOptions; -import android.net.ipsec.ike.exceptions.IkeException; -import android.net.ipsec.ike.exceptions.IkeInternalException; -import android.os.test.TestLooper; - -import androidx.test.InstrumentationRegistry; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachine.CreateChildSaHelper; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachine.IChildSessionSmCallback; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; -import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecord; -import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecordConfig; -import com.android.internal.net.ipsec.ike.SaRecord.ISaRecordHelper; -import com.android.internal.net.ipsec.ike.SaRecord.SaRecordHelper; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask; -import com.android.internal.net.ipsec.ike.message.IkeDeletePayload; -import com.android.internal.net.ipsec.ike.message.IkeKePayload; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeNoncePayload; -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.message.IkeTestUtils; -import com.android.internal.net.ipsec.ike.message.IkeTsPayload; -import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils; -import com.android.internal.net.utils.Log; -import com.android.server.IpSecService; - -import libcore.net.InetAddressUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.security.GeneralSecurityException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executor; - -public final class ChildSessionStateMachineTest { - private static final String TAG = "ChildSessionStateMachineTest"; - - private static final Inet4Address LOCAL_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private static final Inet4Address REMOTE_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - private static final Inet4Address INTERNAL_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("203.0.113.100")); - - private static final int IPV4_PREFIX_LEN = 32; - - private static final String IKE_AUTH_RESP_SA_PAYLOAD = - "2c00002c0000002801030403cae7019f0300000c0100000c800e0080" - + "03000008030000020000000805000000"; - private static final String REKEY_CHILD_RESP_SA_PAYLOAD = - "2800002c0000002801030403cd1736b30300000c0100000c800e0080" - + "03000008030000020000000805000000"; - private static final String REKEY_CHILD_REQ_SA_PAYLOAD = - "2800002c0000002801030403c88336490300000c0100000c800e0080" - + "03000008030000020000000805000000"; - private static final String REKEY_CHILD_UNACCEPTABLE_REQ_SA_PAYLOAD = - "2800002c0000002801030403c88336490300000c0100000c800e00c0" - + "03000008030000020000000805000000"; - - private static final int CURRENT_CHILD_SA_SPI_IN = 0x2ad4c0a2; - private static final int CURRENT_CHILD_SA_SPI_OUT = 0xcae7019f; - - private static final int LOCAL_INIT_NEW_CHILD_SA_SPI_IN = 0x57a09b0f; - private static final int LOCAL_INIT_NEW_CHILD_SA_SPI_OUT = 0xcd1736b3; - - private static final int REMOTE_INIT_NEW_CHILD_SA_SPI_IN = 0xd2d01795; - private static final int REMOTE_INIT_NEW_CHILD_SA_SPI_OUT = 0xc8833649; - - private static final String IKE_SK_D_HEX_STRING = "C86B56EFCF684DCC2877578AEF3137167FE0EBF6"; - private static final byte[] SK_D = TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING); - - private static final int KEY_LEN_IKE_SKD = 20; - - private IkeMacPrf mIkePrf; - - private Context mContext; - private IpSecService mMockIpSecService; - private IpSecManager mMockIpSecManager; - private UdpEncapsulationSocket mMockUdpEncapSocket; - - private TestLooper mLooper; - private ChildSessionStateMachine mChildSessionStateMachine; - - private List<IkePayload> mFirstSaReqPayloads = new LinkedList<>(); - private List<IkePayload> mFirstSaRespPayloads = new LinkedList<>(); - - private ChildSaRecord mSpyCurrentChildSaRecord; - private ChildSaRecord mSpyLocalInitNewChildSaRecord; - private ChildSaRecord mSpyRemoteInitNewChildSaRecord; - - private Log mSpyIkeLog; - - private ISaRecordHelper mMockSaRecordHelper; - - private ChildSessionOptions mChildSessionOptions; - private EncryptionTransform mChildEncryptionTransform; - private IntegrityTransform mChildIntegrityTransform; - private DhGroupTransform mChildDhGroupTransform; - - private ChildSaProposal mMockNegotiatedProposal; - - private Executor mSpyUserCbExecutor; - private ChildSessionCallback mMockChildSessionCallback; - private IChildSessionSmCallback mMockChildSessionSmCallback; - - private ArgumentCaptor<ChildSaRecordConfig> mChildSaRecordConfigCaptor = - ArgumentCaptor.forClass(ChildSaRecordConfig.class); - private ArgumentCaptor<List<IkePayload>> mPayloadListCaptor = - ArgumentCaptor.forClass(List.class); - private ArgumentCaptor<ChildSessionConfiguration> mChildConfigCaptor = - ArgumentCaptor.forClass(ChildSessionConfiguration.class); - - private ArgumentMatcher<ChildLocalRequest> mRekeyChildLocalReqMatcher = - (argument) -> { - return CMD_LOCAL_REQUEST_REKEY_CHILD == argument.procedureType - && mMockChildSessionCallback == argument.childSessionCallback; - }; - - public ChildSessionStateMachineTest() { - mMockSaRecordHelper = mock(SaRecord.ISaRecordHelper.class); - mMockChildSessionSmCallback = mock(IChildSessionSmCallback.class); - - mChildEncryptionTransform = - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128); - mChildIntegrityTransform = - new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96); - - mChildDhGroupTransform = new DhGroupTransform(SaProposal.DH_GROUP_1024_BIT_MODP); - } - - @Before - public void setup() throws Exception { - mSpyIkeLog = TestUtils.makeSpyLogThrowExceptionForWtf(TAG); - IkeManager.setIkeLog(mSpyIkeLog); - - mIkePrf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - - mContext = InstrumentationRegistry.getContext(); - mMockIpSecService = mock(IpSecService.class); - mMockIpSecManager = new IpSecManager(mContext, mMockIpSecService); - mMockUdpEncapSocket = mock(UdpEncapsulationSocket.class); - - mMockNegotiatedProposal = mock(ChildSaProposal.class); - - mSpyUserCbExecutor = - spy( - (command) -> { - command.run(); - }); - - mMockChildSessionCallback = mock(ChildSessionCallback.class); - mChildSessionOptions = buildChildSessionOptions(); - - // Setup thread and looper - mLooper = new TestLooper(); - mChildSessionStateMachine = - new ChildSessionStateMachine( - mLooper.getLooper(), - mContext, - mMockIpSecManager, - mChildSessionOptions, - mSpyUserCbExecutor, - mMockChildSessionCallback, - mMockChildSessionSmCallback); - mChildSessionStateMachine.setDbg(true); - SaRecord.setSaRecordHelper(mMockSaRecordHelper); - - setUpFirstSaNegoPayloadLists(); - setUpChildSaRecords(); - - mChildSessionStateMachine.start(); - } - - @After - public void tearDown() { - mChildSessionStateMachine.setDbg(false); - IkeManager.resetIkeLog(); - SaRecord.setSaRecordHelper(new SaRecordHelper()); - } - - private ChildSaProposal buildSaProposal() throws Exception { - return new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) - .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) - .build(); - } - - private ChildSessionOptions buildChildSessionOptions() throws Exception { - return new TunnelModeChildSessionOptions.Builder() - .addSaProposal(buildSaProposal()) - .addInternalAddressRequest(AF_INET, 1) - .addInternalAddressRequest(INTERNAL_ADDRESS, IPV4_PREFIX_LEN) - .build(); - } - - private void setUpChildSaRecords() { - mSpyCurrentChildSaRecord = - makeSpyChildSaRecord(CURRENT_CHILD_SA_SPI_IN, CURRENT_CHILD_SA_SPI_OUT); - mSpyLocalInitNewChildSaRecord = - makeSpyChildSaRecord( - LOCAL_INIT_NEW_CHILD_SA_SPI_IN, LOCAL_INIT_NEW_CHILD_SA_SPI_OUT); - mSpyRemoteInitNewChildSaRecord = - makeSpyChildSaRecord( - REMOTE_INIT_NEW_CHILD_SA_SPI_IN, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT); - } - - private void setUpSpiResource(InetAddress address, int spiRequested) throws Exception { - when(mMockIpSecService.allocateSecurityParameterIndex( - eq(address.getHostAddress()), anyInt(), anyObject())) - .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(spiRequested)); - } - - private void setUpFirstSaNegoPayloadLists() throws Exception { - // Build locally generated SA payload that has its SPI resource allocated. - setUpSpiResource(LOCAL_ADDRESS, CURRENT_CHILD_SA_SPI_IN); - IkeSaPayload reqSaPayload = - IkeSaPayload.createChildSaRequestPayload( - mChildSessionOptions.getSaProposals(), mMockIpSecManager, LOCAL_ADDRESS); - mFirstSaReqPayloads.add(reqSaPayload); - - // Build a remotely generated SA payload whoes SPI resource has not been allocated. - setUpSpiResource(REMOTE_ADDRESS, CURRENT_CHILD_SA_SPI_OUT); - IkeSaPayload respSaPayload = - (IkeSaPayload) - (IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_SA, true, IKE_AUTH_RESP_SA_PAYLOAD)); - mFirstSaRespPayloads.add(respSaPayload); - - // Build TS Payloads - IkeTsPayload tsInitPayload = - new IkeTsPayload( - true /*isInitiator*/, mChildSessionOptions.getLocalTrafficSelectors()); - IkeTsPayload tsRespPayload = - new IkeTsPayload( - false /*isInitiator*/, mChildSessionOptions.getRemoteTrafficSelectors()); - - mFirstSaReqPayloads.add(tsInitPayload); - mFirstSaReqPayloads.add(tsRespPayload); - mFirstSaRespPayloads.add(tsInitPayload); - mFirstSaRespPayloads.add(tsRespPayload); - - // Build Nonce Payloads - mFirstSaReqPayloads.add(new IkeNoncePayload()); - mFirstSaRespPayloads.add(new IkeNoncePayload()); - - // Build Config Request Payload - List<ConfigAttribute> attrReqList = new LinkedList<>(); - attrReqList.add(new ConfigAttributeIpv4Address(INTERNAL_ADDRESS)); - attrReqList.add(new ConfigAttributeIpv4Netmask()); - mFirstSaReqPayloads.add(new IkeConfigPayload(false /*isReply*/, attrReqList)); - - // Build Config Reply Payload - List<ConfigAttribute> attrRespList = new LinkedList<>(); - attrRespList.add(new ConfigAttributeIpv4Address(INTERNAL_ADDRESS)); - mFirstSaRespPayloads.add(new IkeConfigPayload(true /*isReply*/, attrRespList)); - } - - private ChildSaRecord makeSpyChildSaRecord(int inboundSpi, int outboundSpi) { - ChildSaRecord child = - spy( - new ChildSaRecord( - inboundSpi, - outboundSpi, - true /*localInit*/, - null, - null, - null, - null, - null, - null, - mock(IpSecTransform.class), - mock(IpSecTransform.class), - mock(ChildLocalRequest.class))); - doNothing().when(child).close(); - return child; - } - - private void quitAndVerify() { - mChildSessionStateMachine.mCurrentChildSaRecord = null; - mChildSessionStateMachine.mLocalInitNewChildSaRecord = null; - mChildSessionStateMachine.mRemoteInitNewChildSaRecord = null; - - reset(mMockChildSessionSmCallback); - mChildSessionStateMachine.quit(); - mLooper.dispatchAll(); - - verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine); - verify(mMockChildSessionSmCallback).onChildSessionClosed(mMockChildSessionCallback); - } - - private void verifyChildSaRecordConfig( - ChildSaRecordConfig childSaRecordConfig, - int initSpi, - int respSpi, - boolean isLocalInit) { - assertEquals(mContext, childSaRecordConfig.context); - assertEquals(initSpi, childSaRecordConfig.initSpi.getSpi()); - assertEquals(respSpi, childSaRecordConfig.respSpi.getSpi()); - - if (isLocalInit) { - assertEquals(LOCAL_ADDRESS, childSaRecordConfig.initAddress); - assertEquals(REMOTE_ADDRESS, childSaRecordConfig.respAddress); - } else { - assertEquals(REMOTE_ADDRESS, childSaRecordConfig.initAddress); - assertEquals(LOCAL_ADDRESS, childSaRecordConfig.respAddress); - } - - assertEquals(mMockUdpEncapSocket, childSaRecordConfig.udpEncapSocket); - assertEquals(mIkePrf, childSaRecordConfig.ikePrf); - assertArrayEquals(SK_D, childSaRecordConfig.skD); - assertFalse(childSaRecordConfig.isTransport); - assertEquals(isLocalInit, childSaRecordConfig.isLocalInit); - assertTrue(childSaRecordConfig.hasIntegrityAlgo); - assertEquals( - CMD_LOCAL_REQUEST_REKEY_CHILD, childSaRecordConfig.futureRekeyEvent.procedureType); - assertEquals( - mMockChildSessionCallback, - childSaRecordConfig.futureRekeyEvent.childSessionCallback); - } - - private void verifyNotifyUsersCreateIpSecSa( - ChildSaRecord childSaRecord, boolean expectInbound) { - IpSecTransform transform = - expectInbound - ? childSaRecord.getInboundIpSecTransform() - : childSaRecord.getOutboundIpSecTransform(); - int direction = expectInbound ? IpSecManager.DIRECTION_IN : IpSecManager.DIRECTION_OUT; - - verify(mMockChildSessionCallback).onIpSecTransformCreated(eq(transform), eq(direction)); - } - - private void verifyInitCreateChildResp( - List<IkePayload> reqPayloads, List<IkePayload> respPayloads) throws Exception { - verify(mMockChildSessionSmCallback) - .onChildSaCreated( - mSpyCurrentChildSaRecord.getRemoteSpi(), mChildSessionStateMachine); - verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - - // Validate negotiated SA proposal. - ChildSaProposal negotiatedProposal = mChildSessionStateMachine.mSaProposal; - assertNotNull(negotiatedProposal); - assertEquals( - new EncryptionTransform[] {mChildEncryptionTransform}, - negotiatedProposal.getEncryptionTransforms()); - assertEquals( - new IntegrityTransform[] {mChildIntegrityTransform}, - negotiatedProposal.getIntegrityTransforms()); - - // Validate current ChildSaRecord - verify(mMockSaRecordHelper) - .makeChildSaRecord( - eq(reqPayloads), eq(respPayloads), mChildSaRecordConfigCaptor.capture()); - ChildSaRecordConfig childSaRecordConfig = mChildSaRecordConfigCaptor.getValue(); - - verifyChildSaRecordConfig( - childSaRecordConfig, - CURRENT_CHILD_SA_SPI_IN, - CURRENT_CHILD_SA_SPI_OUT, - true /*isLocalInit*/); - - assertEquals(mSpyCurrentChildSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord); - - verify(mMockChildSessionSmCallback) - .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine)); - verify(mMockChildSessionSmCallback) - .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong()); - verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - - // Verify users have been notified - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verifyNotifyUsersCreateIpSecSa(mSpyCurrentChildSaRecord, true /*expectInbound*/); - verifyNotifyUsersCreateIpSecSa(mSpyCurrentChildSaRecord, false /*expectInbound*/); - verify(mMockChildSessionCallback).onOpened(mChildConfigCaptor.capture()); - - // Verify Child Session Configuration - ChildSessionConfiguration sessionConfig = mChildConfigCaptor.getValue(); - verifyTsList( - Arrays.asList(mChildSessionOptions.getLocalTrafficSelectors()), - sessionConfig.getInboundTrafficSelectors()); - verifyTsList( - Arrays.asList(mChildSessionOptions.getRemoteTrafficSelectors()), - sessionConfig.getOutboundTrafficSelectors()); - - List<LinkAddress> addrList = sessionConfig.getInternalAddressList(); - assertEquals(1, addrList.size()); - assertEquals(INTERNAL_ADDRESS, addrList.get(0).getAddress()); - assertEquals(IPV4_PREFIX_LEN, addrList.get(0).getPrefixLength()); - } - - private void verifyTsList( - List<IkeTrafficSelector> expectedList, List<IkeTrafficSelector> tsList) { - assertEquals(expectedList.size(), tsList.size()); - for (int i = 0; i < expectedList.size(); i++) { - assertEquals(expectedList.get(i), tsList.get(i)); - } - } - - @Test - public void testCreateFirstChild() throws Exception { - when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any())) - .thenReturn(mSpyCurrentChildSaRecord); - - mChildSessionStateMachine.handleFirstChildExchange( - mFirstSaReqPayloads, - mFirstSaRespPayloads, - LOCAL_ADDRESS, - REMOTE_ADDRESS, - mMockUdpEncapSocket, - mIkePrf, - SK_D); - mLooper.dispatchAll(); - - verifyInitCreateChildResp(mFirstSaReqPayloads, mFirstSaRespPayloads); - - quitAndVerify(); - } - - private void verifyOutboundCreatePayloadTypes( - List<IkePayload> outboundPayloads, boolean isRekey) { - assertNotNull( - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_SA, IkeSaPayload.class, outboundPayloads)); - assertNotNull( - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_TS_INITIATOR, IkeTsPayload.class, outboundPayloads)); - assertNotNull( - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_TS_RESPONDER, IkeTsPayload.class, outboundPayloads)); - assertNotNull( - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_NONCE, IkeNoncePayload.class, outboundPayloads)); - assertNull( - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_KE, IkeKePayload.class, outboundPayloads)); - - IkeConfigPayload configPayload = - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_CP, IkeConfigPayload.class, outboundPayloads); - if (isRekey) { - assertNull(configPayload); - } else { - assertNotNull(configPayload); - assertEquals(IkeConfigPayload.CONFIG_TYPE_REQUEST, configPayload.configType); - } - } - - @Test - public void testCreateChild() throws Exception { - when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any())) - .thenReturn(mSpyCurrentChildSaRecord); - - mChildSessionStateMachine.createChildSession( - LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, SK_D); - mLooper.dispatchAll(); - - // Validate outbound payload list - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_CREATE_CHILD_SA), - eq(false), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - - List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue(); - verifyOutboundCreatePayloadTypes(reqPayloadList, false /*isRekey*/); - assertTrue( - IkePayload.getPayloadListForTypeInProvidedList( - PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, reqPayloadList) - .isEmpty()); - - mChildSessionStateMachine.receiveResponse( - EXCHANGE_TYPE_CREATE_CHILD_SA, mFirstSaRespPayloads); - mLooper.dispatchAll(); - - verifyInitCreateChildResp(reqPayloadList, mFirstSaRespPayloads); - - quitAndVerify(); - } - - private <T extends IkeException> void verifyHandleFatalErrorAndQuit(Class<T> exceptionClass) { - assertNull(mChildSessionStateMachine.getCurrentState()); - verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine); - verify(mMockChildSessionSmCallback).onChildSessionClosed(mMockChildSessionCallback); - - verify(mMockChildSessionCallback).onClosedExceptionally(any(exceptionClass)); - } - - @Test - public void testCreateChildHandlesErrorNotifyResp() throws Exception { - // Send out Create request - mChildSessionStateMachine.createChildSession( - LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, SK_D); - mLooper.dispatchAll(); - - // Receive error notification in Create response - IkeNotifyPayload notifyPayload = new IkeNotifyPayload(ERROR_TYPE_NO_PROPOSAL_CHOSEN); - List<IkePayload> respPayloads = new LinkedList<>(); - respPayloads.add(notifyPayload); - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads); - mLooper.dispatchAll(); - - // Verify no SPI for provisional Child was registered. - verify(mMockChildSessionSmCallback, never()) - .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine)); - - // Verify user was notified and state machine has quit. - verifyHandleFatalErrorAndQuit(NoValidProposalChosenException.class); - } - - @Test - public void testCreateChildHandlesRespWithMissingPayload() throws Exception { - // Send out Create request - mChildSessionStateMachine.createChildSession( - LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, SK_D); - mLooper.dispatchAll(); - - // Receive response with no Nonce Payload - List<IkePayload> respPayloads = new LinkedList<>(); - for (IkePayload payload : mFirstSaRespPayloads) { - if (IkePayload.PAYLOAD_TYPE_NONCE == payload.payloadType) continue; - respPayloads.add(payload); - } - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads); - mLooper.dispatchAll(); - - // Verify SPI for provisional Child was registered and unregistered. - verify(mMockChildSessionSmCallback) - .onChildSaCreated(CURRENT_CHILD_SA_SPI_OUT, mChildSessionStateMachine); - verify(mMockChildSessionSmCallback).onChildSaDeleted(CURRENT_CHILD_SA_SPI_OUT); - - // Verify user was notified and state machine has quit. - verifyHandleFatalErrorAndQuit(InvalidSyntaxException.class); - } - - @Test - public void testCreateChildHandlesKeyCalculationFail() throws Exception { - // Throw exception when building ChildSaRecord - when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any())) - .thenThrow( - new GeneralSecurityException("testCreateChildHandlesKeyCalculationFail")); - - // Send out and receive Create Child message - mChildSessionStateMachine.createChildSession( - LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, SK_D); - mLooper.dispatchAll(); - mChildSessionStateMachine.receiveResponse( - EXCHANGE_TYPE_CREATE_CHILD_SA, mFirstSaRespPayloads); - mLooper.dispatchAll(); - - // Verify SPI for provisional Child was registered and unregistered. - verify(mMockChildSessionSmCallback) - .onChildSaCreated(CURRENT_CHILD_SA_SPI_OUT, mChildSessionStateMachine); - verify(mMockChildSessionSmCallback).onChildSaDeleted(CURRENT_CHILD_SA_SPI_OUT); - - // Verify user was notified and state machine has quit. - verifyHandleFatalErrorAndQuit(IkeInternalException.class); - } - - private void setupIdleStateMachine() throws Exception { - mChildSessionStateMachine.mLocalAddress = LOCAL_ADDRESS; - mChildSessionStateMachine.mRemoteAddress = REMOTE_ADDRESS; - mChildSessionStateMachine.mUdpEncapSocket = mMockUdpEncapSocket; - mChildSessionStateMachine.mIkePrf = mIkePrf; - mChildSessionStateMachine.mSkD = SK_D; - - mChildSessionStateMachine.mSaProposal = buildSaProposal(); - mChildSessionStateMachine.mChildCipher = mock(IkeCipher.class); - mChildSessionStateMachine.mChildIntegrity = mock(IkeMacIntegrity.class); - mChildSessionStateMachine.mLocalTs = mChildSessionOptions.getLocalTrafficSelectors(); - mChildSessionStateMachine.mRemoteTs = mChildSessionOptions.getRemoteTrafficSelectors(); - - mChildSessionStateMachine.mCurrentChildSaRecord = mSpyCurrentChildSaRecord; - - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mIdle); - mLooper.dispatchAll(); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - } - - private List<IkePayload> makeDeletePayloads(int spi) { - List<IkePayload> inboundPayloads = new ArrayList<>(1); - inboundPayloads.add(new IkeDeletePayload(new int[] {spi})); - return inboundPayloads; - } - - private void verifyOutboundDeletePayload(int expectedSpi, boolean isResp) { - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_INFORMATIONAL), - eq(isResp), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - - List<IkePayload> outPayloadList = mPayloadListCaptor.getValue(); - assertEquals(1, outPayloadList.size()); - - List<IkeDeletePayload> deletePayloads = - IkePayload.getPayloadListForTypeInProvidedList( - PAYLOAD_TYPE_DELETE, IkeDeletePayload.class, outPayloadList); - assertEquals(1, deletePayloads.size()); - IkeDeletePayload deletePayload = deletePayloads.get(0); - assertEquals(expectedSpi, deletePayload.spisToDelete[0]); - } - - private void verifyNotifyUserDeleteChildSa(ChildSaRecord childSaRecord) { - verify(mMockChildSessionCallback) - .onIpSecTransformDeleted( - eq(childSaRecord.getInboundIpSecTransform()), - eq(IpSecManager.DIRECTION_IN)); - verify(mMockChildSessionCallback) - .onIpSecTransformDeleted( - eq(childSaRecord.getOutboundIpSecTransform()), - eq(IpSecManager.DIRECTION_OUT)); - } - - private void verifyNotifyUsersDeleteSession() { - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockChildSessionCallback).onClosed(); - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - } - - @Test - public void testDeleteChildLocal() throws Exception { - setupIdleStateMachine(); - - // Test initiating Delete request - mChildSessionStateMachine.deleteChildSession(); - mLooper.dispatchAll(); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.DeleteChildLocalDelete); - verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), false /*isResp*/); - - // Test receiving Delete response - mChildSessionStateMachine.receiveResponse( - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - - verifyNotifyUsersDeleteSession(); - } - - @Test - public void testDeleteChildLocalHandlesInvalidResp() throws Exception { - setupIdleStateMachine(); - - // Test initiating Delete request - mChildSessionStateMachine.deleteChildSession(); - mLooper.dispatchAll(); - - // Test receiving response with no Delete Payload - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_INFORMATIONAL, new LinkedList<>()); - mLooper.dispatchAll(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - verify(mMockChildSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - } - - @Test - public void testDeleteChildLocalInInitial() throws Exception { - mChildSessionStateMachine.deleteChildSession(); - mLooper.dispatchAll(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockChildSessionCallback).onClosed(); - } - - @Test - public void testSimultaneousDeleteChild() throws Exception { - setupIdleStateMachine(); - - mChildSessionStateMachine.deleteChildSession(); - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_INFORMATIONAL), - eq(true), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - List<IkePayload> respPayloadList = mPayloadListCaptor.getValue(); - assertTrue(respPayloadList.isEmpty()); - - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_INFORMATIONAL, new LinkedList<>()); - mLooper.dispatchAll(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - - verifyNotifyUsersDeleteSession(); - } - - @Test - public void testReplyRekeyRequestDuringDeletion() throws Exception { - setupIdleStateMachine(); - - mChildSessionStateMachine.deleteChildSession(); - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, mock(List.class)); - mLooper.dispatchAll(); - - // Verify outbound response to Rekey Child request - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_INFORMATIONAL), - eq(true), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - List<IkePayload> respPayloadList = mPayloadListCaptor.getValue(); - assertEquals(1, respPayloadList.size()); - - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) respPayloadList.get(0); - assertEquals(ERROR_TYPE_TEMPORARY_FAILURE, notifyPayload.notifyType); - assertEquals(0, notifyPayload.notifyData.length); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.DeleteChildLocalDelete); - } - - @Test - public void testDeleteChildRemote() throws Exception { - setupIdleStateMachine(); - - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - // Verify response - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_INFORMATIONAL), - eq(true), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - List<IkePayload> respPayloadList = mPayloadListCaptor.getValue(); - - assertEquals(1, respPayloadList.size()); - assertArrayEquals( - new int[] {mSpyCurrentChildSaRecord.getLocalSpi()}, - ((IkeDeletePayload) respPayloadList.get(0)).spisToDelete); - - verifyNotifyUsersDeleteSession(); - } - - private void verifyOutboundRekeySaPayload(List<IkePayload> outboundPayloads, boolean isResp) { - IkeSaPayload saPayload = - IkePayload.getPayloadForTypeInProvidedList( - PAYLOAD_TYPE_SA, IkeSaPayload.class, outboundPayloads); - assertEquals(isResp, saPayload.isSaResponse); - assertEquals(1, saPayload.proposalList.size()); - - IkeSaPayload.ChildProposal proposal = - (IkeSaPayload.ChildProposal) saPayload.proposalList.get(0); - assertEquals(1, proposal.number); // Must be 1-indexed - assertEquals(mChildSessionStateMachine.mSaProposal, proposal.saProposal); - } - - private void verifyOutboundRekeyNotifyPayload(List<IkePayload> outboundPayloads) { - List<IkeNotifyPayload> notifyPayloads = - IkePayload.getPayloadListForTypeInProvidedList( - PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, outboundPayloads); - assertEquals(1, notifyPayloads.size()); - IkeNotifyPayload notifyPayload = notifyPayloads.get(0); - assertEquals(NOTIFY_TYPE_REKEY_SA, notifyPayload.notifyType); - assertEquals(PROTOCOL_ID_ESP, notifyPayload.protocolId); - assertEquals(mSpyCurrentChildSaRecord.getLocalSpi(), notifyPayload.spi); - } - - @Test - public void testRekeyChildLocalCreateSendsRequest() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mChildSessionStateMachine.rekeyChildSession(); - mLooper.dispatchAll(); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildLocalCreate); - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_CREATE_CHILD_SA), - eq(false), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - - // Verify outbound payload list - List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue(); - verifyOutboundCreatePayloadTypes(reqPayloadList, true /*isRekey*/); - - verifyOutboundRekeySaPayload(reqPayloadList, false /*isResp*/); - verifyOutboundRekeyNotifyPayload(reqPayloadList); - } - - private List<IkePayload> makeInboundRekeyChildPayloads( - int remoteSpi, String inboundSaHexString, boolean isLocalInitRekey) throws Exception { - List<IkePayload> inboundPayloads = new LinkedList<>(); - - IkeSaPayload saPayload = - (IkeSaPayload) - (IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_SA, true, inboundSaHexString)); - inboundPayloads.add(saPayload); - - // Build TS Payloads - IkeTrafficSelector[] initTs = - isLocalInitRekey - ? mChildSessionStateMachine.mLocalTs - : mChildSessionStateMachine.mRemoteTs; - IkeTrafficSelector[] respTs = - isLocalInitRekey - ? mChildSessionStateMachine.mRemoteTs - : mChildSessionStateMachine.mLocalTs; - inboundPayloads.add(new IkeTsPayload(true /*isInitiator*/, initTs)); - inboundPayloads.add(new IkeTsPayload(false /*isInitiator*/, respTs)); - - // Build Nonce Payloads - inboundPayloads.add(new IkeNoncePayload()); - - if (isLocalInitRekey) { - // Rekey-Create response without Notify-Rekey payload is valid. - return inboundPayloads; - } - - // Build Rekey-Notification - inboundPayloads.add( - new IkeNotifyPayload( - PROTOCOL_ID_ESP, - mSpyCurrentChildSaRecord.getRemoteSpi(), - NOTIFY_TYPE_REKEY_SA, - new byte[0])); - - return inboundPayloads; - } - - @Test - public void testRekeyChildLocalCreateValidatesResponse() throws Exception { - setupIdleStateMachine(); - setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN); - setUpSpiResource(REMOTE_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_OUT); - - // Send Rekey-Create request - mChildSessionStateMachine.rekeyChildSession(); - mLooper.dispatchAll(); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildLocalCreate); - - // Prepare "rekeyed" SA and receive Rekey response - List<IkePayload> rekeyRespPayloads = - makeInboundRekeyChildPayloads( - LOCAL_INIT_NEW_CHILD_SA_SPI_OUT, - REKEY_CHILD_RESP_SA_PAYLOAD, - true /*isLocalInitRekey*/); - when(mMockSaRecordHelper.makeChildSaRecord( - any(List.class), eq(rekeyRespPayloads), any(ChildSaRecordConfig.class))) - .thenReturn(mSpyLocalInitNewChildSaRecord); - - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyRespPayloads); - mLooper.dispatchAll(); - - // Verify state transition - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildLocalDelete); - - // Verify newly created ChildSaRecord - assertEquals( - mSpyLocalInitNewChildSaRecord, - mChildSessionStateMachine.mLocalInitNewChildSaRecord); - verify(mMockChildSessionSmCallback) - .onChildSaCreated( - eq(mSpyLocalInitNewChildSaRecord.getRemoteSpi()), - eq(mChildSessionStateMachine)); - verify(mMockChildSessionSmCallback) - .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong()); - - verify(mMockSaRecordHelper) - .makeChildSaRecord( - any(List.class), - eq(rekeyRespPayloads), - mChildSaRecordConfigCaptor.capture()); - ChildSaRecordConfig childSaRecordConfig = mChildSaRecordConfigCaptor.getValue(); - verifyChildSaRecordConfig( - childSaRecordConfig, - LOCAL_INIT_NEW_CHILD_SA_SPI_IN, - LOCAL_INIT_NEW_CHILD_SA_SPI_OUT, - true /*isLocalInit*/); - - // Verify users have been notified - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verifyNotifyUsersCreateIpSecSa(mSpyLocalInitNewChildSaRecord, true /*expectInbound*/); - verifyNotifyUsersCreateIpSecSa(mSpyLocalInitNewChildSaRecord, false /*expectInbound*/); - } - - @Test - public void testRekeyLocalCreateHandlesErrorNotifyResp() throws Exception { - setupIdleStateMachine(); - setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN); - - // Send Rekey-Create request - mChildSessionStateMachine.rekeyChildSession(); - mLooper.dispatchAll(); - - // Receive error notification in Create response - IkeNotifyPayload notifyPayload = new IkeNotifyPayload(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE); - List<IkePayload> respPayloads = new LinkedList<>(); - respPayloads.add(notifyPayload); - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads); - mLooper.dispatchAll(); - - // Verify rekey has been rescheduled and Child Session is alive - verify(mMockChildSessionSmCallback) - .scheduleRetryLocalRequest( - (ChildLocalRequest) mSpyCurrentChildSaRecord.getFutureRekeyEvent()); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - - // Verify no SPI for provisional Child was registered. - verify(mMockChildSessionSmCallback, never()) - .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine)); - } - - @Test - public void testRekeyLocalCreateHandlesRespWithMissingPayload() throws Exception { - setupIdleStateMachine(); - setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN); - reset(mMockChildSessionSmCallback); - - // Send Rekey-Create request - mChildSessionStateMachine.rekeyChildSession(); - mLooper.dispatchAll(); - - // Receive response with no SA Payload - List<IkePayload> validRekeyRespPayloads = - makeInboundRekeyChildPayloads( - LOCAL_INIT_NEW_CHILD_SA_SPI_OUT, - REKEY_CHILD_RESP_SA_PAYLOAD, - true /*isLocalInitRekey*/); - List<IkePayload> respPayloads = new LinkedList<>(); - for (IkePayload payload : validRekeyRespPayloads) { - if (IkePayload.PAYLOAD_TYPE_SA == payload.payloadType) continue; - respPayloads.add(payload); - } - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads); - mLooper.dispatchAll(); - - // Verify user was notified and state machine has quit. - verifyHandleFatalErrorAndQuit(InvalidSyntaxException.class); - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - - // Verify no SPI for provisional Child was registered. - verify(mMockChildSessionSmCallback, never()) - .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine)); - - // Verify retry was not scheduled - verify(mMockChildSessionSmCallback, never()).scheduleRetryLocalRequest(any()); - } - - @Test - public void testRekeyLocalCreateChildHandlesKeyCalculationFail() throws Exception { - // Throw exception when building ChildSaRecord - when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any())) - .thenThrow( - new GeneralSecurityException( - "testRekeyCreateChildHandlesKeyCalculationFail")); - - // Setup for rekey negotiation - setupIdleStateMachine(); - setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN); - setUpSpiResource(REMOTE_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_OUT); - reset(mMockChildSessionSmCallback); - - // Send Rekey-Create request - mChildSessionStateMachine.rekeyChildSession(); - mLooper.dispatchAll(); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildLocalCreate); - - // Receive Rekey response - List<IkePayload> rekeyRespPayloads = - makeInboundRekeyChildPayloads( - LOCAL_INIT_NEW_CHILD_SA_SPI_OUT, - REKEY_CHILD_RESP_SA_PAYLOAD, - true /*isLocalInitRekey*/); - mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyRespPayloads); - mLooper.dispatchAll(); - - // Verify user was notified and state machine has quit. - verifyHandleFatalErrorAndQuit(IkeInternalException.class); - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - - // Verify SPI for provisional Child was registered and unregistered. - verify(mMockChildSessionSmCallback) - .onChildSaCreated(LOCAL_INIT_NEW_CHILD_SA_SPI_OUT, mChildSessionStateMachine); - verify(mMockChildSessionSmCallback).onChildSaDeleted(LOCAL_INIT_NEW_CHILD_SA_SPI_OUT); - - // Verify retry was not scheduled - verify(mMockChildSessionSmCallback, never()).scheduleRetryLocalRequest(any()); - } - - @Test - public void testRekeyChildLocalDeleteSendsRequest() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildLocalDelete - mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete); - mLooper.dispatchAll(); - - // Verify outbound delete request - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildLocalDelete); - verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), false /*isResp*/); - - assertEquals(mSpyCurrentChildSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord); - assertEquals( - mSpyLocalInitNewChildSaRecord, mChildSessionStateMachine.mChildSaRecordSurviving); - } - - void verifyChildSaUpdated(ChildSaRecord oldSaRecord, ChildSaRecord newSaRecord) { - verify(mMockChildSessionSmCallback).onChildSaDeleted(oldSaRecord.getRemoteSpi()); - verify(oldSaRecord).close(); - - assertNull(mChildSessionStateMachine.mChildSaRecordSurviving); - assertEquals(newSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord); - } - - @Test - public void testRekeyChildLocalDeleteValidatesResponse() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildLocalDelete - mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete); - mLooper.dispatchAll(); - - // Test receiving Delete response - mChildSessionStateMachine.receiveResponse( - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - - // First invoked in #setupIdleStateMachine - verify(mMockChildSessionSmCallback, times(2)) - .onProcedureFinished(mChildSessionStateMachine); - - verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyLocalInitNewChildSaRecord); - - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockChildSessionCallback, never()).onClosed(); - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - } - - @Test - public void testRekeyChildLocalDeleteHandlesInvalidResp() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildLocalDelete - mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete); - mLooper.dispatchAll(); - - // Test receiving Delete response with missing Delete payload - mChildSessionStateMachine.receiveResponse( - EXCHANGE_TYPE_INFORMATIONAL, new ArrayList<IkePayload>()); - mLooper.dispatchAll(); - - // Verify rekey has finished - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyLocalInitNewChildSaRecord); - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - - // First invoked in #setupIdleStateMachine - verify(mMockChildSessionSmCallback, times(2)) - .onProcedureFinished(mChildSessionStateMachine); - } - - @Test - public void testRekeyChildRemoteCreate() throws Exception { - setupIdleStateMachine(); - - // Setup for new Child SA negotiation. - setUpSpiResource(LOCAL_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_IN); - setUpSpiResource(REMOTE_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT); - - List<IkePayload> rekeyReqPayloads = - makeInboundRekeyChildPayloads( - REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, - REKEY_CHILD_REQ_SA_PAYLOAD, - false /*isLocalInitRekey*/); - when(mMockSaRecordHelper.makeChildSaRecord( - eq(rekeyReqPayloads), any(List.class), any(ChildSaRecordConfig.class))) - .thenReturn(mSpyRemoteInitNewChildSaRecord); - - // Receive rekey Child request - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads); - mLooper.dispatchAll(); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildRemoteDelete); - - // Verify outbound rekey response - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(EXCHANGE_TYPE_CREATE_CHILD_SA), - eq(true), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - List<IkePayload> respPayloadList = mPayloadListCaptor.getValue(); - verifyOutboundCreatePayloadTypes(respPayloadList, true /*isRekey*/); - - verifyOutboundRekeySaPayload(respPayloadList, true /*isResp*/); - verifyOutboundRekeyNotifyPayload(respPayloadList); - - // Verify new Child SA - assertEquals( - mSpyRemoteInitNewChildSaRecord, - mChildSessionStateMachine.mRemoteInitNewChildSaRecord); - - verify(mMockChildSessionSmCallback) - .onChildSaCreated( - eq(mSpyRemoteInitNewChildSaRecord.getRemoteSpi()), - eq(mChildSessionStateMachine)); - verify(mMockChildSessionSmCallback) - .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong()); - - verify(mMockSaRecordHelper) - .makeChildSaRecord( - eq(rekeyReqPayloads), - any(List.class), - mChildSaRecordConfigCaptor.capture()); - ChildSaRecordConfig childSaRecordConfig = mChildSaRecordConfigCaptor.getValue(); - verifyChildSaRecordConfig( - childSaRecordConfig, - REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, - REMOTE_INIT_NEW_CHILD_SA_SPI_IN, - false /*isLocalInit*/); - - // Verify that users are notified the creation of new inbound IpSecTransform - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, true /*expectInbound*/); - } - - private void verifyOutboundErrorNotify(int exchangeType, int errorCode) { - verify(mMockChildSessionSmCallback) - .onOutboundPayloadsReady( - eq(exchangeType), - eq(true), - mPayloadListCaptor.capture(), - eq(mChildSessionStateMachine)); - List<IkePayload> respPayloadList = mPayloadListCaptor.getValue(); - - assertEquals(1, respPayloadList.size()); - IkePayload payload = respPayloadList.get(0); - assertEquals(IkePayload.PAYLOAD_TYPE_NOTIFY, payload.payloadType); - assertEquals(errorCode, ((IkeNotifyPayload) payload).notifyType); - } - - @Test - public void testRekeyChildRemoteCreateHandlesInvalidReq() throws Exception { - setupIdleStateMachine(); - - List<IkePayload> rekeyReqPayloads = - makeInboundRekeyChildPayloads( - REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, - REKEY_CHILD_UNACCEPTABLE_REQ_SA_PAYLOAD, - false /*isLocalInitRekey*/); - - // Receive rekey Child request - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads); - mLooper.dispatchAll(); - - // Verify error notification was sent and state machind was back to Idle - verifyOutboundErrorNotify(EXCHANGE_TYPE_CREATE_CHILD_SA, ERROR_TYPE_NO_PROPOSAL_CHOSEN); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - } - - @Test - public void testRekeyChildRemoteCreateSaCreationFail() throws Exception { - // Throw exception when building ChildSaRecord - when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any())) - .thenThrow( - new GeneralSecurityException("testRekeyChildRemoteCreateSaCreationFail")); - - setupIdleStateMachine(); - - List<IkePayload> rekeyReqPayloads = - makeInboundRekeyChildPayloads( - REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, - REKEY_CHILD_REQ_SA_PAYLOAD, - false /*isLocalInitRekey*/); - - // Receive rekey Child request - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads); - mLooper.dispatchAll(); - - // Verify error notification was sent and state machind was back to Idle - verifyOutboundErrorNotify(EXCHANGE_TYPE_CREATE_CHILD_SA, ERROR_TYPE_NO_PROPOSAL_CHOSEN); - - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - } - - @Test - public void testRekeyChildRemoteDelete() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildRemoteDelete - mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete); - - // Test receiving Delete request - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - // Verify outbound Delete response - verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), true /*isResp*/); - - // Verify Child SA has been updated - verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyRemoteInitNewChildSaRecord); - - // Verify procedure has been finished. #onProcedureFinished was first invoked in - // #setupIdleStateMachine - verify(mMockChildSessionSmCallback, times(2)) - .onProcedureFinished(mChildSessionStateMachine); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - - verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class)); - - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/); - verify(mMockChildSessionCallback, never()).onClosed(); - } - - @Test - public void testRekeyChildLocalDeleteWithReqForNewSa() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildLocalDelete - mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete); - mLooper.dispatchAll(); - - // Test receiving Delete new Child SA request - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyLocalInitNewChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - // Verify outbound Delete response on new Child SA - verifyOutboundDeletePayload(mSpyLocalInitNewChildSaRecord.getLocalSpi(), true /*isResp*/); - verify(mMockChildSessionSmCallback) - .onChildSaDeleted(mSpyLocalInitNewChildSaRecord.getRemoteSpi()); - verify(mSpyLocalInitNewChildSaRecord).close(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - - verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class)); - - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - verifyNotifyUserDeleteChildSa(mSpyLocalInitNewChildSaRecord); - - verify(mMockChildSessionCallback).onClosed(); - } - - @Test - public void testRekeyChildRemoteDeleteWithReqForNewSa() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildRemoteDelete - mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete); - mLooper.dispatchAll(); - - // Test receiving Delete new Child SA request - mChildSessionStateMachine.receiveRequest( - IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, - EXCHANGE_TYPE_INFORMATIONAL, - makeDeletePayloads(mSpyRemoteInitNewChildSaRecord.getRemoteSpi())); - mLooper.dispatchAll(); - - // Verify outbound Delete response on new Child SA - verifyOutboundDeletePayload(mSpyRemoteInitNewChildSaRecord.getLocalSpi(), true /*isResp*/); - verify(mMockChildSessionSmCallback) - .onChildSaDeleted(mSpyRemoteInitNewChildSaRecord.getRemoteSpi()); - verify(mSpyRemoteInitNewChildSaRecord).close(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - - verify(mSpyUserCbExecutor, times(3)).execute(any(Runnable.class)); - - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - verifyNotifyUserDeleteChildSa(mSpyRemoteInitNewChildSaRecord); - verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/); - - verify(mMockChildSessionCallback).onClosed(); - } - - @Test - public void testRekeyChildRemoteDeleteTimeout() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildRemoteDelete - mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete); - mLooper.dispatchAll(); - - mLooper.moveTimeForward(REKEY_DELETE_TIMEOUT_MS); - mLooper.dispatchAll(); - - // Verify no response sent. - verify(mMockChildSessionSmCallback, never()) - .onOutboundPayloadsReady(anyInt(), anyBoolean(), any(List.class), anyObject()); - - // Verify Child SA has been renewed - verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyRemoteInitNewChildSaRecord); - - // Verify procedure has been finished. #onProcedureFinished was first invoked in - // #setupIdleStateMachine - verify(mMockChildSessionSmCallback, times(2)) - .onProcedureFinished(mChildSessionStateMachine); - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.Idle); - - verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class)); - - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/); - - verify(mMockChildSessionCallback, never()).onClosed(); - } - - @Test - public void testRekeyChildRemoteDeleteExitAndRenter() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildRemoteDelete - mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete); - mLooper.dispatchAll(); - - // Trigger a timeout, and immediately re-enter remote-delete - mLooper.moveTimeForward(REKEY_DELETE_TIMEOUT_MS / 2 + 1); - mChildSessionStateMachine.sendMessage(ChildSessionStateMachine.TIMEOUT_REKEY_REMOTE_DELETE); - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete); - mLooper.dispatchAll(); - - // Shift time forward - mLooper.moveTimeForward(REKEY_DELETE_TIMEOUT_MS / 2 + 1); - mLooper.dispatchAll(); - - // Verify final state has not changed - timeout was not triggered. - assertTrue( - mChildSessionStateMachine.getCurrentState() - instanceof ChildSessionStateMachine.RekeyChildRemoteDelete); - - verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class)); - - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/); - - verify(mMockChildSessionCallback, never()).onClosed(); - } - - @Test - public void testCloseSessionNow() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyChildLocalDelete - mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord; - mChildSessionStateMachine.sendMessage( - CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete); - - mChildSessionStateMachine.killSession(); - mLooper.dispatchAll(); - - assertNull(mChildSessionStateMachine.getCurrentState()); - - verify(mSpyUserCbExecutor, times(3)).execute(any(Runnable.class)); - - verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord); - verifyNotifyUserDeleteChildSa(mSpyLocalInitNewChildSaRecord); - - verify(mMockChildSessionCallback).onClosed(); - } - - @Test - public void testValidateExpectKeExistCase() throws Exception { - when(mMockNegotiatedProposal.getDhGroupTransforms()) - .thenReturn(new DhGroupTransform[] {mChildDhGroupTransform}); - List<IkePayload> payloadList = new LinkedList<>(); - payloadList.add(new IkeKePayload(SaProposal.DH_GROUP_1024_BIT_MODP)); - - CreateChildSaHelper.validateKePayloads( - payloadList, true /*isResp*/, mMockNegotiatedProposal); - CreateChildSaHelper.validateKePayloads( - payloadList, false /*isResp*/, mMockNegotiatedProposal); - } - - @Test - public void testValidateExpectNoKeExistCase() throws Exception { - when(mMockNegotiatedProposal.getDhGroupTransforms()).thenReturn(new DhGroupTransform[0]); - List<IkePayload> payloadList = new LinkedList<>(); - - CreateChildSaHelper.validateKePayloads( - payloadList, true /*isResp*/, mMockNegotiatedProposal); - CreateChildSaHelper.validateKePayloads( - payloadList, false /*isResp*/, mMockNegotiatedProposal); - } - - @Test - public void testThrowWhenKeMissing() throws Exception { - when(mMockNegotiatedProposal.getDhGroupTransforms()) - .thenReturn(new DhGroupTransform[] {mChildDhGroupTransform}); - List<IkePayload> payloadList = new LinkedList<>(); - - try { - CreateChildSaHelper.validateKePayloads( - payloadList, true /*isResp*/, mMockNegotiatedProposal); - fail("Expected to fail due to the absence of KE Payload"); - } catch (InvalidSyntaxException expected) { - } - - try { - CreateChildSaHelper.validateKePayloads( - payloadList, false /*isResp*/, mMockNegotiatedProposal); - fail("Expected to fail due to the absence of KE Payload"); - } catch (InvalidKeException expected) { - } - } - - @Test - public void testThrowWhenKeHasMismatchedDhGroup() throws Exception { - when(mMockNegotiatedProposal.getDhGroupTransforms()) - .thenReturn(new DhGroupTransform[] {mChildDhGroupTransform}); - List<IkePayload> payloadList = new LinkedList<>(); - payloadList.add(new IkeKePayload(SaProposal.DH_GROUP_2048_BIT_MODP)); - - try { - CreateChildSaHelper.validateKePayloads( - payloadList, true /*isResp*/, mMockNegotiatedProposal); - fail("Expected to fail due to mismatched DH Group"); - } catch (InvalidSyntaxException expected) { - } - - try { - CreateChildSaHelper.validateKePayloads( - payloadList, false /*isResp*/, mMockNegotiatedProposal); - fail("Expected to fail due to mismatched DH Group"); - } catch (InvalidKeException expected) { - } - } - - @Test - public void testThrowForUnexpectedKe() throws Exception { - DhGroupTransform noneGroup = new DhGroupTransform(SaProposal.DH_GROUP_NONE); - when(mMockNegotiatedProposal.getDhGroupTransforms()) - .thenReturn(new DhGroupTransform[] {noneGroup}); - List<IkePayload> payloadList = new LinkedList<>(); - payloadList.add(new IkeKePayload(SaProposal.DH_GROUP_2048_BIT_MODP)); - - try { - CreateChildSaHelper.validateKePayloads( - payloadList, true /*isResp*/, mMockNegotiatedProposal); - fail("Expected to fail due to unexpected KE payload."); - } catch (InvalidSyntaxException expected) { - } - - CreateChildSaHelper.validateKePayloads( - payloadList, false /*isResp*/, mMockNegotiatedProposal); - } - - @Test - public void testHandleUnexpectedException() throws Exception { - Log spyIkeLog = TestUtils.makeSpyLogDoLogErrorForWtf(TAG); - IkeManager.setIkeLog(spyIkeLog); - - mChildSessionStateMachine.createChildSession( - null /*localAddress*/, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, SK_D); - mLooper.dispatchAll(); - - verifyHandleFatalErrorAndQuit(IkeInternalException.class); - verify(spyIkeLog).wtf(anyString(), anyString(), any(RuntimeException.class)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestSchedulerTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestSchedulerTest.java deleted file mode 100644 index 3406d014..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestSchedulerTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.IProcedureConsumer; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; - -public final class IkeLocalRequestSchedulerTest { - private IkeLocalRequestScheduler mScheduler; - - private IProcedureConsumer mMockConsumer; - private LocalRequest[] mMockRequestArray; - - private ArgumentCaptor<LocalRequest> mLocalRequestCaptor = - ArgumentCaptor.forClass(LocalRequest.class); - - @Before - public void setUp() { - mMockConsumer = mock(IProcedureConsumer.class); - mScheduler = new IkeLocalRequestScheduler(mMockConsumer); - - mMockRequestArray = new LocalRequest[10]; - for (int i = 0; i < mMockRequestArray.length; i++) { - mMockRequestArray[i] = mock(LocalRequest.class); - } - } - - @Test - public void testAddMultipleRequestProcessOnlyOne() { - for (LocalRequest r : mMockRequestArray) mScheduler.addRequest(r); - - // Verify that no procedure was preemptively pulled from the queue - verify(mMockConsumer, never()).onNewProcedureReady(any()); - - // Check that the onNewPrcedureReady called exactly once, on the first item - mScheduler.readyForNextProcedure(); - verify(mMockConsumer, times(1)).onNewProcedureReady(any()); - verify(mMockConsumer, times(1)).onNewProcedureReady(mMockRequestArray[0]); - for (int i = 1; i < mMockRequestArray.length; i++) { - verify(mMockConsumer, never()).onNewProcedureReady(mMockRequestArray[i]); - } - } - - @Test - public void testProcessOrder() { - InOrder inOrder = inOrder(mMockConsumer); - - for (LocalRequest r : mMockRequestArray) mScheduler.addRequest(r); - for (int i = 0; i < mMockRequestArray.length; i++) mScheduler.readyForNextProcedure(); - - for (LocalRequest r : mMockRequestArray) { - inOrder.verify(mMockConsumer).onNewProcedureReady(r); - } - } - - @Test - public void testAddRequestToFrontProcessOrder() { - InOrder inOrder = inOrder(mMockConsumer); - - LocalRequest[] mockHighPriorityRequestArray = new LocalRequest[10]; - for (int i = 0; i < mockHighPriorityRequestArray.length; i++) { - mockHighPriorityRequestArray[i] = mock(LocalRequest.class); - } - - for (LocalRequest r : mMockRequestArray) mScheduler.addRequest(r); - for (LocalRequest r : mockHighPriorityRequestArray) mScheduler.addRequestAtFront(r); - - for (int i = 0; i < mockHighPriorityRequestArray.length + mMockRequestArray.length; i++) { - mScheduler.readyForNextProcedure(); - } - - // Verify processing order. mockHighPriorityRequestArray is processed in reverse order - for (int i = mockHighPriorityRequestArray.length - 1; i >= 0; i--) { - inOrder.verify(mMockConsumer).onNewProcedureReady(mockHighPriorityRequestArray[i]); - } - for (LocalRequest r : mMockRequestArray) { - inOrder.verify(mMockConsumer).onNewProcedureReady(r); - } - } - - @Test - public void testDoNotProcessCanceledRequest() { - LocalRequest[] requestArray = new LocalRequest[4]; - - for (int i = 0; i < requestArray.length; i++) { - requestArray[i] = new LocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE); - mScheduler.addRequest(requestArray[i]); - } - - mScheduler.readyForNextProcedure(); - verify(mMockConsumer).onNewProcedureReady(eq(requestArray[0])); - - requestArray[1].cancel(); - mScheduler.readyForNextProcedure(); - verify(mMockConsumer, never()).onNewProcedureReady(eq(requestArray[1])); - verify(mMockConsumer).onNewProcedureReady(eq(requestArray[2])); - - mScheduler.readyForNextProcedure(); - verify(mMockConsumer).onNewProcedureReady(eq(requestArray[3])); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java deleted file mode 100644 index 94f4d622..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java +++ /dev/null @@ -1,4036 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; - -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.RETRY_INTERVAL_MS; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.SA_SOFT_LIFETIME_MS; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.TEMP_FAILURE_RETRY_TIMEOUT_MS; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA; -import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP; -import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_AUTH; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.eap.EapSessionConfig; -import android.net.ipsec.ike.ChildSaProposal; -import android.net.ipsec.ike.ChildSessionCallback; -import android.net.ipsec.ike.ChildSessionOptions; -import android.net.ipsec.ike.IkeIpv4AddrIdentification; -import android.net.ipsec.ike.IkeManager; -import android.net.ipsec.ike.IkeSaProposal; -import android.net.ipsec.ike.IkeSessionCallback; -import android.net.ipsec.ike.IkeSessionOptions; -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.TransportModeChildSessionOptions; -import android.net.ipsec.ike.exceptions.IkeException; -import android.net.ipsec.ike.exceptions.IkeInternalException; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.os.test.TestLooper; -import android.telephony.TelephonyManager; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.eap.EapAuthenticator; -import com.android.internal.net.eap.IEapCallback; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachine.IChildSessionSmCallback; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachineFactory.ChildSessionFactoryHelper; -import com.android.internal.net.ipsec.ike.ChildSessionStateMachineFactory.IChildSessionFactoryHelper; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IkeSecurityParameterIndex; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.ReceivedIkePacket; -import com.android.internal.net.ipsec.ike.SaRecord.ISaRecordHelper; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecord; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecordConfig; -import com.android.internal.net.ipsec.ike.SaRecord.SaRecordHelper; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; -import com.android.internal.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; -import com.android.internal.net.ipsec.ike.message.IkeAuthDigitalSignPayload; -import com.android.internal.net.ipsec.ike.message.IkeAuthPayload; -import com.android.internal.net.ipsec.ike.message.IkeAuthPskPayload; -import com.android.internal.net.ipsec.ike.message.IkeCertX509CertPayload; -import com.android.internal.net.ipsec.ike.message.IkeDeletePayload; -import com.android.internal.net.ipsec.ike.message.IkeEapPayload; -import com.android.internal.net.ipsec.ike.message.IkeHeader; -import com.android.internal.net.ipsec.ike.message.IkeIdPayload; -import com.android.internal.net.ipsec.ike.message.IkeInformationalPayload; -import com.android.internal.net.ipsec.ike.message.IkeKePayload; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResult; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultOk; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultPartial; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultProtectedError; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultUnprotectedError; -import com.android.internal.net.ipsec.ike.message.IkeMessage.IIkeMessageHelper; -import com.android.internal.net.ipsec.ike.message.IkeMessage.IkeMessageHelper; -import com.android.internal.net.ipsec.ike.message.IkeNoncePayload; -import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload; -import com.android.internal.net.ipsec.ike.message.IkePayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.message.IkeSkfPayload; -import com.android.internal.net.ipsec.ike.message.IkeTestUtils; -import com.android.internal.net.ipsec.ike.message.IkeTsPayload; -import com.android.internal.net.ipsec.ike.testutils.CertUtils; -import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils; -import com.android.internal.net.ipsec.ike.utils.Retransmitter; -import com.android.internal.net.ipsec.ike.utils.Retransmitter.IBackoffTimeoutCalculator; -import com.android.internal.net.utils.Log; -import com.android.internal.util.State; - -import libcore.net.InetAddressUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.invocation.InvocationOnMock; - -import java.io.IOException; -import java.net.Inet4Address; -import java.security.GeneralSecurityException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executor; - -public final class IkeSessionStateMachineTest { - private static final String TAG = "IkeSessionStateMachineTest"; - - private static final Inet4Address LOCAL_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private static final Inet4Address REMOTE_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("127.0.0.1")); - - private static final String IKE_INIT_RESP_HEX_STRING = - "5f54bf6d8b48e6e1909232b3d1edcb5c21202220000000000000014c220000300000" - + "002c010100040300000c0100000c800e008003000008030000020300000802000002" - + "00000008040000022800008800020000fe014fefed55a4229928bfa3dad1ea6ffaca" - + "abfb5f5bdd71790e99a192530e3f849d3a3d96dc6e0a7a10ff6f72a6162103ac573c" - + "acd41d08b7a034cad8f5eab09c14ced5a9e4af5692dff028f21c1119dd75226b6af6" - + "b2f009245369c9892cc5742e5c94a254ebff052470771fb2cb4f29a35d8953e18a1a" - + "6c6fbc56acc188a5290000249756112ca539f5c25abacc7ee92b73091942a9c06950" - + "f98848f1af1694c4ddff2900001c00004004c53f054b976a25d75fde72dbf1c7b6c8" - + "c9aa9ca12900001c00004005b16d79b21c1bc89ca7350f42de805be0227e2ed62b00" - + "00080000401400000014882fe56d6fd20dbc2251613b2ebe5beb"; - private static final String IKE_SA_PAYLOAD_HEX_STRING = - "220000300000002c010100040300000c0100000c800e00800300000803000002030" - + "00008020000020000000804000002"; - private static final String IKE_REKEY_SA_PAYLOAD_HEX_STRING = - "22000038000000340101080400000000000000FF0300000c0100000c800e0080030" - + "000080300000203000008020000020000000804000002"; - private static final String IKE_REKEY_UNACCEPTABLE_SA_PAYLOAD_HEX_STRING = - "22000038000000340101080400000000000000FF0300000c0100000c800e0080030" - + "00008030000020300000802000002000000080400000e"; - private static final int IKE_REKEY_SA_INITIATOR_SPI = 0xff; - private static final String KE_PAYLOAD_HEX_STRING = - "2800008800020000b4a2faf4bb54878ae21d638512ece55d9236fc50" - + "46ab6cef82220f421f3ce6361faf36564ecb6d28798a94aa" - + "d7b2b4b603ddeaaa5630adb9ece8ac37534036040610ebdd" - + "92f46bef84f0be7db860351843858f8acf87056e272377f7" - + "0c9f2d81e29c7b0ce4f291a3a72476bb0b278fd4b7b0a4c2" - + "6bbeb08214c7071376079587"; - private static final String NONCE_INIT_PAYLOAD_HEX_STRING = - "29000024c39b7f368f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c72cb4240eb5c46412"; - private static final String NONCE_RESP_PAYLOAD_HEX_STRING = - "290000249756112ca539f5c25abacc7ee92b73091942a9c06950f98848f1af1694c4ddff"; - private static final String NONCE_INIT_HEX_STRING = - "c39b7f368f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c72cb4240eb5c46412"; - private static final String NONCE_RESP_HEX_STRING = - "9756112ca539f5c25abacc7ee92b73091942a9c06950f98848f1af1694c4ddff"; - private static final String NAT_DETECTION_SOURCE_PAYLOAD_HEX_STRING = - "2900001c00004004e54f73b7d83f6beb881eab2051d8663f421d10b0"; - private static final String NAT_DETECTION_DESTINATION_PAYLOAD_HEX_STRING = - "2b00001c00004005d915368ca036004cb578ae3e3fb268509aeab190"; - private static final String FRAGMENTATION_SUPPORTED_PAYLOAD_HEX_STRING = "290000080000402e"; - private static final String DELETE_IKE_PAYLOAD_HEX_STRING = "0000000801000000"; - private static final String NOTIFY_REKEY_IKE_PAYLOAD_HEX_STRING = "2100000800004009"; - private static final String ID_PAYLOAD_INITIATOR_HEX_STRING = - "290000180200000031313233343536373839414243444546"; - private static final String ID_PAYLOAD_RESPONDER_HEX_STRING = "2700000c010000007f000001"; - private static final String PSK_AUTH_RESP_PAYLOAD_HEX_STRING = - "2100001c0200000058f36412e9b7b38df817a9f7779b7a008dacdd25"; - private static final String GENERIC_DIGITAL_SIGN_AUTH_RESP_HEX_STRING = - "300000580e0000000f300d06092a864886f70d01010b05006f76af4150d653c5d413" - + "6b9f69d905849bf075c563e6d14ccda42361ec3e7d12c72e2dece5711ea1d952f7b8e" - + "12c5d982aa4efdaeac36a02b222aa96242cc424"; - private static final String CHILD_SA_PAYLOAD_HEX_STRING = - "2c00002c0000002801030403cae7019f0300000c0100000c800e008003000008030" - + "000020000000805000000"; - private static final String TS_INIT_PAYLOAD_HEX_STRING = - "2d00001801000000070000100000ffff00000000ffffffff"; - private static final String TS_RESP_PAYLOAD_HEX_STRING = - "2900001801000000070000100000ffff000000000fffffff"; - - private static final String PSK_HEX_STRING = "6A756E69706572313233"; - - private static final String PRF_KEY_INIT_HEX_STRING = - "094787780EE466E2CB049FA327B43908BC57E485"; - private static final String PRF_KEY_RESP_HEX_STRING = - "A30E6B08BE56C0E6BFF4744143C75219299E1BEB"; - - private static final byte[] EAP_DUMMY_MSG = "EAP Message".getBytes(); - - private static final int KEY_LEN_IKE_INTE = 20; - private static final int KEY_LEN_IKE_ENCR = 16; - private static final int KEY_LEN_IKE_PRF = 20; - private static final int KEY_LEN_IKE_SKD = KEY_LEN_IKE_PRF; - - private static final int CHILD_SPI_LOCAL = 0x2ad4c0a2; - private static final int CHILD_SPI_REMOTE = 0xcae7019f; - - private static final int DUMMY_UDP_ENCAP_RESOURCE_ID = 0x3234; - private static final int UDP_ENCAP_PORT = 34567; - - private static final int EAP_SIM_SUB_ID = 1; - - private static final int PAYLOAD_TYPE_UNSUPPORTED = 127; - - private static final long RETRANSMIT_BACKOFF_TIMEOUT_MS = 5000L; - - private MockIpSecTestUtils mMockIpSecTestUtils; - private Context mContext; - private IpSecManager mIpSecManager; - private UdpEncapsulationSocket mUdpEncapSocket; - - private IkeSocket mSpyIkeSocket; - - private TestLooper mLooper; - private IkeSessionStateMachine mIkeSessionStateMachine; - - private byte[] mPsk; - - private ChildSessionOptions mChildSessionOptions; - - private Executor mSpyUserCbExecutor; - private IkeSessionCallback mMockIkeSessionCallback; - private ChildSessionCallback mMockChildSessionCallback; - - private EncryptionTransform mIkeEncryptionTransform; - private IntegrityTransform mIkeIntegrityTransform; - private PrfTransform mIkePrfTransform; - private DhGroupTransform mIkeDhGroupTransform; - - private IIkeMessageHelper mMockIkeMessageHelper; - private ISaRecordHelper mMockSaRecordHelper; - private IBackoffTimeoutCalculator mMockBackoffTimeoutCalculator; - - private ChildSessionStateMachine mMockChildSessionStateMachine; - private IChildSessionFactoryHelper mMockChildSessionFactoryHelper; - private IChildSessionSmCallback mDummyChildSmCallback; - - private IkeSaRecord mSpyCurrentIkeSaRecord; - private IkeSaRecord mSpyLocalInitIkeSaRecord; - private IkeSaRecord mSpyRemoteInitIkeSaRecord; - - private Log mSpyIkeLog; - - private int mExpectedCurrentSaLocalReqMsgId; - private int mExpectedCurrentSaRemoteReqMsgId; - - private EapSessionConfig mEapSessionConfig; - private IkeEapAuthenticatorFactory mMockEapAuthenticatorFactory; - private EapAuthenticator mMockEapAuthenticator; - - private X509Certificate mRootCertificate; - private X509Certificate mServerEndCertificate; - - private ArgumentCaptor<IkeMessage> mIkeMessageCaptor = - ArgumentCaptor.forClass(IkeMessage.class); - private ArgumentCaptor<IkeSaRecordConfig> mIkeSaRecordConfigCaptor = - ArgumentCaptor.forClass(IkeSaRecordConfig.class); - private ArgumentCaptor<IChildSessionSmCallback> mChildSessionSmCbCaptor = - ArgumentCaptor.forClass(IChildSessionSmCallback.class); - private ArgumentCaptor<List<IkePayload>> mPayloadListCaptor = - ArgumentCaptor.forClass(List.class); - - private ReceivedIkePacket makeDummyReceivedIkeInitRespPacket( - long initiatorSpi, - long responderSpi, - @IkeHeader.ExchangeType int eType, - boolean isResp, - boolean fromIkeInit, - List<Integer> payloadTypeList, - List<String> payloadHexStringList) - throws Exception { - - List<IkePayload> payloadList = - hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, isResp); - // Build a remotely generated NAT_DETECTION_SOURCE_IP payload to mock a remote node's - // network that is not behind NAT. - IkePayload sourceNatPayload = - new IkeNotifyPayload( - NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, - IkeNotifyPayload.generateNatDetectionData( - initiatorSpi, - responderSpi, - REMOTE_ADDRESS, - IkeSocket.IKE_SERVER_PORT)); - payloadList.add(sourceNatPayload); - return makeDummyUnencryptedReceivedIkePacket( - initiatorSpi, responderSpi, eType, isResp, fromIkeInit, payloadList); - } - - private ReceivedIkePacket makeDummyUnencryptedReceivedIkePacket( - long initiatorSpi, - long responderSpi, - @IkeHeader.ExchangeType int eType, - boolean isResp, - boolean fromIkeInit, - List<IkePayload> payloadList) - throws Exception { - IkeMessage dummyIkeMessage = - makeDummyIkeMessageForTest( - initiatorSpi, - responderSpi, - eType, - isResp, - fromIkeInit, - 0, - false /*isEncrypted*/, - payloadList); - - byte[] dummyIkePacketBytes = new byte[0]; - when(mMockIkeMessageHelper.decode(0, dummyIkeMessage.ikeHeader, dummyIkePacketBytes)) - .thenReturn(new DecodeResultOk(dummyIkeMessage, dummyIkePacketBytes)); - - return new ReceivedIkePacket(dummyIkeMessage.ikeHeader, dummyIkePacketBytes); - } - - private ReceivedIkePacket makeDummyEncryptedReceivedIkePacket( - IkeSaRecord ikeSaRecord, - @IkeHeader.ExchangeType int eType, - boolean isResp, - List<Integer> payloadTypeList, - List<String> payloadHexStringList) - throws Exception { - List<IkePayload> payloadList = - hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, isResp); - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - ikeSaRecord, eType, isResp, payloadList); - } - - private ReceivedIkePacket makeDummyEncryptedReceivedIkePacketWithPayloadList( - IkeSaRecord ikeSaRecord, - @IkeHeader.ExchangeType int eType, - boolean isResp, - List<IkePayload> payloadList) - throws Exception { - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - ikeSaRecord, - eType, - isResp, - isResp - ? ikeSaRecord.getLocalRequestMessageId() - : ikeSaRecord.getRemoteRequestMessageId(), - payloadList, - new byte[0] /*dummyIkePacketBytes*/); - } - - private ReceivedIkePacket makeDummyEncryptedReceivedIkePacketWithPayloadList( - IkeSaRecord ikeSaRecord, - @IkeHeader.ExchangeType int eType, - boolean isResp, - int msgId, - List<IkePayload> payloadList, - byte[] dummyIkePacketBytes) - throws Exception { - boolean fromIkeInit = !ikeSaRecord.isLocalInit; - IkeMessage dummyIkeMessage = - makeDummyIkeMessageForTest( - ikeSaRecord.getInitiatorSpi(), - ikeSaRecord.getResponderSpi(), - eType, - isResp, - fromIkeInit, - msgId, - true /*isEncyprted*/, - payloadList); - - setDecodeEncryptedPacketResult( - ikeSaRecord, - dummyIkeMessage.ikeHeader, - null /*collectedFrags*/, - new DecodeResultOk(dummyIkeMessage, dummyIkePacketBytes)); - - return new ReceivedIkePacket(dummyIkeMessage.ikeHeader, dummyIkePacketBytes); - } - - private ReceivedIkePacket makeDummyReceivedIkePacketWithInvalidSyntax( - IkeSaRecord ikeSaRecord, boolean isResp, int eType) { - return makeDummyReceivedIkePacketWithDecodingError( - ikeSaRecord, isResp, eType, new InvalidSyntaxException("IkeStateMachineTest")); - } - - private ReceivedIkePacket makeDummyReceivedIkePacketWithDecodingError( - IkeSaRecord ikeSaRecord, boolean isResp, int eType, IkeProtocolException exception) { - IkeHeader header = - makeDummyIkeHeader(ikeSaRecord, isResp, eType, IkePayload.PAYLOAD_TYPE_SK); - byte[] dummyPacket = new byte[0]; - when(mMockIkeMessageHelper.decode( - anyInt(), any(), any(), eq(ikeSaRecord), eq(header), any(), any())) - .thenReturn(new DecodeResultProtectedError(exception, dummyPacket)); - - return new ReceivedIkePacket(header, dummyPacket); - } - - private ReceivedIkePacket makeDummyReceivedIkePacketWithUnprotectedError( - IkeSaRecord ikeSaRecord, boolean isResp, int eType, IkeException exception) { - IkeHeader header = - makeDummyIkeHeader(ikeSaRecord, isResp, eType, IkePayload.PAYLOAD_TYPE_SK); - byte[] dummyPacket = new byte[0]; - when(mMockIkeMessageHelper.decode( - anyInt(), any(), any(), eq(ikeSaRecord), eq(header), any(), any())) - .thenReturn(new DecodeResultUnprotectedError(exception)); - - return new ReceivedIkePacket(header, dummyPacket); - } - - private ReceivedIkePacket makeDummyReceivedIkeFragmentPacket( - IkeSaRecord ikeSaRecord, - boolean isResp, - int eType, - IkeSkfPayload skfPayload, - int nextPayloadType, - DecodeResultPartial collectedFrags) { - IkeHeader header = - makeDummyIkeHeader(ikeSaRecord, isResp, eType, IkePayload.PAYLOAD_TYPE_SKF); - - byte[] dummyPacket = new byte[0]; - DecodeResultPartial resultFrags = - new DecodeResultPartial( - header, dummyPacket, skfPayload, nextPayloadType, collectedFrags); - setDecodeEncryptedPacketResult(ikeSaRecord, header, collectedFrags, resultFrags); - - return new ReceivedIkePacket(header, dummyPacket); - } - - private ReceivedIkePacket makeDummyReceivedLastIkeFragmentPacketOk( - IkeSaRecord ikeSaRecord, - boolean isResp, - int eType, - DecodeResultPartial collectedFrags, - List<IkePayload> payloadList, - byte[] firstFragBytes) { - IkeHeader header = - makeDummyIkeHeader(ikeSaRecord, isResp, eType, IkePayload.PAYLOAD_TYPE_SKF); - - IkeMessage completeMessage = new IkeMessage(header, payloadList); - - setDecodeEncryptedPacketResult( - ikeSaRecord, - header, - collectedFrags, - new DecodeResultOk(completeMessage, firstFragBytes)); - - return new ReceivedIkePacket(header, new byte[0] /*dummyIkePacketBytes*/); - } - - private ReceivedIkePacket makeDummyReceivedLastIkeFragmentPacketError( - IkeSaRecord ikeSaRecord, - boolean isResp, - int eType, - DecodeResultPartial collectedFrags, - IkeException exception) { - IkeHeader header = - makeDummyIkeHeader(ikeSaRecord, isResp, eType, IkePayload.PAYLOAD_TYPE_SKF); - - byte[] dummyIkePacketBytes = new byte[0]; - setDecodeEncryptedPacketResult( - ikeSaRecord, - header, - collectedFrags, - new DecodeResultProtectedError(exception, dummyIkePacketBytes)); - - return new ReceivedIkePacket(header, dummyIkePacketBytes); - } - - private IkeHeader makeDummyIkeHeader( - IkeSaRecord ikeSaRecord, boolean isResp, int eType, int firstPayloadType) { - return new IkeHeader( - ikeSaRecord.getInitiatorSpi(), - ikeSaRecord.getResponderSpi(), - firstPayloadType, - eType, - isResp, - !ikeSaRecord.isLocalInit, - isResp - ? ikeSaRecord.getLocalRequestMessageId() - : ikeSaRecord.getRemoteRequestMessageId()); - } - - private void setDecodeEncryptedPacketResult( - IkeSaRecord ikeSaRecord, - IkeHeader header, - DecodeResultPartial collectedFrags, - DecodeResult result) { - when(mMockIkeMessageHelper.decode( - anyInt(), - any(), - any(), - eq(ikeSaRecord), - eq(header), - any(), - eq(collectedFrags))) - .thenReturn(result); - } - - private IkeMessage makeDummyIkeMessageForTest( - long initSpi, - long respSpi, - @IkeHeader.ExchangeType int eType, - boolean isResp, - boolean fromikeInit, - int messageId, - boolean isEncrypted, - List<IkePayload> payloadList) - throws Exception { - int firstPayloadType = - isEncrypted ? IkePayload.PAYLOAD_TYPE_SK : IkePayload.PAYLOAD_TYPE_NO_NEXT; - - IkeHeader header = - new IkeHeader( - initSpi, respSpi, firstPayloadType, eType, isResp, fromikeInit, messageId); - - return new IkeMessage(header, payloadList); - } - - private static List<IkePayload> hexStrListToIkePayloadList( - List<Integer> payloadTypeList, List<String> payloadHexStringList, boolean isResp) - throws Exception { - List<IkePayload> payloadList = new LinkedList<>(); - for (int i = 0; i < payloadTypeList.size(); i++) { - payloadList.add( - IkeTestUtils.hexStringToIkePayload( - payloadTypeList.get(i), isResp, payloadHexStringList.get(i))); - } - return payloadList; - } - - private void verifyDecodeEncryptedMessage(IkeSaRecord record, ReceivedIkePacket rcvPacket) - throws Exception { - verify(mMockIkeMessageHelper) - .decode( - anyInt(), - any(), - any(), - eq(record), - eq(rcvPacket.ikeHeader), - eq(rcvPacket.ikePacketBytes), - eq(null)); - } - - private static IkeSaRecord makeDummyIkeSaRecord(long initSpi, long respSpi, boolean isLocalInit) - throws IOException { - Inet4Address initAddress = isLocalInit ? LOCAL_ADDRESS : REMOTE_ADDRESS; - Inet4Address respAddress = isLocalInit ? REMOTE_ADDRESS : LOCAL_ADDRESS; - - return new IkeSaRecord( - IkeSecurityParameterIndex.allocateSecurityParameterIndex(initAddress, initSpi), - IkeSecurityParameterIndex.allocateSecurityParameterIndex(respAddress, respSpi), - isLocalInit, - TestUtils.hexStringToByteArray(NONCE_INIT_HEX_STRING), - TestUtils.hexStringToByteArray(NONCE_RESP_HEX_STRING), - new byte[KEY_LEN_IKE_SKD], - new byte[KEY_LEN_IKE_INTE], - new byte[KEY_LEN_IKE_INTE], - new byte[KEY_LEN_IKE_ENCR], - new byte[KEY_LEN_IKE_ENCR], - TestUtils.hexStringToByteArray(PRF_KEY_INIT_HEX_STRING), - TestUtils.hexStringToByteArray(PRF_KEY_RESP_HEX_STRING), - new LocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE)); - } - - @Before - public void setUp() throws Exception { - mSpyIkeLog = TestUtils.makeSpyLogThrowExceptionForWtf(TAG); - IkeManager.setIkeLog(mSpyIkeLog); - - mMockIpSecTestUtils = MockIpSecTestUtils.setUpMockIpSec(); - mIpSecManager = mMockIpSecTestUtils.getIpSecManager(); - mContext = mMockIpSecTestUtils.getContext(); - mUdpEncapSocket = mIpSecManager.openUdpEncapsulationSocket(); - mEapSessionConfig = - new EapSessionConfig.Builder() - .setEapSimConfig(EAP_SIM_SUB_ID, TelephonyManager.APPTYPE_USIM) - .build(); - - mMockEapAuthenticatorFactory = mock(IkeEapAuthenticatorFactory.class); - mMockEapAuthenticator = mock(EapAuthenticator.class); - when(mMockEapAuthenticatorFactory.newEapAuthenticator(any(), any(), any(), any())) - .thenReturn(mMockEapAuthenticator); - - mRootCertificate = CertUtils.createCertFromPemFile("self-signed-ca-a.pem"); - mServerEndCertificate = CertUtils.createCertFromPemFile("end-cert-a.pem"); - - mPsk = TestUtils.hexStringToByteArray(PSK_HEX_STRING); - - mChildSessionOptions = buildChildSessionOptions(); - - mIkeEncryptionTransform = - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128); - mIkeIntegrityTransform = - new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96); - mIkePrfTransform = new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1); - mIkeDhGroupTransform = new DhGroupTransform(SaProposal.DH_GROUP_1024_BIT_MODP); - - mSpyUserCbExecutor = - spy( - (command) -> { - command.run(); - }); - - mMockIkeSessionCallback = mock(IkeSessionCallback.class); - mMockChildSessionCallback = mock(ChildSessionCallback.class); - - mLooper = new TestLooper(); - - mMockChildSessionStateMachine = mock(ChildSessionStateMachine.class); - mMockChildSessionFactoryHelper = mock(IChildSessionFactoryHelper.class); - ChildSessionStateMachineFactory.setChildSessionFactoryHelper( - mMockChildSessionFactoryHelper); - setupChildStateMachineFactory(mMockChildSessionStateMachine); - - // Inject longer retransmission timeout - mMockBackoffTimeoutCalculator = mock(IBackoffTimeoutCalculator.class); - when(mMockBackoffTimeoutCalculator.getExponentialBackoffTimeout(anyInt())) - .thenReturn(RETRANSMIT_BACKOFF_TIMEOUT_MS); - Retransmitter.setBackoffTimeoutCalculator(mMockBackoffTimeoutCalculator); - - // Setup state machine - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsPsk(mPsk)); - - mMockIkeMessageHelper = mock(IkeMessage.IIkeMessageHelper.class); - IkeMessage.setIkeMessageHelper(mMockIkeMessageHelper); - resetMockIkeMessageHelper(); - - mMockSaRecordHelper = mock(SaRecord.ISaRecordHelper.class); - SaRecord.setSaRecordHelper(mMockSaRecordHelper); - - mSpyCurrentIkeSaRecord = spy(makeDummyIkeSaRecord(11, 12, true)); - mSpyLocalInitIkeSaRecord = spy(makeDummyIkeSaRecord(21, 22, true)); - mSpyRemoteInitIkeSaRecord = spy(makeDummyIkeSaRecord(31, 32, false)); - - mExpectedCurrentSaLocalReqMsgId = 0; - mExpectedCurrentSaRemoteReqMsgId = 0; - } - - @After - public void tearDown() throws Exception { - mIkeSessionStateMachine.quit(); - mIkeSessionStateMachine.setDbg(false); - mUdpEncapSocket.close(); - - mSpyCurrentIkeSaRecord.close(); - mSpyLocalInitIkeSaRecord.close(); - mSpyRemoteInitIkeSaRecord.close(); - - IkeManager.resetIkeLog(); - Retransmitter.resetBackoffTimeoutCalculator(); - IkeMessage.setIkeMessageHelper(new IkeMessageHelper()); - SaRecord.setSaRecordHelper(new SaRecordHelper()); - ChildSessionStateMachineFactory.setChildSessionFactoryHelper( - new ChildSessionFactoryHelper()); - } - - private IkeSessionStateMachine makeAndStartIkeSession(IkeSessionOptions ikeOptions) - throws Exception { - IkeSessionStateMachine ikeSession = - new IkeSessionStateMachine( - mLooper.getLooper(), - mContext, - mIpSecManager, - ikeOptions, - mChildSessionOptions, - mSpyUserCbExecutor, - mMockIkeSessionCallback, - mMockChildSessionCallback, - mMockEapAuthenticatorFactory); - ikeSession.setDbg(true); - - mLooper.dispatchAll(); - ikeSession.mLocalAddress = LOCAL_ADDRESS; - - mSpyIkeSocket = spy(IkeSocket.getIkeSocket(mUdpEncapSocket, ikeSession)); - doNothing().when(mSpyIkeSocket).sendIkePacket(any(), any()); - ikeSession.mIkeSocket = mSpyIkeSocket; - - return ikeSession; - } - - public static IkeSaProposal buildSaProposal() throws Exception { - return new IkeSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) - .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) - .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) - .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) - .build(); - } - - private IkeSessionOptions.Builder buildIkeSessionOptionsCommon() throws Exception { - return new IkeSessionOptions.Builder() - .setServerAddress(REMOTE_ADDRESS) - .setUdpEncapsulationSocket(mUdpEncapSocket) - .addSaProposal(buildSaProposal()) - .setLocalIdentification(new IkeIpv4AddrIdentification((Inet4Address) LOCAL_ADDRESS)) - .setRemoteIdentification( - new IkeIpv4AddrIdentification((Inet4Address) REMOTE_ADDRESS)); - } - - private IkeSessionOptions buildIkeSessionOptionsPsk(byte[] psk) throws Exception { - return buildIkeSessionOptionsCommon().setAuthPsk(psk).build(); - } - - private IkeSessionOptions buildIkeSessionOptionsEap() throws Exception { - return buildIkeSessionOptionsCommon() - .setAuthEap(mRootCertificate, mEapSessionConfig) - .build(); - } - - private ChildSessionOptions buildChildSessionOptions() throws Exception { - ChildSaProposal saProposal = - new ChildSaProposal.Builder() - .addEncryptionAlgorithm( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) - .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) - .build(); - - return new TransportModeChildSessionOptions.Builder().addSaProposal(saProposal).build(); - } - - private ReceivedIkePacket makeIkeInitResponse() throws Exception { - // TODO: Build real IKE INIT response when IKE INIT response validation is implemented. - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_SA); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_KE); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NONCE); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY); - - payloadHexStringList.add(IKE_SA_PAYLOAD_HEX_STRING); - payloadHexStringList.add(KE_PAYLOAD_HEX_STRING); - payloadHexStringList.add(NONCE_RESP_PAYLOAD_HEX_STRING); - payloadHexStringList.add(NAT_DETECTION_SOURCE_PAYLOAD_HEX_STRING); - payloadHexStringList.add(NAT_DETECTION_DESTINATION_PAYLOAD_HEX_STRING); - payloadHexStringList.add(FRAGMENTATION_SUPPORTED_PAYLOAD_HEX_STRING); - - // In each test assign different IKE responder SPI in IKE INIT response to avoid remote SPI - // collision during response validation. - // STOPSHIP: b/131617794 allow #mockIkeSetup to be independent in each test after we can - // support IkeSession cleanup. - return makeDummyReceivedIkeInitRespPacket( - 1L /*initiator SPI*/, - 2L /*responder SPI*/, - IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, - true /*isResp*/, - false /*fromIkeInit*/, - payloadTypeList, - payloadHexStringList); - } - - private List<IkePayload> getIkeAuthPayloadListWithChildPayloads( - List<IkePayload> authRelatedPayloads) throws Exception { - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_SA); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_TS_INITIATOR); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_TS_RESPONDER); - - payloadHexStringList.add(CHILD_SA_PAYLOAD_HEX_STRING); - payloadHexStringList.add(TS_INIT_PAYLOAD_HEX_STRING); - payloadHexStringList.add(TS_RESP_PAYLOAD_HEX_STRING); - - List<IkePayload> payloadList = - hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, true /*isResp*/); - payloadList.addAll(authRelatedPayloads); - - return payloadList; - } - - private ReceivedIkePacket makeIkeAuthRespWithChildPayloads(List<IkePayload> authRelatedPayloads) - throws Exception { - List<IkePayload> payloadList = getIkeAuthPayloadListWithChildPayloads(authRelatedPayloads); - - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - true /*isResp*/, - payloadList); - } - - private ReceivedIkePacket makeIkeAuthRespWithoutChildPayloads( - List<IkePayload> authRelatedPayloads) throws Exception { - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - true /*isResp*/, - authRelatedPayloads); - } - - private ReceivedIkePacket makeCreateChildCreateMessage(boolean isResp) throws Exception { - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - isResp, - makeCreateChildPayloadList(isResp)); - } - - private ReceivedIkePacket makeRekeyChildCreateMessage(boolean isResp, int spi) - throws Exception { - IkeNotifyPayload rekeyPayload = - new IkeNotifyPayload( - IkePayload.PROTOCOL_ID_ESP, - spi, - IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA, - new byte[0]); - - List<IkePayload> payloadList = makeCreateChildPayloadList(isResp); - payloadList.add(rekeyPayload); - - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - isResp, - payloadList); - } - - private List<IkePayload> makeCreateChildPayloadList(boolean isResp) throws Exception { - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_SA); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NONCE); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_TS_INITIATOR); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_TS_RESPONDER); - - payloadHexStringList.add(CHILD_SA_PAYLOAD_HEX_STRING); - payloadHexStringList.add(NONCE_RESP_PAYLOAD_HEX_STRING); - payloadHexStringList.add(TS_INIT_PAYLOAD_HEX_STRING); - payloadHexStringList.add(TS_RESP_PAYLOAD_HEX_STRING); - - return hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, isResp); - } - - private ReceivedIkePacket makeDeleteChildPacket(IkeDeletePayload[] payloads, boolean isResp) - throws Exception { - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, - isResp, - Arrays.asList(payloads)); - } - - private ReceivedIkePacket makeRekeyIkeResponse() throws Exception { - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_SA); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_KE); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NONCE); - - payloadHexStringList.add(IKE_REKEY_SA_PAYLOAD_HEX_STRING); - payloadHexStringList.add(KE_PAYLOAD_HEX_STRING); - payloadHexStringList.add(NONCE_RESP_PAYLOAD_HEX_STRING); - - return makeDummyEncryptedReceivedIkePacket( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - true /*isResp*/, - payloadTypeList, - payloadHexStringList); - } - - private ReceivedIkePacket makeDeleteIkeResponse(IkeSaRecord ikeSaRecord) throws Exception { - return makeDummyEncryptedReceivedIkePacket( - ikeSaRecord, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, - true /*isResp*/, - new LinkedList<>(), - new LinkedList<>()); - } - - private ReceivedIkePacket makeDpdIkeRequest(IkeSaRecord saRecord) throws Exception { - return makeDummyEncryptedReceivedIkePacket( - saRecord, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, - false /*isResp*/, - new LinkedList<>(), - new LinkedList<>()); - } - - private ReceivedIkePacket makeDpdIkeRequest(int msgId, byte[] dummyIkePacketBytes) - throws Exception { - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - EXCHANGE_TYPE_INFORMATIONAL, - false /*isResp*/, - msgId, - new LinkedList<>(), - dummyIkePacketBytes); - } - - private ReceivedIkePacket makeRekeyIkeRequest() throws Exception { - IkeSaPayload saPayload = - (IkeSaPayload) - IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_SA, - false /*isResp*/, - IKE_REKEY_SA_PAYLOAD_HEX_STRING); - return makeRekeyIkeRequest(saPayload); - } - - private ReceivedIkePacket makeRekeyIkeRequestWithUnacceptableProposal() throws Exception { - IkeSaPayload saPayload = - (IkeSaPayload) - IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_SA, - false /*isResp*/, - IKE_REKEY_UNACCEPTABLE_SA_PAYLOAD_HEX_STRING); - return makeRekeyIkeRequest(saPayload); - } - - private ReceivedIkePacket makeRekeyIkeRequest(IkeSaPayload saPayload) throws Exception { - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_KE); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NONCE); - - payloadHexStringList.add(KE_PAYLOAD_HEX_STRING); - payloadHexStringList.add(NONCE_INIT_PAYLOAD_HEX_STRING); - - List<IkePayload> payloadList = - hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, false /*isResp*/); - payloadList.add(saPayload); - - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - false /*isResp*/, - payloadList); - } - - private ReceivedIkePacket makeDeleteIkeRequest(IkeSaRecord saRecord) throws Exception { - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_DELETE); - - payloadHexStringList.add(DELETE_IKE_PAYLOAD_HEX_STRING); - - return makeDummyEncryptedReceivedIkePacket( - saRecord, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, - false /*isResp*/, - payloadTypeList, - payloadHexStringList); - } - - private ReceivedIkePacket makeResponseWithErrorNotify(IkeNotifyPayload notify) - throws Exception { - List<IkePayload> payloads = new LinkedList<>(); - payloads.add(notify); - return makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, EXCHANGE_TYPE_INFORMATIONAL, true /*isResp*/, payloads); - } - - private static boolean isIkePayloadExist( - List<IkePayload> payloadList, @IkePayload.PayloadType int payloadType) { - for (IkePayload payload : payloadList) { - if (payload.payloadType == payloadType) return true; - } - return false; - } - - private static boolean isNotifyExist( - List<IkePayload> payloadList, @IkeNotifyPayload.NotifyType int notifyType) { - for (IkeNotifyPayload notify : - IkePayload.getPayloadListForTypeInProvidedList( - PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, payloadList)) { - if (notify.notifyType == notifyType) return true; - } - return false; - } - - private void verifyIncrementLocaReqMsgId() { - assertEquals( - ++mExpectedCurrentSaLocalReqMsgId, - mSpyCurrentIkeSaRecord.getLocalRequestMessageId()); - } - - private void verifyIncrementRemoteReqMsgId() { - assertEquals( - ++mExpectedCurrentSaRemoteReqMsgId, - mSpyCurrentIkeSaRecord.getRemoteRequestMessageId()); - } - - private void verifyRetransmissionStarted() { - assertTrue( - mIkeSessionStateMachine - .getHandler() - .hasMessages(IkeSessionStateMachine.CMD_RETRANSMIT)); - } - - private void verifyRetransmissionStopped() { - assertFalse( - mIkeSessionStateMachine - .getHandler() - .hasMessages(IkeSessionStateMachine.CMD_RETRANSMIT)); - } - - private IkeMessage verifyEncryptAndEncodeAndGetMessage(IkeSaRecord ikeSaRecord) { - verify(mMockIkeMessageHelper) - .encryptAndEncode( - anyObject(), - anyObject(), - eq(ikeSaRecord), - mIkeMessageCaptor.capture(), - anyBoolean(), - anyInt()); - return mIkeMessageCaptor.getValue(); - } - - private void verifyEncryptAndEncodeNeverCalled(IkeSaRecord ikeSaRecord) { - verify(mMockIkeMessageHelper, never()) - .encryptAndEncode( - anyObject(), - anyObject(), - eq(ikeSaRecord), - any(IkeMessage.class), - anyBoolean(), - anyInt()); - } - - private void verifyEncryptAndEncodeNeverCalled() { - verify(mMockIkeMessageHelper, never()) - .encryptAndEncode( - anyObject(), - anyObject(), - any(IkeSaRecord.class), - any(IkeMessage.class), - anyBoolean(), - anyInt()); - } - - private void resetMockIkeMessageHelper() { - reset(mMockIkeMessageHelper); - when(mMockIkeMessageHelper.encode(any())).thenReturn(new byte[0]); - when(mMockIkeMessageHelper.encryptAndEncode( - any(), any(), any(), any(), anyBoolean(), anyInt())) - .thenReturn(new byte[1][0]); - } - - @Test - public void testQuit() { - mIkeSessionStateMachine.quit(); - mLooper.dispatchAll(); - - verify(mSpyIkeSocket).releaseReference(eq(mIkeSessionStateMachine)); - verify(mSpyIkeSocket).close(); - } - - @Test - public void testAllocateIkeSpi() throws Exception { - // Test randomness. - IkeSecurityParameterIndex ikeSpiOne = - IkeSecurityParameterIndex.allocateSecurityParameterIndex(LOCAL_ADDRESS); - IkeSecurityParameterIndex ikeSpiTwo = - IkeSecurityParameterIndex.allocateSecurityParameterIndex(LOCAL_ADDRESS); - - assertNotEquals(ikeSpiOne.getSpi(), ikeSpiTwo.getSpi()); - ikeSpiTwo.close(); - - // Test duplicate SPIs. - long spiValue = ikeSpiOne.getSpi(); - try { - IkeSecurityParameterIndex.allocateSecurityParameterIndex(LOCAL_ADDRESS, spiValue); - fail("Expected to fail because duplicate SPI was assigned to the same address."); - } catch (IOException expected) { - - } - - ikeSpiOne.close(); - IkeSecurityParameterIndex ikeSpiThree = - IkeSecurityParameterIndex.allocateSecurityParameterIndex(LOCAL_ADDRESS, spiValue); - ikeSpiThree.close(); - } - - private void setupFirstIkeSa() throws Exception { - // Inject IkeSaRecord and release IKE SPI resource since we will lose their references - // later. - when(mMockSaRecordHelper.makeFirstIkeSaRecord(any(), any(), any())) - .thenAnswer( - (invocation) -> { - captureAndReleaseIkeSpiResource(invocation, 2); - return mSpyCurrentIkeSaRecord; - }); - } - - private void setupRekeyedIkeSa(IkeSaRecord rekeySaRecord) throws Exception { - // Inject IkeSaRecord and release IKE SPI resource since we will lose their references - // later. - when(mMockSaRecordHelper.makeRekeyedIkeSaRecord( - eq(mSpyCurrentIkeSaRecord), any(), any(), any(), any())) - .thenAnswer( - (invocation) -> { - captureAndReleaseIkeSpiResource(invocation, 4); - return rekeySaRecord; - }); - } - - private void throwExceptionWhenMakeRekeyIkeSa(Exception exception) throws Exception { - // Inject IkeSaRecord and release IKE SPI resource since we will lose their references - // later. - when(mMockSaRecordHelper.makeRekeyedIkeSaRecord( - eq(mSpyCurrentIkeSaRecord), any(), any(), any(), any())) - .thenAnswer( - (invocation) -> { - captureAndReleaseIkeSpiResource(invocation, 4); - throw exception; - }); - } - - private void captureAndReleaseIkeSpiResource(InvocationOnMock invocation, int ikeConfigIndex) { - IkeSaRecordConfig config = (IkeSaRecordConfig) invocation.getArguments()[ikeConfigIndex]; - config.initSpi.close(); - config.respSpi.close(); - } - - @Test - public void testCreateIkeLocalIkeInit() throws Exception { - setupFirstIkeSa(); - - // Send IKE INIT request - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Receive IKE INIT response - ReceivedIkePacket dummyReceivedIkePacket = makeIkeInitResponse(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyReceivedIkePacket); - mLooper.dispatchAll(); - verifyIncrementLocaReqMsgId(); - - // Validate outbound IKE INIT request - verify(mMockIkeMessageHelper, times(2)).encode(mIkeMessageCaptor.capture()); - IkeMessage ikeInitReqMessage = mIkeMessageCaptor.getValue(); - - IkeHeader ikeHeader = ikeInitReqMessage.ikeHeader; - assertEquals(IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, ikeHeader.exchangeType); - assertFalse(ikeHeader.isResponseMsg); - assertTrue(ikeHeader.fromIkeInitiator); - - List<IkePayload> payloadList = ikeInitReqMessage.ikePayloadList; - assertTrue(isIkePayloadExist(payloadList, IkePayload.PAYLOAD_TYPE_SA)); - assertTrue(isIkePayloadExist(payloadList, IkePayload.PAYLOAD_TYPE_KE)); - assertTrue(isIkePayloadExist(payloadList, IkePayload.PAYLOAD_TYPE_NONCE)); - assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP)); - assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP)); - assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED)); - - verify(mSpyIkeSocket) - .registerIke(eq(mSpyCurrentIkeSaRecord.getLocalSpi()), eq(mIkeSessionStateMachine)); - - verify(mMockIkeMessageHelper) - .decode(0, dummyReceivedIkePacket.ikeHeader, dummyReceivedIkePacket.ikePacketBytes); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - - // Validate negotiated SA proposal. - IkeSaProposal negotiatedProposal = mIkeSessionStateMachine.mSaProposal; - assertNotNull(negotiatedProposal); - - assertEquals( - new EncryptionTransform[] {mIkeEncryptionTransform}, - negotiatedProposal.getEncryptionTransforms()); - assertEquals( - new IntegrityTransform[] {mIkeIntegrityTransform}, - negotiatedProposal.getIntegrityTransforms()); - assertEquals(new PrfTransform[] {mIkePrfTransform}, negotiatedProposal.getPrfTransforms()); - - // Validate current IkeSaRecord. - verify(mMockSaRecordHelper) - .makeFirstIkeSaRecord( - any(IkeMessage.class), - any(IkeMessage.class), - mIkeSaRecordConfigCaptor.capture()); - - IkeSaRecordConfig ikeSaRecordConfig = mIkeSaRecordConfigCaptor.getValue(); - assertEquals(KEY_LEN_IKE_PRF, ikeSaRecordConfig.prf.getKeyLength()); - assertEquals(KEY_LEN_IKE_INTE, ikeSaRecordConfig.integrityKeyLength); - assertEquals(KEY_LEN_IKE_ENCR, ikeSaRecordConfig.encryptionKeyLength); - assertEquals(CMD_LOCAL_REQUEST_REKEY_IKE, ikeSaRecordConfig.futureRekeyEvent.procedureType); - - // Validate NAT detection - assertTrue(mIkeSessionStateMachine.mIsLocalBehindNat); - assertFalse(mIkeSessionStateMachine.mIsRemoteBehindNat); - - // Validate fragmentation support negotiation - assertTrue(mIkeSessionStateMachine.mSupportFragment); - } - - private void setIkeInitResults() throws Exception { - mIkeSessionStateMachine.mIkeCipher = mock(IkeCipher.class); - mIkeSessionStateMachine.mIkeIntegrity = mock(IkeMacIntegrity.class); - mIkeSessionStateMachine.mIkePrf = mock(IkeMacPrf.class); - mIkeSessionStateMachine.mSaProposal = buildSaProposal(); - mIkeSessionStateMachine.mCurrentIkeSaRecord = mSpyCurrentIkeSaRecord; - mIkeSessionStateMachine.mLocalAddress = LOCAL_ADDRESS; - mIkeSessionStateMachine.mIsLocalBehindNat = true; - mIkeSessionStateMachine.mIsRemoteBehindNat = false; - mIkeSessionStateMachine.mSupportFragment = true; - mIkeSessionStateMachine.addIkeSaRecord(mSpyCurrentIkeSaRecord); - } - - /** Initializes the mIkeSessionStateMachine in the IDLE state. */ - private void setupIdleStateMachine() throws Exception { - setIkeInitResults(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); - mLooper.dispatchAll(); - - mDummyChildSmCallback = - createChildAndGetChildSessionSmCallback( - mMockChildSessionStateMachine, CHILD_SPI_REMOTE, mMockChildSessionCallback); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - private void mockIkeInitAndTransitionToIkeAuth(State authState) throws Exception { - setIkeInitResults(); - - // Need to create a real IkeMacPrf instance for authentication because we cannot inject a - // method stub for IkeMacPrf#signBytes. IkeMacPrf#signBytes is inheritted from a package - // protected class IkePrf. We don't have the visibility to mock it. - mIkeSessionStateMachine.mIkePrf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - - mIkeSessionStateMachine.mIkeInitRequestBytes = new byte[0]; - mIkeSessionStateMachine.mIkeInitResponseBytes = new byte[0]; - mIkeSessionStateMachine.mIkeInitNoncePayload = new IkeNoncePayload(); - mIkeSessionStateMachine.mIkeRespNoncePayload = new IkeNoncePayload(); - - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_FORCE_TRANSITION, authState); - mLooper.dispatchAll(); - } - - private void setupChildStateMachineFactory(ChildSessionStateMachine child) { - // After state machine start, add to the callback->statemachine map - when(mMockChildSessionFactoryHelper.makeChildSessionStateMachine( - eq(mLooper.getLooper()), - eq(mContext), - eq(mChildSessionOptions), - eq(mSpyUserCbExecutor), - any(ChildSessionCallback.class), - any(IChildSessionSmCallback.class))) - .thenReturn(child); - } - - /** - * Utility to register a new callback -> state machine mapping. - * - * <p>Must be used if IkeSessionStateMachine.openChildSession() is not called, but commands - * injected instead. - * - * @param callback The callback to be used for the mapping - * @param sm The ChildSessionStateMachine instance to be used. - */ - private void registerChildStateMachine( - ChildSessionCallback callback, ChildSessionStateMachine sm) { - setupChildStateMachineFactory(sm); - mIkeSessionStateMachine.registerChildSessionCallback( - mChildSessionOptions, callback, false /*isFirstChild*/); - } - - @Test - public void testCreateAdditionalChild() throws Exception { - setupIdleStateMachine(); - - ChildSessionCallback childCallback = mock(ChildSessionCallback.class); - ChildSessionStateMachine childStateMachine = mock(ChildSessionStateMachine.class); - registerChildStateMachine(childCallback, childStateMachine); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new ChildLocalRequest( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_CHILD, - childCallback, - mChildSessionOptions)); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verify(childStateMachine) - .createChildSession( - eq(LOCAL_ADDRESS), - eq(REMOTE_ADDRESS), - any(), // udpEncapSocket - eq(mIkeSessionStateMachine.mIkePrf), - any()); // sk_d - - // Once for initial child, a second time for the additional child. - verify(mMockChildSessionFactoryHelper) - .makeChildSessionStateMachine( - eq(mLooper.getLooper()), - eq(mContext), - eq(mChildSessionOptions), - eq(mSpyUserCbExecutor), - eq(childCallback), - mChildSessionSmCbCaptor.capture()); - IChildSessionSmCallback cb = mChildSessionSmCbCaptor.getValue(); - - // Mocking sending request - cb.onOutboundPayloadsReady( - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - false /*isResp*/, - new LinkedList<>(), - childStateMachine); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - IkeMessage createChildRequest = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = createChildRequest.ikeHeader; - assertEquals(IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, ikeHeader.exchangeType); - assertFalse(ikeHeader.isResponseMsg); - assertTrue(ikeHeader.fromIkeInitiator); - assertEquals(mSpyCurrentIkeSaRecord.getLocalRequestMessageId(), ikeHeader.messageId); - assertTrue(createChildRequest.ikePayloadList.isEmpty()); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - - // Mocking receiving response - ReceivedIkePacket dummyCreateChildResp = makeCreateChildCreateMessage(true /*isResp*/); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyCreateChildResp); - mLooper.dispatchAll(); - - verifyIncrementLocaReqMsgId(); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyCreateChildResp); - - verify(childStateMachine) - .receiveResponse( - eq(IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA), mPayloadListCaptor.capture()); - - List<IkePayload> childRespList = mPayloadListCaptor.getValue(); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_SA)); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_TS_INITIATOR)); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_TS_RESPONDER)); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_NONCE)); - - // Mock finishing procedure - cb.onProcedureFinished(childStateMachine); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - verifyRetransmissionStopped(); - } - - @Test - public void testTriggerDeleteChildLocal() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new ChildLocalRequest( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_CHILD, - mMockChildSessionCallback, - null /*childOptions*/)); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verify(mMockChildSessionStateMachine).deleteChildSession(); - } - - @Test - public void testHandleDeleteChildBeforeCreation() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new ChildLocalRequest( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_CHILD, - mock(ChildSessionCallback.class), - null /*childOptions*/)); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testTriggerRekeyChildLocal() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new ChildLocalRequest( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD, - mMockChildSessionCallback, - null /*childOptions*/)); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verify(mMockChildSessionStateMachine).rekeyChildSession(); - } - - @Test - public void testScheduleAndTriggerRekeyChildLocal() throws Exception { - setupIdleStateMachine(); - long dummyRekeyTimeout = 10000L; - - ChildLocalRequest rekeyRequest = - new ChildLocalRequest( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD, - mMockChildSessionCallback, - null /*childOptions*/); - mDummyChildSmCallback.scheduleLocalRequest(rekeyRequest, dummyRekeyTimeout); - - mLooper.moveTimeForward(dummyRekeyTimeout); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verify(mMockChildSessionStateMachine).rekeyChildSession(); - } - - private IChildSessionSmCallback createChildAndGetChildSessionSmCallback( - ChildSessionStateMachine child, int remoteSpi) throws Exception { - return createChildAndGetChildSessionSmCallback( - child, remoteSpi, mock(ChildSessionCallback.class)); - } - - private IChildSessionSmCallback createChildAndGetChildSessionSmCallback( - ChildSessionStateMachine child, int remoteSpi, ChildSessionCallback childCallback) - throws Exception { - registerChildStateMachine(childCallback, child); - - IChildSessionSmCallback cb = mIkeSessionStateMachine.new ChildSessionSmCallback(); - cb.onChildSaCreated(remoteSpi, child); - mLooper.dispatchAll(); - - return cb; - } - - private void transitionToChildProcedureOngoing() { - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mChildProcedureOngoing); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - } - - private void verifyChildReceiveDeleteRequest( - ChildSessionStateMachine child, IkeDeletePayload[] expectedDelPayloads) { - verify(child) - .receiveRequest( - eq(IKE_EXCHANGE_SUBTYPE_DELETE_CHILD), - eq(EXCHANGE_TYPE_INFORMATIONAL), - mPayloadListCaptor.capture()); - List<IkePayload> reqPayloads = mPayloadListCaptor.getValue(); - - int numExpectedDelPayloads = expectedDelPayloads.length; - assertEquals(numExpectedDelPayloads, reqPayloads.size()); - - for (int i = 0; i < numExpectedDelPayloads; i++) { - assertEquals(expectedDelPayloads[i], (IkeDeletePayload) reqPayloads.get(i)); - } - } - - private void outboundDeleteChildPayloadsReady( - IChildSessionSmCallback childSmCb, - IkeDeletePayload delPayload, - boolean isResp, - ChildSessionStateMachine child) { - List<IkePayload> outPayloadList = new LinkedList<>(); - outPayloadList.add(delPayload); - childSmCb.onOutboundPayloadsReady( - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, isResp, outPayloadList, child); - mLooper.dispatchAll(); - } - - private List<IkePayload> verifyOutInfoMsgHeaderAndGetPayloads(boolean isResp) { - IkeMessage deleteChildMessage = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = deleteChildMessage.ikeHeader; - assertEquals(mSpyCurrentIkeSaRecord.getInitiatorSpi(), ikeHeader.ikeInitiatorSpi); - assertEquals(mSpyCurrentIkeSaRecord.getResponderSpi(), ikeHeader.ikeResponderSpi); - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - assertEquals(isResp, ikeHeader.isResponseMsg); - - return deleteChildMessage.ikePayloadList; - } - - @Test - public void testDeferChildRequestToChildProcedureOngoing() throws Exception { - setupIdleStateMachine(); - - IkeDeletePayload[] inboundDelPayloads = - new IkeDeletePayload[] {new IkeDeletePayload(new int[] {CHILD_SPI_REMOTE})}; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteChildPacket(inboundDelPayloads, false /*isResp*/)); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verifyChildReceiveDeleteRequest(mMockChildSessionStateMachine, inboundDelPayloads); - } - - @Test - public void testRemoteDeleteOneChild() throws Exception { - setupIdleStateMachine(); - transitionToChildProcedureOngoing(); - - // Receive Delete Child Request - IkeDeletePayload[] inboundDelPayloads = - new IkeDeletePayload[] {new IkeDeletePayload(new int[] {CHILD_SPI_REMOTE})}; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteChildPacket(inboundDelPayloads, false /*isResp*/)); - mLooper.dispatchAll(); - - // Verify received payloads - verifyChildReceiveDeleteRequest(mMockChildSessionStateMachine, inboundDelPayloads); - - // Outbound payload list ready - IkeDeletePayload outDelPayload = new IkeDeletePayload(new int[] {CHILD_SPI_LOCAL}); - outboundDeleteChildPayloadsReady( - mDummyChildSmCallback, - outDelPayload, - true /*isResp*/, - mMockChildSessionStateMachine); - - // Verify outbound response - List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, payloadList.size()); - assertEquals(outDelPayload, ((IkeDeletePayload) payloadList.get(0))); - } - - @Test - public void testRemoteDeleteMultipleChildSession() throws Exception { - ChildSessionStateMachine childOne = mock(ChildSessionStateMachine.class); - int childOneRemoteSpi = 11; - int childOneLocalSpi = 12; - - ChildSessionStateMachine childTwo = mock(ChildSessionStateMachine.class); - int childTwoRemoteSpi = 21; - int childTwoLocalSpi = 22; - - setupIdleStateMachine(); - IChildSessionSmCallback childSmCbOne = - createChildAndGetChildSessionSmCallback(childOne, childOneRemoteSpi); - IChildSessionSmCallback childSmCbTwo = - createChildAndGetChildSessionSmCallback(childTwo, childTwoRemoteSpi); - - transitionToChildProcedureOngoing(); - - // Receive Delete Child Request - IkeDeletePayload[] inboundDelPayloads = - new IkeDeletePayload[] { - new IkeDeletePayload(new int[] {childOneRemoteSpi, childTwoRemoteSpi}) - }; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteChildPacket(inboundDelPayloads, false /*isResp*/)); - mLooper.dispatchAll(); - - // Verify received payloads - verifyChildReceiveDeleteRequest(childOne, inboundDelPayloads); - verifyChildReceiveDeleteRequest(childTwo, inboundDelPayloads); - - // childOne outbound payload list ready - IkeDeletePayload outDelPayloadOne = new IkeDeletePayload(new int[] {childOneLocalSpi}); - outboundDeleteChildPayloadsReady(childSmCbOne, outDelPayloadOne, true /*isResp*/, childOne); - mLooper.dispatchAll(); - - // Verify that no response is sent - verifyEncryptAndEncodeNeverCalled(mSpyCurrentIkeSaRecord); - - // childTwo outbound payload list ready - IkeDeletePayload outDelPayloadTwo = new IkeDeletePayload(new int[] {childTwoLocalSpi}); - outboundDeleteChildPayloadsReady(childSmCbTwo, outDelPayloadTwo, true /*isResp*/, childTwo); - mLooper.dispatchAll(); - - // Verify outbound response - List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(2, payloadList.size()); - assertEquals(outDelPayloadOne, ((IkeDeletePayload) payloadList.get(0))); - assertEquals(outDelPayloadTwo, ((IkeDeletePayload) payloadList.get(1))); - } - - @Test - public void testRemoteDeleteMultipleChildSaInSameSession() throws Exception { - int newChildRemoteSpi = 21; - int newChildLocalSpi = 22; - - setupIdleStateMachine(); - mDummyChildSmCallback.onChildSaCreated(newChildRemoteSpi, mMockChildSessionStateMachine); - - transitionToChildProcedureOngoing(); - - // Receive Delete Child Request - IkeDeletePayload[] inboundDelPayloads = - new IkeDeletePayload[] { - new IkeDeletePayload(new int[] {CHILD_SPI_REMOTE}), - new IkeDeletePayload(new int[] {newChildRemoteSpi}) - }; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteChildPacket(inboundDelPayloads, false /*isResp*/)); - mLooper.dispatchAll(); - - // Verify received payloads - verifyChildReceiveDeleteRequest(mMockChildSessionStateMachine, inboundDelPayloads); - - // child outbound payload list ready - IkeDeletePayload outDelPayload = - new IkeDeletePayload(new int[] {CHILD_SPI_LOCAL, newChildLocalSpi}); - outboundDeleteChildPayloadsReady( - mDummyChildSmCallback, - outDelPayload, - true /*isResp*/, - mMockChildSessionStateMachine); - mLooper.dispatchAll(); - - // Verify outbound response - List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, payloadList.size()); - assertEquals(outDelPayload, ((IkeDeletePayload) payloadList.get(0))); - } - - @Test - public void testIgnoreUnrecognizedChildSpi() throws Exception { - int unrecognizedSpi = 2; - - setupIdleStateMachine(); - transitionToChildProcedureOngoing(); - - // Receive Delete Child Request - IkeDeletePayload[] inboundDelPayloads = - new IkeDeletePayload[] { - new IkeDeletePayload(new int[] {unrecognizedSpi, CHILD_SPI_REMOTE}) - }; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteChildPacket(inboundDelPayloads, false /*isResp*/)); - mLooper.dispatchAll(); - - // Verify received payloads - verifyChildReceiveDeleteRequest(mMockChildSessionStateMachine, inboundDelPayloads); - - // child outbound payload list ready - IkeDeletePayload outPayload = new IkeDeletePayload(new int[] {CHILD_SPI_LOCAL}); - outboundDeleteChildPayloadsReady( - mDummyChildSmCallback, outPayload, true /*isResp*/, mMockChildSessionStateMachine); - mLooper.dispatchAll(); - - // Verify outbound response - List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, payloadList.size()); - assertEquals(outPayload, ((IkeDeletePayload) payloadList.get(0))); - } - - @Test - public void testRemoteDeleteChildHandlesReqWithNoRecognizedSpi() throws Exception { - int unrecognizedSpi = 2; - - setupIdleStateMachine(); - - // Receive Delete Child Request without any recognized SPI - IkeDeletePayload[] inboundDelPayloads = - new IkeDeletePayload[] {new IkeDeletePayload(new int[] {unrecognizedSpi})}; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteChildPacket(inboundDelPayloads, false /*isResp*/)); - mLooper.dispatchAll(); - - // Verify outbound empty response was sent - List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertTrue(payloadList.isEmpty()); - - // Verify IKE Session was back to Idle - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testRemoteCreateChild() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - CMD_RECEIVE_IKE_PACKET, makeCreateChildCreateMessage(false /*isResp*/)); - - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - List<IkePayload> ikePayloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, ikePayloadList.size()); - assertEquals( - ERROR_TYPE_NO_ADDITIONAL_SAS, - ((IkeNotifyPayload) ikePayloadList.get(0)).notifyType); - } - - @Test - public void testTriggerRemoteRekeyChild() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - CMD_RECEIVE_IKE_PACKET, - makeRekeyChildCreateMessage(false /*isResp*/, CHILD_SPI_REMOTE)); - mLooper.dispatchAll(); - - verify(mMockChildSessionStateMachine) - .receiveRequest( - eq(IKE_EXCHANGE_SUBTYPE_REKEY_CHILD), - eq(EXCHANGE_TYPE_CREATE_CHILD_SA), - any(List.class)); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - } - - @Test - public void testHandleRekeyChildReqWithUnrecognizedSpi() throws Exception { - int unrecognizedSpi = 2; - - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - CMD_RECEIVE_IKE_PACKET, - makeRekeyChildCreateMessage(false /*isResp*/, unrecognizedSpi)); - mLooper.dispatchAll(); - - verify(mMockChildSessionStateMachine, never()).receiveRequest(anyInt(), anyInt(), any()); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - List<IkePayload> ikePayloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, ikePayloadList.size()); - IkeNotifyPayload notifyPayload = (IkeNotifyPayload) ikePayloadList.get(0); - assertEquals(ERROR_TYPE_CHILD_SA_NOT_FOUND, notifyPayload.notifyType); - assertEquals(unrecognizedSpi, notifyPayload.spi); - } - - private void verifyNotifyUserCloseSession() { - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockIkeSessionCallback).onClosed(); - } - - @Test - public void testRcvRemoteDeleteIkeWhenChildProcedureOngoing() throws Exception { - setupIdleStateMachine(); - transitionToChildProcedureOngoing(); - - mIkeSessionStateMachine.sendMessage( - CMD_RECEIVE_IKE_PACKET, makeDeleteIkeRequest(mSpyCurrentIkeSaRecord)); - - mLooper.dispatchAll(); - - verifyNotifyUserCloseSession(); - - // Verify state machine quit properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - - List<IkePayload> ikePayloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertTrue(ikePayloadList.isEmpty()); - } - - @Test - public void testRcvRemoteRekeyIkeWhenChildProcedureOngoing() throws Exception { - setupIdleStateMachine(); - transitionToChildProcedureOngoing(); - - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, makeRekeyIkeRequest()); - - mLooper.dispatchAll(); - - // Since we have forced state machine to transition to ChildProcedureOngoing state without - // really starting any Child procedure, it should transition to Idle at this time. - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - List<IkePayload> ikePayloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, ikePayloadList.size()); - assertEquals( - ERROR_TYPE_TEMPORARY_FAILURE, - ((IkeNotifyPayload) ikePayloadList.get(0)).notifyType); - } - - @Test - public void testKillChildSessions() throws Exception { - setupIdleStateMachine(); - - ChildSessionStateMachine childOne = mock(ChildSessionStateMachine.class); - ChildSessionStateMachine childTwo = mock(ChildSessionStateMachine.class); - registerChildStateMachine(mock(ChildSessionCallback.class), childOne); - registerChildStateMachine(mock(ChildSessionCallback.class), childTwo); - - mIkeSessionStateMachine.mCurrentIkeSaRecord = null; - - mIkeSessionStateMachine.quitNow(); - - mLooper.dispatchAll(); - - verify(childOne).killSession(); - verify(childTwo).killSession(); - } - - private IkeMessage verifyAuthReqAndGetMsg() { - IkeMessage ikeAuthReqMessage = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = ikeAuthReqMessage.ikeHeader; - assertEquals(IkeHeader.EXCHANGE_TYPE_IKE_AUTH, ikeHeader.exchangeType); - assertFalse(ikeHeader.isResponseMsg); - assertTrue(ikeHeader.fromIkeInitiator); - - return ikeAuthReqMessage; - } - - private IkeMessage verifyAuthReqWithChildPayloadsAndGetMsg() { - IkeMessage ikeAuthReqMessage = verifyAuthReqAndGetMsg(); - - assertNotNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_ID_INITIATOR, IkeIdPayload.class)); - assertNotNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_ID_RESPONDER, IkeIdPayload.class)); - assertNotNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class)); - assertNotNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_TS_INITIATOR, IkeTsPayload.class)); - assertNotNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_TS_RESPONDER, IkeTsPayload.class)); - - return ikeAuthReqMessage; - } - - private void verifySharedKeyAuthentication( - IkeAuthPskPayload spyAuthPayload, - IkeIdPayload respIdPayload, - List<IkePayload> authRelatedPayloads, - boolean hasChildPayloads) - throws Exception { - // Send IKE AUTH response to IKE state machine - ReceivedIkePacket authResp = makeIkeAuthRespWithChildPayloads(authRelatedPayloads); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, authResp); - mLooper.dispatchAll(); - - // Validate outbound IKE AUTH request - IkeMessage ikeAuthReqMessage; - if (hasChildPayloads) { - ikeAuthReqMessage = verifyAuthReqWithChildPayloadsAndGetMsg(); - } else { - ikeAuthReqMessage = verifyAuthReqAndGetMsg(); - } - assertNotNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_AUTH, IkeAuthPskPayload.class)); - - // Validate inbound IKE AUTH response - verifyIncrementLocaReqMsgId(); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, authResp); - - // Validate authentication is done. Cannot use matchers because IkeAuthPskPayload is final. - verify(spyAuthPayload) - .verifyInboundSignature( - mPsk, - mIkeSessionStateMachine.mIkeInitRequestBytes, - mSpyCurrentIkeSaRecord.nonceInitiator, - respIdPayload.getEncodedPayloadBody(), - mIkeSessionStateMachine.mIkePrf, - mSpyCurrentIkeSaRecord.getSkPr()); - - // Validate that user has been notified - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockIkeSessionCallback).onOpened(any()); - // TODO: Verify sessionConfiguration - - // Verify payload list pair for first Child negotiation - ArgumentCaptor<List<IkePayload>> mReqPayloadListCaptor = - ArgumentCaptor.forClass(List.class); - ArgumentCaptor<List<IkePayload>> mRespPayloadListCaptor = - ArgumentCaptor.forClass(List.class); - verify(mMockChildSessionStateMachine) - .handleFirstChildExchange( - mReqPayloadListCaptor.capture(), - mRespPayloadListCaptor.capture(), - eq(LOCAL_ADDRESS), - eq(REMOTE_ADDRESS), - any(), // udpEncapSocket - eq(mIkeSessionStateMachine.mIkePrf), - any()); // sk_d - List<IkePayload> childReqList = mReqPayloadListCaptor.getValue(); - List<IkePayload> childRespList = mRespPayloadListCaptor.getValue(); - - assertTrue(isIkePayloadExist(childReqList, IkePayload.PAYLOAD_TYPE_SA)); - assertTrue(isIkePayloadExist(childReqList, IkePayload.PAYLOAD_TYPE_TS_INITIATOR)); - assertTrue(isIkePayloadExist(childReqList, IkePayload.PAYLOAD_TYPE_TS_RESPONDER)); - assertTrue(isIkePayloadExist(childReqList, IkePayload.PAYLOAD_TYPE_NONCE)); - IkeSaPayload reqSaPayload = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, childReqList); - assertFalse(reqSaPayload.isSaResponse); - - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_SA)); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_TS_INITIATOR)); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_TS_RESPONDER)); - assertTrue(isIkePayloadExist(childRespList, IkePayload.PAYLOAD_TYPE_NONCE)); - IkeSaPayload respSaPayload = - IkePayload.getPayloadForTypeInProvidedList( - IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, childRespList); - assertTrue(respSaPayload.isSaResponse); - - // Mock finishing first Child SA negotiation. - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - - verify(mMockChildSessionFactoryHelper) - .makeChildSessionStateMachine( - eq(mLooper.getLooper()), - eq(mContext), - eq(mChildSessionOptions), - eq(mSpyUserCbExecutor), - eq(mMockChildSessionCallback), - mChildSessionSmCbCaptor.capture()); - IChildSessionSmCallback cb = mChildSessionSmCbCaptor.getValue(); - - cb.onProcedureFinished(mMockChildSessionStateMachine); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - private IkeAuthPskPayload makeSpyRespPskPayload() throws Exception { - IkeAuthPskPayload spyAuthPayload = - spy( - (IkeAuthPskPayload) - IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_AUTH, - true /*isResp*/, - PSK_AUTH_RESP_PAYLOAD_HEX_STRING)); - - doNothing() - .when(spyAuthPayload) - .verifyInboundSignature(any(), any(), any(), any(), any(), any()); - return spyAuthPayload; - } - - private IkeAuthDigitalSignPayload makeSpyDigitalSignAuthPayload() throws Exception { - IkeAuthDigitalSignPayload spyAuthPayload = - spy( - (IkeAuthDigitalSignPayload) - IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_AUTH, - true /*isResp*/, - GENERIC_DIGITAL_SIGN_AUTH_RESP_HEX_STRING)); - doNothing() - .when(spyAuthPayload) - .verifyInboundSignature(any(), any(), any(), any(), any(), any()); - return spyAuthPayload; - } - - private IkeIdPayload makeRespIdPayload() throws Exception { - return (IkeIdPayload) - IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_ID_RESPONDER, - true /*isResp*/, - ID_PAYLOAD_RESPONDER_HEX_STRING); - } - - @Test - public void testCreateIkeLocalIkeAuthPsk() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - - // Build IKE AUTH response with Auth-PSK Payload and ID-Responder Payload. - List<IkePayload> authRelatedPayloads = new LinkedList<>(); - IkeAuthPskPayload spyAuthPayload = makeSpyRespPskPayload(); - authRelatedPayloads.add(spyAuthPayload); - - IkeIdPayload respIdPayload = makeRespIdPayload(); - authRelatedPayloads.add(respIdPayload); - - verifySharedKeyAuthentication(spyAuthPayload, respIdPayload, authRelatedPayloads, true); - verifyRetransmissionStopped(); - } - - @Test - public void testCreateIkeLocalIkeAuthPskVerifyFail() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - resetMockIkeMessageHelper(); - - // Build IKE AUTH response with invalid Auth-PSK Payload and ID-Responder Payload. - List<IkePayload> authRelatedPayloads = new LinkedList<>(); - IkeAuthPskPayload spyAuthPayload = makeSpyRespPskPayload(); - doThrow(new AuthenticationFailedException("DummyAuthFailException")) - .when(spyAuthPayload) - .verifyInboundSignature(any(), any(), any(), any(), any(), any()); - authRelatedPayloads.add(spyAuthPayload); - - IkeIdPayload respIdPayload = makeRespIdPayload(); - authRelatedPayloads.add(respIdPayload); - - // Send response to IKE state machine - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeIkeAuthRespWithChildPayloads(authRelatedPayloads)); - mLooper.dispatchAll(); - - // Verify Delete request was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(false /*isResp*/); - assertEquals(1, payloads.size()); - assertEquals(IkePayload.PAYLOAD_TYPE_DELETE, payloads.get(0).payloadType); - - // Verify IKE Session was closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback) - .onClosedExceptionally(any(AuthenticationFailedException.class)); - } - - @Test - public void testAuthPskHandleRespWithParsingError() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - resetMockIkeMessageHelper(); - - // Mock receiving packet with syntax error - ReceivedIkePacket mockInvalidPacket = - makeDummyReceivedIkePacketWithInvalidSyntax( - mSpyCurrentIkeSaRecord, true /*isResp*/, IkeHeader.EXCHANGE_TYPE_IKE_AUTH); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockInvalidPacket); - mLooper.dispatchAll(); - - // Verify Delete request was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(false /*isResp*/); - assertEquals(1, payloads.size()); - assertEquals(IkePayload.PAYLOAD_TYPE_DELETE, payloads.get(0).payloadType); - - // Verify IKE Session is closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - } - - @Test - public void testCreateIkeLocalIkeAuthPreEap() throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Mock IKE INIT - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - - // Build IKE AUTH response with EAP. Auth, ID-Resp and Cert payloads. - List<IkePayload> authRelatedPayloads = new LinkedList<>(); - - authRelatedPayloads.add(new IkeEapPayload(EAP_DUMMY_MSG)); - authRelatedPayloads.add(makeSpyDigitalSignAuthPayload()); - authRelatedPayloads.add(makeRespIdPayload()); - - IkeCertX509CertPayload certPayload = new IkeCertX509CertPayload(mServerEndCertificate); - authRelatedPayloads.add(certPayload); - - // Send IKE AUTH response to IKE state machine - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeIkeAuthRespWithoutChildPayloads(authRelatedPayloads)); - mLooper.dispatchAll(); - - // Validate outbound IKE AUTH request - IkeMessage ikeAuthReqMessage = verifyAuthReqWithChildPayloadsAndGetMsg(); - assertNull( - ikeAuthReqMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_AUTH, IkeAuthPayload.class)); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeAuthInEap); - verifyRetransmissionStopped(); - assertNotNull(mIkeSessionStateMachine.mInitIdPayload); - assertNotNull(mIkeSessionStateMachine.mRespIdPayload); - } - - private IEapCallback verifyEapAuthenticatorCreatedAndGetCallback() { - ArgumentCaptor<IEapCallback> captor = ArgumentCaptor.forClass(IEapCallback.class); - - verify(mMockEapAuthenticatorFactory) - .newEapAuthenticator( - eq(mIkeSessionStateMachine.getHandler().getLooper()), - captor.capture(), - eq(mContext), - eq(mEapSessionConfig)); - - return captor.getValue(); - } - - @Test - public void testCreateIkeLocalIkeAuthInEapStartsAuthenticatorAndProxiesMessage() - throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthInEap); - mLooper.dispatchAll(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EAP_START_EAP_AUTH, new IkeEapPayload(EAP_DUMMY_MSG)); - mLooper.dispatchAll(); - - verifyEapAuthenticatorCreatedAndGetCallback(); - - verify(mMockEapAuthenticator).processEapMessage(eq(EAP_DUMMY_MSG)); - } - - @Test - public void testCreateIkeLocalIkeAuthInEapHandlesOutboundResponse() throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthInEap); - mLooper.dispatchAll(); - - IEapCallback callback = verifyEapAuthenticatorCreatedAndGetCallback(); - callback.onResponse(EAP_DUMMY_MSG); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Verify EAP response - IkeMessage resp = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - IkeHeader ikeHeader = resp.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_IKE_AUTH, ikeHeader.exchangeType); - assertFalse(ikeHeader.isResponseMsg); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - - assertEquals(1, resp.ikePayloadList.size()); - assertArrayEquals(EAP_DUMMY_MSG, ((IkeEapPayload) resp.ikePayloadList.get(0)).eapMessage); - } - - @Test - public void testCreateIkeLocalIkeAuthInEapHandlesMissingEapPacket() throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthInEap); - mLooper.dispatchAll(); - - // Mock sending IKE_AUTH{EAP} request - IEapCallback callback = verifyEapAuthenticatorCreatedAndGetCallback(); - callback.onResponse(EAP_DUMMY_MSG); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Send IKE AUTH response with no EAP Payload to IKE state machine - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeIkeAuthRespWithoutChildPayloads(new LinkedList<>())); - mLooper.dispatchAll(); - - // Verify state machine quit properly - verify(mMockIkeSessionCallback) - .onClosedExceptionally(any(AuthenticationFailedException.class)); - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testCreateIkeLocalIkeAuthInEapHandlesSuccess() throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthInEap); - mLooper.dispatchAll(); - - IEapCallback callback = verifyEapAuthenticatorCreatedAndGetCallback(); - - // Setup dummy initIdPayload for next state. - mIkeSessionStateMachine.mInitIdPayload = mock(IkeIdPayload.class); - when(mIkeSessionStateMachine.mInitIdPayload.getEncodedPayloadBody()) - .thenReturn(new byte[0]); - - callback.onSuccess(mPsk, new byte[0]); // use mPsk as MSK, eMSK does not matter - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeAuthPostEap); - } - - @Test - public void testCreateIkeLocalIkeAuthInEapHandlesError() throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthInEap); - mLooper.dispatchAll(); - - IEapCallback callback = verifyEapAuthenticatorCreatedAndGetCallback(); - - Throwable error = new IllegalArgumentException(); - callback.onError(error); - mLooper.dispatchAll(); - - // Fires user error callbacks - verify(mMockIkeSessionCallback) - .onClosedExceptionally(argThat(err -> err.getCause() == error)); - - // Verify state machine quit properly - verify(mSpyCurrentIkeSaRecord).close(); - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testCreateIkeLocalIkeAuthInEapHandlesFailure() throws Exception { - mIkeSessionStateMachine.quitNow(); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthInEap); - mLooper.dispatchAll(); - - IEapCallback callback = verifyEapAuthenticatorCreatedAndGetCallback(); - callback.onFail(); - mLooper.dispatchAll(); - - // Fires user error callbacks - verify(mMockIkeSessionCallback) - .onClosedExceptionally(any(AuthenticationFailedException.class)); - - // Verify state machine quit properly - verify(mSpyCurrentIkeSaRecord).close(); - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testCreateIkeLocalIkeAuthPostEap() throws Exception { - mIkeSessionStateMachine.quitNow(); - reset(mMockChildSessionFactoryHelper); - setupChildStateMachineFactory(mMockChildSessionStateMachine); - mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionOptionsEap()); - - // Setup dummy state from IkeAuthPreEap for next state. - mIkeSessionStateMachine.mInitIdPayload = mock(IkeIdPayload.class); - when(mIkeSessionStateMachine.mInitIdPayload.getEncodedPayloadBody()) - .thenReturn(new byte[0]); - mIkeSessionStateMachine.mRespIdPayload = - (IkeIdPayload) - IkeTestUtils.hexStringToIkePayload( - IkePayload.PAYLOAD_TYPE_ID_RESPONDER, - true /*isResp*/, - ID_PAYLOAD_RESPONDER_HEX_STRING); - - List<Integer> payloadTypeList = new LinkedList<>(); - List<String> payloadHexStringList = new LinkedList<>(); - - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_SA); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_TS_INITIATOR); - payloadTypeList.add(IkePayload.PAYLOAD_TYPE_TS_RESPONDER); - - payloadHexStringList.add(CHILD_SA_PAYLOAD_HEX_STRING); - payloadHexStringList.add(TS_INIT_PAYLOAD_HEX_STRING); - payloadHexStringList.add(TS_RESP_PAYLOAD_HEX_STRING); - - mIkeSessionStateMachine.mFirstChildReqList = - hexStrListToIkePayloadList(payloadTypeList, payloadHexStringList, false /*isResp*/); - - // Setup state and go to IN_EAP state - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuthPostEap); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_EAP_FINISH_EAP_AUTH, mPsk); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Build IKE AUTH response with Auth-PSK Payload and ID-Responder Payload. - List<IkePayload> authRelatedPayloads = new LinkedList<>(); - IkeAuthPskPayload spyAuthPayload = makeSpyRespPskPayload(); - authRelatedPayloads.add(spyAuthPayload); - - IkeIdPayload respIdPayload = makeRespIdPayload(); - - verifySharedKeyAuthentication(spyAuthPayload, respIdPayload, authRelatedPayloads, false); - verifyRetransmissionStopped(); - } - - @Test - public void testCreateIkeLocalIkeAuthHandlesFirstFrag() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - - // Received IKE fragment - byte[] unencryptedData = "testCreateIkeLocalIkeAuthHandlesFrag".getBytes(); - int fragNum = 1; - int totalFragments = 2; - IkeSkfPayload skfPayload = - IkeTestUtils.makeDummySkfPayload(unencryptedData, fragNum, totalFragments); - - ReceivedIkePacket packet = - makeDummyReceivedIkeFragmentPacket( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - skfPayload, - PAYLOAD_TYPE_AUTH, - null /* collectedFrags*/); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, packet); - mLooper.dispatchAll(); - - // Verify state doesn't change - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeAuth); - - // Verify the IkeSaRecord has stored the fragment. - DecodeResultPartial resultPartial = - mSpyCurrentIkeSaRecord.getCollectedFragments(true /*isResp*/); - assertEquals(PAYLOAD_TYPE_AUTH, resultPartial.firstPayloadType); - assertEquals(totalFragments, resultPartial.collectedFragsList.length); - assertArrayEquals(unencryptedData, resultPartial.collectedFragsList[fragNum - 1]); - assertFalse(resultPartial.isAllFragmentsReceived()); - - assertNull(mSpyCurrentIkeSaRecord.getCollectedFragments(false /*isResp*/)); - } - - @Test - public void testCreateIkeLocalIkeAuthHandlesLastFragOk() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - - // Set previously collected IKE fragments - DecodeResultPartial mockCollectedFrags = mock(DecodeResultPartial.class); - mSpyCurrentIkeSaRecord.updateCollectedFragments(mockCollectedFrags, true /*isResp*/); - - // Build reassembled IKE AUTH response with Auth-PSK Payload and ID-Responder Payload. - List<IkePayload> authRelatedPayloads = new LinkedList<>(); - IkeAuthPskPayload spyAuthPayload = makeSpyRespPskPayload(); - authRelatedPayloads.add(spyAuthPayload); - - IkeIdPayload respIdPayload = makeRespIdPayload(); - authRelatedPayloads.add(respIdPayload); - - List<IkePayload> authPayloadList = - getIkeAuthPayloadListWithChildPayloads(authRelatedPayloads); - - // Receive last auth response and do IKE_AUTH - ReceivedIkePacket packet = - makeDummyReceivedLastIkeFragmentPacketOk( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - mockCollectedFrags, - authPayloadList, - "FirstFrag".getBytes()); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, packet); - mLooper.dispatchAll(); - - // Verify IKE AUTH is done - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - - // Verify collected response fragments are cleared. - assertNull(mSpyCurrentIkeSaRecord.getCollectedFragments(true /*isResp*/)); - verify(mSpyCurrentIkeSaRecord).resetCollectedFragments(true /*isResp*/); - } - - @Test - public void testCreateIkeLocalIkeAuthHandlesLastFragError() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - resetMockIkeMessageHelper(); - - // Set previously collected IKE fragments - DecodeResultPartial mockCollectedFrags = mock(DecodeResultPartial.class); - mSpyCurrentIkeSaRecord.updateCollectedFragments(mockCollectedFrags, true /*isResp*/); - - // Receive last auth response with syntax error - ReceivedIkePacket packet = - makeDummyReceivedLastIkeFragmentPacketError( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - mockCollectedFrags, - new InvalidSyntaxException("IkeStateMachineTest")); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, packet); - mLooper.dispatchAll(); - - // Verify Delete request is sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(false /*isResp*/); - assertEquals(1, payloads.size()); - assertEquals(IkePayload.PAYLOAD_TYPE_DELETE, payloads.get(0).payloadType); - - // Verify IKE Session is closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - - // Collected response fragments are cleared - assertNull(mSpyCurrentIkeSaRecord.getCollectedFragments(true /*isResp*/)); - verify(mSpyCurrentIkeSaRecord).resetCollectedFragments(true /*isResp*/); - } - - @Test - public void testRekeyIkeLocalCreateSendsRequest() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - verifyRetransmissionStarted(); - - // Verify outbound message - IkeMessage rekeyMsg = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = rekeyMsg.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, ikeHeader.exchangeType); - assertEquals(mSpyCurrentIkeSaRecord.getLocalRequestMessageId(), ikeHeader.messageId); - assertFalse(ikeHeader.isResponseMsg); - assertTrue(ikeHeader.fromIkeInitiator); - - // Verify SA payload & proposals - IkeSaPayload saPayload = - rekeyMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); - assertFalse(saPayload.isSaResponse); - assertEquals(1, saPayload.proposalList.size()); - - IkeSaPayload.IkeProposal proposal = - (IkeSaPayload.IkeProposal) saPayload.proposalList.get(0); - assertEquals(1, proposal.number); // Must be 1-indexed - assertEquals(IkePayload.PROTOCOL_ID_IKE, proposal.protocolId); - assertEquals(IkePayload.SPI_LEN_IKE, proposal.spiSize); - assertEquals(mIkeSessionStateMachine.mSaProposal, proposal.saProposal); - - // Verify Nonce and KE payloads exist. - assertNotNull( - rekeyMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class)); - - IkeKePayload kePayload = - rekeyMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class); - assertNotNull(kePayload); - assertTrue(kePayload.isOutbound); - } - - @Test - public void testRekeyIkeLocalCreateHandlesResponse() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Prepare "rekeyed" SA - setupRekeyedIkeSa(mSpyLocalInitIkeSaRecord); - - // Receive Rekey response - ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = makeRekeyIkeResponse(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementLocaReqMsgId(); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRespReceivedPacket); - - // Verify in delete state, and new SA record was saved: - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalDelete); - verifyRetransmissionStarted(); - assertEquals(mSpyLocalInitIkeSaRecord, mIkeSessionStateMachine.mLocalInitNewIkeSaRecord); - verify(mSpyIkeSocket) - .registerIke( - eq(mSpyLocalInitIkeSaRecord.getLocalSpi()), eq(mIkeSessionStateMachine)); - } - - @Test - public void testRekeyIkeLocalCreateHandleRespWithParsingError() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - resetMockIkeMessageHelper(); - - // Mock receiving packet with syntax error - ReceivedIkePacket mockInvalidPacket = - makeDummyReceivedIkePacketWithInvalidSyntax( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockInvalidPacket); - mLooper.dispatchAll(); - - // Verify Delete request was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(false /*isResp*/); - assertEquals(1, payloads.size()); - assertEquals(IkePayload.PAYLOAD_TYPE_DELETE, payloads.get(0).payloadType); - - // Verify IKE Session is closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - } - - @Test - public void testRekeyIkeLocalCreateHandleRespWithNonFatalErrorNotify() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - - // Mock receiving packet with NO_PROPOSAL_CHOSEN - ReceivedIkePacket resp = - makeResponseWithErrorNotify(new IkeNotifyPayload(ERROR_TYPE_NO_PROPOSAL_CHOSEN)); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, resp); - mLooper.dispatchAll(); - - // Verify IKE Session goes back to Idle - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - // Move time forward to trigger retry - mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - } - - @Test - public void testRekeyIkeLocalCreateHandleRespWithFatalErrorNotify() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - resetMockIkeMessageHelper(); - - // Mock receiving packet with NO_PROPOSAL_CHOSEN - ReceivedIkePacket resp = - makeResponseWithErrorNotify(new IkeNotifyPayload(ERROR_TYPE_INVALID_SYNTAX)); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, resp); - mLooper.dispatchAll(); - - // Verify no message was sent because a fatal error notification was received - verifyEncryptAndEncodeNeverCalled(mSpyCurrentIkeSaRecord); - - // Verify IKE Session is closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - } - - @Test - public void testRekeyIkeLocalCreateSaCreationFail() throws Exception { - // Throw error when building new IKE SA - throwExceptionWhenMakeRekeyIkeSa( - new GeneralSecurityException("testRekeyIkeLocalCreateSaCreationFail")); - - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - resetMockIkeMessageHelper(); - - // Receive Rekey response - ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = makeRekeyIkeResponse(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); - mLooper.dispatchAll(); - - // Verify Delete request was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(false /*isResp*/); - assertEquals(1, payloads.size()); - assertEquals(IkePayload.PAYLOAD_TYPE_DELETE, payloads.get(0).payloadType); - - // Verify IKE Session is closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(IkeInternalException.class)); - } - - @Test - public void testRekeyIkeLocalCreateHandleReqWithNonFatalError() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - resetMockIkeMessageHelper(); - - // Build protocol exception - List<Integer> unsupportedPayloads = new LinkedList<>(); - unsupportedPayloads.add(PAYLOAD_TYPE_UNSUPPORTED); - UnsupportedCriticalPayloadException exception = - new UnsupportedCriticalPayloadException(unsupportedPayloads); - - // Mock receiving packet with unsupported critical payload - ReceivedIkePacket mockInvalidPacket = - makeDummyReceivedIkePacketWithDecodingError( - mSpyCurrentIkeSaRecord, - false /*isResp*/, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - exception); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockInvalidPacket); - mLooper.dispatchAll(); - - // Verify error notification was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, payloads.size()); - - IkePayload payload = payloads.get(0); - assertEquals(IkePayload.PAYLOAD_TYPE_NOTIFY, payload.payloadType); - assertEquals( - ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, ((IkeNotifyPayload) payload).notifyType); - - // Verify IKE Session stays in the same state - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - } - - private void mockCreateAndTransitionToRekeyDeleteLocal() { - // Seed fake rekey data and force transition to RekeyIkeLocalDelete - mIkeSessionStateMachine.mLocalInitNewIkeSaRecord = mSpyLocalInitIkeSaRecord; - mIkeSessionStateMachine.addIkeSaRecord(mSpyLocalInitIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeLocalDelete); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - } - - @Test - public void testRekeyIkeLocalDeleteSendsRequest() throws Exception { - setupIdleStateMachine(); - mockCreateAndTransitionToRekeyDeleteLocal(); - - // Verify Rekey-Delete request - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalDelete); - verifyRetransmissionStarted(); - - // Verify outbound message - IkeMessage delMsg = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = delMsg.ikeHeader; - assertEquals(mSpyCurrentIkeSaRecord.getInitiatorSpi(), ikeHeader.ikeInitiatorSpi); - assertEquals(mSpyCurrentIkeSaRecord.getResponderSpi(), ikeHeader.ikeResponderSpi); - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - assertFalse(ikeHeader.isResponseMsg); - - List<IkeDeletePayload> deletePayloadList = - delMsg.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_DELETE, IkeDeletePayload.class); - assertEquals(1, deletePayloadList.size()); - - IkeDeletePayload deletePayload = deletePayloadList.get(0); - assertEquals(IkePayload.PROTOCOL_ID_IKE, deletePayload.protocolId); - assertEquals(0, deletePayload.numSpi); - assertEquals(0, deletePayload.spiSize); - assertArrayEquals(new int[0], deletePayload.spisToDelete); - } - - private void verifyRekeyReplaceSa(IkeSaRecord newSaRecord) { - verify(mSpyCurrentIkeSaRecord).close(); - verify(mSpyIkeSocket).unregisterIke(eq(mSpyCurrentIkeSaRecord.getLocalSpi())); - verify(mSpyIkeSocket, never()).unregisterIke(eq(newSaRecord.getLocalSpi())); - - assertEquals(mIkeSessionStateMachine.mCurrentIkeSaRecord, newSaRecord); - - verify(mMockChildSessionStateMachine).setSkD(newSaRecord.getSkD()); - } - - @Test - public void testRekeyIkeLocalDeleteHandlesResponse() throws Exception { - setupIdleStateMachine(); - mockCreateAndTransitionToRekeyDeleteLocal(); - - // Receive Delete response - ReceivedIkePacket dummyDeleteIkeRespReceivedPacket = - makeDeleteIkeResponse(mSpyCurrentIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRespReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementLocaReqMsgId(); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRespReceivedPacket); - - // Verify final state - Idle, with new SA, and old SA closed. - verifyRekeyReplaceSa(mSpyLocalInitIkeSaRecord); - verify(mMockIkeSessionCallback, never()).onClosed(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - verifyRetransmissionStopped(); - } - - @Test - public void testRekeyIkeLocalDeleteHandlesRespWithParsingError() throws Exception { - setupIdleStateMachine(); - mockCreateAndTransitionToRekeyDeleteLocal(); - resetMockIkeMessageHelper(); - - // Mock receiving packet with syntax error - ReceivedIkePacket mockInvalidPacket = - makeDummyReceivedIkePacketWithInvalidSyntax( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockInvalidPacket); - mLooper.dispatchAll(); - - // Verify no more request out - verifyEncryptAndEncodeNeverCalled(mSpyCurrentIkeSaRecord); - - // Verify final state - Idle, with new SA, and old SA closed. - verifyRekeyReplaceSa(mSpyLocalInitIkeSaRecord); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - verifyRetransmissionStopped(); - } - - @Test - public void testRekeyIkeLocalDeleteWithRequestOnNewSa() throws Exception { - setupIdleStateMachine(); - mockCreateAndTransitionToRekeyDeleteLocal(); - - // Receive an empty (DPD) request on the new IKE SA - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDpdIkeRequest(mSpyLocalInitIkeSaRecord)); - mLooper.dispatchAll(); - - // Verify final state - Idle, with new SA, and old SA closed. - verifyRekeyReplaceSa(mSpyLocalInitIkeSaRecord); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - verifyRetransmissionStopped(); - } - - @Test - public void testRekeyIkeLocalDeleteWithRequestFragOnNewSa() throws Exception { - setupIdleStateMachine(); - mockCreateAndTransitionToRekeyDeleteLocal(); - - // Received IKE fragment - byte[] unencryptedData = "testRekeyIkeLocalDeleteWithRequestFragOnNewSa".getBytes(); - int fragNum = 1; - int totalFragments = 2; - IkeSkfPayload skfPayload = - IkeTestUtils.makeDummySkfPayload(unencryptedData, fragNum, totalFragments); - - ReceivedIkePacket packet = - makeDummyReceivedIkeFragmentPacket( - mSpyLocalInitIkeSaRecord, - false /*isResp*/, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - skfPayload, - PAYLOAD_TYPE_SA, - null /* collectedFrags*/); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, packet); - mLooper.dispatchAll(); - - // Verify rekey is done. - verifyRekeyReplaceSa(mSpyLocalInitIkeSaRecord); - verifyRetransmissionStopped(); - - // Verify the IkeSaRecord has stored the new fragment. - DecodeResultPartial resultPartial = - mSpyLocalInitIkeSaRecord.getCollectedFragments(false /*isResp*/); - assertEquals(PAYLOAD_TYPE_SA, resultPartial.firstPayloadType); - assertEquals(totalFragments, resultPartial.collectedFragsList.length); - assertArrayEquals(unencryptedData, resultPartial.collectedFragsList[fragNum - 1]); - assertFalse(resultPartial.isAllFragmentsReceived()); - } - - @Test - public void testRekeyIkeRemoteDeleteWithRequestOnNewSa() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyIkeRemoteDelete - mIkeSessionStateMachine.mRemoteInitNewIkeSaRecord = mSpyRemoteInitIkeSaRecord; - mIkeSessionStateMachine.addIkeSaRecord(mSpyRemoteInitIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeRemoteDelete); - mLooper.dispatchAll(); - - // Receive an empty (DPD) request on the new IKE SA - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDpdIkeRequest(mSpyRemoteInitIkeSaRecord)); - mLooper.dispatchAll(); - - // Verify final state - Idle, with new SA, and old SA closed. - verifyRekeyReplaceSa(mSpyRemoteInitIkeSaRecord); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testRekeyIkeRemoteCreate() throws Exception { - setupIdleStateMachine(); - - setupRekeyedIkeSa(mSpyRemoteInitIkeSaRecord); - - // Receive Rekey request - ReceivedIkePacket dummyRekeyIkeRequestReceivedPacket = makeRekeyIkeRequest(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRequestReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementRemoteReqMsgId(); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRequestReceivedPacket); - - // Verify SA created with correct parameters - ArgumentCaptor<SaRecord.IkeSaRecordConfig> recordConfigCaptor = - ArgumentCaptor.forClass(SaRecord.IkeSaRecordConfig.class); - verify(mMockSaRecordHelper) - .makeRekeyedIkeSaRecord(any(), any(), any(), any(), recordConfigCaptor.capture()); - assertEquals(IKE_REKEY_SA_INITIATOR_SPI, recordConfigCaptor.getValue().initSpi.getSpi()); - - // Verify outbound CREATE_CHILD_SA message - IkeMessage rekeyCreateResp = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - IkeHeader rekeyCreateRespHeader = rekeyCreateResp.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, rekeyCreateRespHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyCreateRespHeader.exchangeType); - assertTrue(rekeyCreateRespHeader.isResponseMsg); - assertTrue(rekeyCreateRespHeader.fromIkeInitiator); - assertNotNull( - rekeyCreateResp.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class)); - assertNotNull( - rekeyCreateResp.getPayloadForType(IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class)); - assertNotNull( - rekeyCreateResp.getPayloadForType( - IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class)); - - // Verify SA, StateMachine state - assertEquals(mSpyCurrentIkeSaRecord, mIkeSessionStateMachine.mIkeSaRecordAwaitingRemoteDel); - assertEquals(mSpyRemoteInitIkeSaRecord, mIkeSessionStateMachine.mIkeSaRecordSurviving); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeRemoteDelete); - verify(mSpyIkeSocket) - .registerIke( - eq(mSpyRemoteInitIkeSaRecord.getLocalSpi()), eq(mIkeSessionStateMachine)); - } - - @Test - public void testRekeyIkeRemoteCreateHandlesInvalidReq() throws Exception { - setupIdleStateMachine(); - - // Receive Rekey request - ReceivedIkePacket request = makeRekeyIkeRequestWithUnacceptableProposal(); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, request); - mLooper.dispatchAll(); - - verifyProcessRekeyReqFailure(ERROR_TYPE_NO_PROPOSAL_CHOSEN); - } - - @Test - public void testRekeyIkeRemoteCreateSaCreationFailure() throws Exception { - // Throw error when building new IKE SA - throwExceptionWhenMakeRekeyIkeSa( - new GeneralSecurityException("testRekeyIkeRemoteCreateSaCreationFailure")); - setupIdleStateMachine(); - - // Receive Rekey request - ReceivedIkePacket request = makeRekeyIkeRequest(); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, request); - mLooper.dispatchAll(); - - verifyProcessRekeyReqFailure(ERROR_TYPE_NO_PROPOSAL_CHOSEN); - } - - private void verifyProcessRekeyReqFailure(int expectedErrorCode) { - // Verify IKE Session is back to Idle - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - // Verify error notification was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, payloads.size()); - IkeNotifyPayload notify = (IkeNotifyPayload) payloads.get(0); - assertEquals(expectedErrorCode, notify.notifyType); - } - - @Test - public void testRekeyIkeRemoteDelete() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyIkeLocalDelete - mIkeSessionStateMachine.mRemoteInitNewIkeSaRecord = mSpyRemoteInitIkeSaRecord; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeRemoteDelete); - mLooper.dispatchAll(); - - // Rekey Delete request - ReceivedIkePacket dummyDeleteIkeRequestReceivedPacket = - makeDeleteIkeRequest(mSpyCurrentIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRequestReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementRemoteReqMsgId(); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRequestReceivedPacket); - - // Verify outbound DELETE_IKE_SA message - IkeMessage rekeyDeleteResp = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - IkeHeader rekeyDeleteRespHeader = rekeyDeleteResp.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, rekeyDeleteRespHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, rekeyDeleteRespHeader.exchangeType); - assertTrue(rekeyDeleteRespHeader.isResponseMsg); - assertTrue(rekeyDeleteRespHeader.fromIkeInitiator); - assertTrue(rekeyDeleteResp.ikePayloadList.isEmpty()); - - // Verify final state - Idle, with new SA, and old SA closed. - verifyRekeyReplaceSa(mSpyRemoteInitIkeSaRecord); - - verify(mMockIkeSessionCallback, never()).onClosed(); - - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRequestReceivedPacket); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testRekeyIkeRemoteDeleteExitAndRenter() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyIkeLocalDelete - mIkeSessionStateMachine.mRemoteInitNewIkeSaRecord = mSpyRemoteInitIkeSaRecord; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeRemoteDelete); - mLooper.dispatchAll(); - - // Trigger a timeout, and immediately re-enter remote-delete - mLooper.moveTimeForward(IkeSessionStateMachine.REKEY_DELETE_TIMEOUT_MS / 2 + 1); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.TIMEOUT_REKEY_REMOTE_DELETE); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeRemoteDelete); - mLooper.dispatchAll(); - - // Shift time forward, and assert the previous timeout was NOT fired. - mLooper.moveTimeForward(IkeSessionStateMachine.REKEY_DELETE_TIMEOUT_MS / 2 + 1); - mLooper.dispatchAll(); - - // Verify no request received, or response sent. - verify(mMockIkeMessageHelper, never()).decode(anyInt(), anyObject(), anyObject()); - verifyEncryptAndEncodeNeverCalled(mSpyCurrentIkeSaRecord); - - // Verify final state has not changed - signal was not sent. - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeRemoteDelete); - } - - @Test - public void testRekeyIkeRemoteDeleteTimedOut() throws Exception { - setupIdleStateMachine(); - - // Seed fake rekey data and force transition to RekeyIkeLocalDelete - mIkeSessionStateMachine.mRemoteInitNewIkeSaRecord = mSpyRemoteInitIkeSaRecord; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeRemoteDelete); - mLooper.dispatchAll(); - - mLooper.moveTimeForward(IkeSessionStateMachine.REKEY_DELETE_TIMEOUT_MS); - mLooper.dispatchAll(); - - // Verify no request received, or response sent. - verify(mMockIkeMessageHelper, never()).decode(anyInt(), anyObject(), anyObject()); - verifyEncryptAndEncodeNeverCalled(mSpyCurrentIkeSaRecord); - - // Verify final state - Idle, with new SA, and old SA closed. - verifyRekeyReplaceSa(mSpyRemoteInitIkeSaRecord); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testSimulRekey() throws Exception { - setupIdleStateMachine(); - - // Prepare "rekeyed" SA - setupRekeyedIkeSa(mSpyLocalInitIkeSaRecord); - when(mSpyLocalInitIkeSaRecord.compareTo(mSpyRemoteInitIkeSaRecord)).thenReturn(1); - - // Send Rekey request on mSpyCurrentIkeSaRecord - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - - // Receive Rekey request on mSpyCurrentIkeSaRecord - ReceivedIkePacket dummyRekeyIkeRequestReceivedPacket = makeRekeyIkeRequest(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRequestReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementRemoteReqMsgId(); - - // Receive Rekey response on mSpyCurrentIkeSaRecord - ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = makeRekeyIkeResponse(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementLocaReqMsgId(); - verify(mSpyIkeSocket) - .registerIke( - eq(mSpyLocalInitIkeSaRecord.getLocalSpi()), eq(mIkeSessionStateMachine)); - - // Receive Delete response on mSpyCurrentIkeSaRecord - ReceivedIkePacket dummyDeleteIkeRespReceivedPacket = - makeDeleteIkeResponse(mSpyCurrentIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRespReceivedPacket); - mLooper.dispatchAll(); - verifyIncrementLocaReqMsgId(); - - // Verify - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRequestReceivedPacket); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyRekeyIkeRespReceivedPacket); - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDeleteIkeRespReceivedPacket); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - verifyRekeyReplaceSa(mSpyLocalInitIkeSaRecord); - verify(mMockIkeSessionCallback, never()).onClosed(); - } - - @Test - public void testOpenIkeSession() throws Exception { - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.Initial); - - mIkeSessionStateMachine.openSession(); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeInit); - } - - @Test - public void testIkeInitSchedulesRekey() throws Exception { - setupFirstIkeSa(); - - // Send IKE INIT request - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE); - - // Receive IKE INIT response - ReceivedIkePacket dummyReceivedIkePacket = makeIkeInitResponse(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyReceivedIkePacket); - - // Mock IKE AUTH and transition to Idle - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); - mLooper.dispatchAll(); - mIkeSessionStateMachine.mSaProposal = buildSaProposal(); - - // Move time forward to trigger rekey - mLooper.moveTimeForward(SA_SOFT_LIFETIME_MS); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - } - - @Test - public void testRekeyCreateIkeSchedulesRekey() throws Exception { - setupIdleStateMachine(); - - // Send Rekey-Create request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - - // Prepare "rekeyed" SA - setupRekeyedIkeSa(mSpyLocalInitIkeSaRecord); - - // Receive Rekey response - ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = makeRekeyIkeResponse(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); - mLooper.dispatchAll(); - - // Mock rekey delete and transition to Idle - mIkeSessionStateMachine.mCurrentIkeSaRecord = mSpyLocalInitIkeSaRecord; - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); - mLooper.dispatchAll(); - - // Move time forward to trigger rekey - mLooper.moveTimeForward(SA_SOFT_LIFETIME_MS); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - } - - @Test - public void testBuildEncryptedInformationalMessage() throws Exception { - IkeNotifyPayload payload = new IkeNotifyPayload(ERROR_TYPE_INVALID_SYNTAX, new byte[0]); - - boolean isResp = false; - IkeMessage generated = - mIkeSessionStateMachine.buildEncryptedInformationalMessage( - mSpyCurrentIkeSaRecord, new IkeInformationalPayload[] {payload}, isResp, 0); - - assertEquals(mSpyCurrentIkeSaRecord.getInitiatorSpi(), generated.ikeHeader.ikeInitiatorSpi); - assertEquals(mSpyCurrentIkeSaRecord.getResponderSpi(), generated.ikeHeader.ikeResponderSpi); - assertEquals( - mSpyCurrentIkeSaRecord.getLocalRequestMessageId(), generated.ikeHeader.messageId); - assertEquals(isResp, generated.ikeHeader.isResponseMsg); - assertEquals(IkePayload.PAYLOAD_TYPE_SK, generated.ikeHeader.nextPayloadType); - - List<IkeNotifyPayload> generatedPayloads = - generated.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - assertEquals(1, generatedPayloads.size()); - - IkeNotifyPayload generatedPayload = generatedPayloads.get(0); - assertArrayEquals(new byte[0], generatedPayload.notifyData); - assertEquals(ERROR_TYPE_INVALID_SYNTAX, generatedPayload.notifyType); - } - - private void verifyLastSentRespAllPackets(byte[][] expectedPackets, IkeSaRecord saRecord) { - if (expectedPackets == null) { - assertNull(saRecord.getLastSentRespAllPackets()); - return; - } - - assertEquals(expectedPackets.length, saRecord.getLastSentRespAllPackets().size()); - for (int i = 0; i < expectedPackets.length; i++) { - assertArrayEquals(expectedPackets[i], saRecord.getLastSentRespAllPackets().get(i)); - } - } - - @Test - public void testEncryptedRetransmitterImmediatelySendsRequest() throws Exception { - setupIdleStateMachine(); - byte[][] dummyLastRespBytes = - new byte[][] {"testRetransmitterSendsRequestLastResp".getBytes()}; - mSpyCurrentIkeSaRecord.updateLastSentRespAllPackets(Arrays.asList(dummyLastRespBytes)); - - IkeMessage spyIkeReqMessage = - spy( - new IkeMessage( - new IkeHeader( - mSpyCurrentIkeSaRecord.getInitiatorSpi(), - mSpyCurrentIkeSaRecord.getResponderSpi(), - IkePayload.PAYLOAD_TYPE_SK, - EXCHANGE_TYPE_INFORMATIONAL, - false /*isResp*/, - mSpyCurrentIkeSaRecord.isLocalInit, - mSpyCurrentIkeSaRecord.getLocalRequestMessageId()), - new LinkedList<>())); - - // Use something unique as a sentinel value - byte[][] dummyReqBytesList = - new byte[][] { - "testRetransmitterSendsReqFrag1".getBytes(), - "testRetransmitterSendsReqFrag2".getBytes() - }; - - doReturn(dummyReqBytesList) - .when(spyIkeReqMessage) - .encryptAndEncode(any(), any(), eq(mSpyCurrentIkeSaRecord), anyBoolean(), anyInt()); - - IkeSessionStateMachine.EncryptedRetransmitter retransmitter = - mIkeSessionStateMachine.new EncryptedRetransmitter(spyIkeReqMessage); - - // Verify message is sent out, and that request does not change cached retransmit-response - // mLastSentIkeResp. - verify(mSpyIkeSocket).sendIkePacket(eq(dummyReqBytesList[0]), eq(REMOTE_ADDRESS)); - verify(mSpyIkeSocket).sendIkePacket(eq(dummyReqBytesList[1]), eq(REMOTE_ADDRESS)); - verifyLastSentRespAllPackets(dummyLastRespBytes, mSpyCurrentIkeSaRecord); - } - - // TODO: b/141275871 Test retransmisstions are fired for correct times within certain time. - - @Test - public void testCacheLastRequestAndResponse() throws Exception { - setupIdleStateMachine(); - mSpyCurrentIkeSaRecord.updateLastReceivedReqFirstPacket(null /*reqPacket*/); - mSpyCurrentIkeSaRecord.updateLastSentRespAllPackets(null /*respPacketList*/); - - byte[] dummyIkeReqFirstPacket = "testLastSentRequest".getBytes(); - byte[][] dummyIkeResp = - new byte[][] { - "testLastSentRespFrag1".getBytes(), "testLastSentRespFrag2".getBytes() - }; - - when(mMockIkeMessageHelper.encryptAndEncode( - any(), - any(), - eq(mSpyCurrentIkeSaRecord), - any(IkeMessage.class), - anyBoolean(), - anyInt())) - .thenReturn(dummyIkeResp); - - // Receive a DPD request, expect to send dummyIkeResp - ReceivedIkePacket dummyDpdRequest = - makeDpdIkeRequest( - mSpyCurrentIkeSaRecord.getRemoteRequestMessageId(), dummyIkeReqFirstPacket); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDpdRequest); - mLooper.dispatchAll(); - - verify(mSpyIkeSocket).sendIkePacket(eq(dummyIkeResp[0]), eq(REMOTE_ADDRESS)); - verify(mSpyIkeSocket).sendIkePacket(eq(dummyIkeResp[1]), eq(REMOTE_ADDRESS)); - - verifyLastSentRespAllPackets(dummyIkeResp, mSpyCurrentIkeSaRecord); - assertTrue(mSpyCurrentIkeSaRecord.isRetransmittedRequest(dummyIkeReqFirstPacket)); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testReplyRetransmittedRequest() throws Exception { - setupIdleStateMachine(); - - // Mock last sent request and response - byte[] dummyIkeReqFirstPacket = "testRcvRetransmittedRequestReq".getBytes(); - byte[][] dummyIkeResp = new byte[][] {"testRcvRetransmittedRequestResp".getBytes()}; - - mSpyCurrentIkeSaRecord.updateLastReceivedReqFirstPacket(dummyIkeReqFirstPacket); - mSpyCurrentIkeSaRecord.updateLastSentRespAllPackets(Arrays.asList(dummyIkeResp)); - mSpyCurrentIkeSaRecord.incrementRemoteRequestMessageId(); - - // Build request with last validated message ID - ReceivedIkePacket request = - makeDpdIkeRequest( - mSpyCurrentIkeSaRecord.getRemoteRequestMessageId() - 1, - dummyIkeReqFirstPacket); - - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, request); - - mLooper.dispatchAll(); - - verifyLastSentRespAllPackets(dummyIkeResp, mSpyCurrentIkeSaRecord); - verify(mSpyIkeSocket).sendIkePacket(eq(dummyIkeResp[0]), eq(REMOTE_ADDRESS)); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testDiscardFakeRetransmittedRequest() throws Exception { - setupIdleStateMachine(); - - // Mock last sent request and response - byte[] dummyIkeReqFirstPacket = "testDiscardFakeRetransmittedRequestReq".getBytes(); - byte[][] dummyIkeResp = new byte[][] {"testDiscardFakeRetransmittedRequestResp".getBytes()}; - mSpyCurrentIkeSaRecord.updateLastReceivedReqFirstPacket(dummyIkeReqFirstPacket); - mSpyCurrentIkeSaRecord.updateLastSentRespAllPackets(Arrays.asList(dummyIkeResp)); - mSpyCurrentIkeSaRecord.incrementRemoteRequestMessageId(); - - // Build request with last validated message ID but different bytes - ReceivedIkePacket request = - makeDpdIkeRequest( - mSpyCurrentIkeSaRecord.getRemoteRequestMessageId() - 1, new byte[0]); - - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, request); - - mLooper.dispatchAll(); - - verifyLastSentRespAllPackets(dummyIkeResp, mSpyCurrentIkeSaRecord); - verify(mSpyIkeSocket, never()).sendIkePacket(any(), any()); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } - - @Test - public void testDiscardRetransmittedResponse() throws Exception { - mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); - verifyRetransmissionStarted(); - - // Build and send fake response with last validated message ID to IKE state machine - ReceivedIkePacket resp = - makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, - true /*isResp*/, - mSpyCurrentIkeSaRecord.getLocalRequestMessageId() - 1, - new LinkedList<>(), - new byte[0]); - - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp); - mLooper.dispatchAll(); - - // Verify current state does not change - verifyRetransmissionStarted(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.CreateIkeLocalIkeAuth); - } - - @Test - public void testDeleteIkeLocalDeleteRequest() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Verify outbound message - IkeMessage delMsg = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = delMsg.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertFalse(ikeHeader.isResponseMsg); - assertTrue(ikeHeader.fromIkeInitiator); - - List<IkeDeletePayload> deletePayloadList = - delMsg.getPayloadListForType( - IkePayload.PAYLOAD_TYPE_DELETE, IkeDeletePayload.class); - assertEquals(1, deletePayloadList.size()); - - IkeDeletePayload deletePayload = deletePayloadList.get(0); - assertEquals(IkePayload.PROTOCOL_ID_IKE, deletePayload.protocolId); - assertEquals(0, deletePayload.numSpi); - assertEquals(0, deletePayload.spiSize); - assertArrayEquals(new int[0], deletePayload.spisToDelete); - } - - @Test - public void testDeleteIkeLocalDeleteResponse() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - ReceivedIkePacket received = makeDeleteIkeResponse(mSpyCurrentIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, received); - mLooper.dispatchAll(); - verifyIncrementLocaReqMsgId(); - - verifyNotifyUserCloseSession(); - - // Verify state machine quit properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testDeleteIkeLocalDeleteResponseWithParsingError() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - resetMockIkeMessageHelper(); - - // Mock receiving response with syntax error - ReceivedIkePacket mockInvalidPacket = - makeDummyReceivedIkePacketWithInvalidSyntax( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockInvalidPacket); - mLooper.dispatchAll(); - - // Verify no more request out - verifyEncryptAndEncodeNeverCalled(mSpyCurrentIkeSaRecord); - - // Verify state machine quit properly - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testDeleteIkeLocalDeleteHandlesInvalidResp() throws Exception { - setupIdleStateMachine(); - - // Send delete request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_IKE)); - mLooper.dispatchAll(); - - // Receive response with wrong exchange type - ReceivedIkePacket resp = - makeDummyReceivedIkePacketWithInvalidSyntax( - mSpyCurrentIkeSaRecord, - true /*isResp*/, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, resp); - mLooper.dispatchAll(); - - // Verify state machine quit properly - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testDeleteIkeLocalDeleteReceivedNonDeleteRequest() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_IKE)); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Verify delete sent out. - verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - resetMockIkeMessageHelper(); // Discard value. - - ReceivedIkePacket received = makeRekeyIkeRequest(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, received); - - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - verifyIncrementRemoteReqMsgId(); - - // Verify outbound response - IkeMessage resp = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = resp.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertTrue(ikeHeader.isResponseMsg); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - - List<IkeNotifyPayload> notificationPayloadList = - resp.getPayloadListForType(IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); - assertEquals(1, notificationPayloadList.size()); - - IkeNotifyPayload notifyPayload = notificationPayloadList.get(0); - assertEquals(IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE, notifyPayload.notifyType); - } - - @Test - public void testDeleteIkeRemoteDelete() throws Exception { - setupIdleStateMachine(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, - makeDeleteIkeRequest(mSpyCurrentIkeSaRecord)); - - mLooper.dispatchAll(); - verifyIncrementRemoteReqMsgId(); - - // Verify outbound message - IkeMessage delMsg = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - - IkeHeader ikeHeader = delMsg.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertTrue(ikeHeader.isResponseMsg); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - - assertTrue(delMsg.ikePayloadList.isEmpty()); - - verifyNotifyUserCloseSession(); - - // Verify state machine quit properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - @Test - public void testReceiveDpd() throws Exception { - setupIdleStateMachine(); - - // Receive a DPD request, expect to stay in IDLE state - ReceivedIkePacket dummyDpdRequest = makeDpdIkeRequest(mSpyCurrentIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDpdRequest); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDpdRequest); - - // Verify outbound response - IkeMessage resp = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - IkeHeader ikeHeader = resp.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertTrue(ikeHeader.isResponseMsg); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - assertTrue(resp.ikePayloadList.isEmpty()); - } - - @Test - public void testReceiveDpdNonIdle() throws Exception { - setupIdleStateMachine(); - - // Move to a non-idle state. Use RekeyIkeRemoteDelete, as it doesn't send out any requests. - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mRekeyIkeRemoteDelete); - mLooper.dispatchAll(); - - // In a rekey state, receiving (and handling) a DPD should not result in a change of states - ReceivedIkePacket dummyDpdRequest = makeDpdIkeRequest(mSpyCurrentIkeSaRecord); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDpdRequest); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeRemoteDelete); - - verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, dummyDpdRequest); - - // Verify outbound response - IkeMessage resp = verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - IkeHeader ikeHeader = resp.ikeHeader; - assertEquals(IkePayload.PAYLOAD_TYPE_SK, ikeHeader.nextPayloadType); - assertEquals(IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, ikeHeader.exchangeType); - assertTrue(ikeHeader.isResponseMsg); - assertEquals(mSpyCurrentIkeSaRecord.isLocalInit, ikeHeader.fromIkeInitiator); - assertTrue(resp.ikePayloadList.isEmpty()); - } - - @Test - public void testIdleTriggersNewRequests() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - - // Verify that the command is executed, and the state machine transitions to the right state - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - verifyRetransmissionStarted(); - } - - @Test - public void testNonIdleStateDoesNotTriggerNewRequests() throws Exception { - setupIdleStateMachine(); - - // Force ourselves into a non-idle state - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mReceiving); - mLooper.dispatchAll(); - verifyEncryptAndEncodeNeverCalled(); - - // Queue a local request, and expect that it is not run (yet) - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE, - new LocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); - mLooper.dispatchAll(); - - // Verify that the state machine is still in the Receiving state - verifyEncryptAndEncodeNeverCalled(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.Receiving); - - // Go back to Idle, and expect to immediately transition to RekeyIkeLocalCreate from the - // queued request - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - verifyEncryptAndEncodeAndGetMessage(mSpyCurrentIkeSaRecord); - } - - @Test - public void testOpenChildSessionValidatesArgs() throws Exception { - setupIdleStateMachine(); - - // Expect failure - no callbacks provided - try { - mIkeSessionStateMachine.openChildSession(mChildSessionOptions, null); - } catch (IllegalArgumentException expected) { - } - - // Expect failure - callbacks already registered - try { - mIkeSessionStateMachine.openChildSession( - mChildSessionOptions, mMockChildSessionCallback); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testOpenChildSession() throws Exception { - setupIdleStateMachine(); - - ChildSessionCallback cb = mock(ChildSessionCallback.class); - mIkeSessionStateMachine.openChildSession(mChildSessionOptions, cb); - - // Test that inserting the same cb returns an error, even before the state - // machine has a chance to process it. - try { - mIkeSessionStateMachine.openChildSession(mChildSessionOptions, cb); - } catch (IllegalArgumentException expected) { - } - - verify(mMockChildSessionFactoryHelper) - .makeChildSessionStateMachine( - eq(mLooper.getLooper()), - eq(mContext), - eq(mChildSessionOptions), - eq(mSpyUserCbExecutor), - eq(cb), - any()); - - // Verify state in IkeSessionStateMachine - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - - synchronized (mIkeSessionStateMachine.mChildCbToSessions) { - assertTrue(mIkeSessionStateMachine.mChildCbToSessions.containsKey(cb)); - } - } - - @Test - public void testCloseChildSessionValidatesArgs() throws Exception { - setupIdleStateMachine(); - - // Expect failure - callbacks not registered - try { - mIkeSessionStateMachine.closeChildSession(mock(ChildSessionCallback.class)); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testCloseChildSession() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.closeChildSession(mMockChildSessionCallback); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - } - - @Test - public void testCloseImmediatelyAfterOpenChildSession() throws Exception { - setupIdleStateMachine(); - - ChildSessionCallback cb = mock(ChildSessionCallback.class); - mIkeSessionStateMachine.openChildSession(mChildSessionOptions, cb); - - // Verify that closing the session immediately still picks up the child callback - // even before the looper has a chance to run. - mIkeSessionStateMachine.closeChildSession(mMockChildSessionCallback); - } - - @Test - public void testOnChildSessionClosed() throws Exception { - setupIdleStateMachine(); - - mDummyChildSmCallback.onChildSessionClosed(mMockChildSessionCallback); - - synchronized (mIkeSessionStateMachine.mChildCbToSessions) { - assertFalse( - mIkeSessionStateMachine.mChildCbToSessions.containsKey( - mMockChildSessionCallback)); - } - } - - @Test - public void testHandleUnexpectedExceptionInEnterState() throws Exception { - Log spyIkeLog = TestUtils.makeSpyLogDoLogErrorForWtf(TAG); - IkeManager.setIkeLog(spyIkeLog); - - IkeSessionOptions mockSessionOptions = mock(IkeSessionOptions.class); - when(mockSessionOptions.getSaProposals()).thenThrow(mock(RuntimeException.class)); - - IkeSessionStateMachine ikeSession = - new IkeSessionStateMachine( - mLooper.getLooper(), - mContext, - mIpSecManager, - mockSessionOptions, - mChildSessionOptions, - mSpyUserCbExecutor, - mMockIkeSessionCallback, - mMockChildSessionCallback, - mMockEapAuthenticatorFactory); - // Send IKE INIT request - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE); - mLooper.dispatchAll(); - - assertNull(ikeSession.getCurrentState()); - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(IkeInternalException.class)); - verify(spyIkeLog).wtf(anyString(), anyString(), any(RuntimeException.class)); - } - - @Test - public void testHandleUnexpectedExceptionInProcessStateMsg() throws Exception { - Log spyIkeLog = TestUtils.makeSpyLogDoLogErrorForWtf(TAG); - IkeManager.setIkeLog(spyIkeLog); - - setupIdleStateMachine(); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, null /*receivedIkePacket*/); - mLooper.dispatchAll(); - - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mSpyUserCbExecutor).execute(any(Runnable.class)); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(IkeInternalException.class)); - verify(spyIkeLog).wtf(anyString(), anyString(), any(RuntimeException.class)); - } - - @Test - public void testCreateIkeLocalIkeInitRcvErrorNotify() throws Exception { - // Send IKE INIT request - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE); - mLooper.dispatchAll(); - verifyRetransmissionStarted(); - - // Receive IKE INIT response with erro notification. - List<IkePayload> payloads = new LinkedList<>(); - payloads.add(new IkeNotifyPayload(IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN)); - ReceivedIkePacket resp = - makeDummyUnencryptedReceivedIkePacket( - 1L /*initiator SPI*/, - 2L /*respodner SPI*/, - IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT, - true /*isResp*/, - false /*fromIkeInit*/, - payloads); - - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp); - mLooper.dispatchAll(); - - // Fires user error callbacks - verify(mMockIkeSessionCallback) - .onClosedExceptionally( - argThat(err -> err instanceof NoValidProposalChosenException)); - // Verify state machine quit properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - } - - private void mockSendRekeyChildReq() throws Exception { - setupIdleStateMachine(); - - ChildLocalRequest childLocalRequest = - new ChildLocalRequest( - IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD, - mMockChildSessionCallback, - null /*childOptions*/); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, childLocalRequest); - mLooper.dispatchAll(); - - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verify(mMockChildSessionStateMachine).rekeyChildSession(); - - // Mocking sending request - mDummyChildSmCallback.onOutboundPayloadsReady( - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA, - false /*isResp*/, - new LinkedList<>(), - mMockChildSessionStateMachine); - mLooper.dispatchAll(); - } - - private void mockRcvTempFail() throws Exception { - ReceivedIkePacket resp = - makeResponseWithErrorNotify(new IkeNotifyPayload(ERROR_TYPE_TEMPORARY_FAILURE)); - - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); - mLooper.dispatchAll(); - } - - @Test - public void testTempFailureHandlerScheduleRetry() throws Exception { - mockSendRekeyChildReq(); - - // Mock sending TEMPORARY_FAILURE response - mockRcvTempFail(); - - // Move time forward to trigger retry - mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS); - mLooper.dispatchAll(); - - // Verify that rekey is triggered again - assertTrue( - mIkeSessionStateMachine.getCurrentState() - instanceof IkeSessionStateMachine.ChildProcedureOngoing); - verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession(); - } - - @Test - public void testTempFailureHandlerTimeout() throws Exception { - long currentTime = 0; - int retryCnt = 0; - - mockSendRekeyChildReq(); - - while (currentTime + RETRY_INTERVAL_MS < TEMP_FAILURE_RETRY_TIMEOUT_MS) { - mockRcvTempFail(); - - mLooper.moveTimeForward(RETRY_INTERVAL_MS); - currentTime += RETRY_INTERVAL_MS; - mLooper.dispatchAll(); - - retryCnt++; - verify(mMockChildSessionStateMachine, times(1 + retryCnt)).rekeyChildSession(); - } - - mLooper.moveTimeForward(RETRY_INTERVAL_MS); - mLooper.dispatchAll(); - - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(IkeInternalException.class)); - } - - @Test - public void testTempFailureHandlerCancelTimer() throws Exception { - mockSendRekeyChildReq(); - - // Mock sending TEMPORARY_FAILURE response - mockRcvTempFail(); - - // Move time forward to trigger retry - mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS); - mLooper.dispatchAll(); - verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession(); - - // Mock sending a valid response - ReceivedIkePacket resp = - makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - EXCHANGE_TYPE_CREATE_CHILD_SA, - true /*isResp*/, - new LinkedList<>()); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp); - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); - mLooper.dispatchAll(); - - // Move time forward - mLooper.moveTimeForward(IkeSessionStateMachine.TEMP_FAILURE_RETRY_TIMEOUT_MS); - mLooper.dispatchAll(); - - // Validate IKE Session is not closed - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - // Validate no more retry - verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession(); - } - - @Test - public void testIdleReceiveRequestWithFatalError() throws Exception { - setupIdleStateMachine(); - - // Mock receiving packet with syntax error - ReceivedIkePacket mockInvalidPacket = - makeDummyReceivedIkePacketWithInvalidSyntax( - mSpyCurrentIkeSaRecord, - false /*isResp*/, - IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA); - mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockInvalidPacket); - mLooper.dispatchAll(); - - // Verify Delete request was sent - List<IkePayload> payloads = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, payloads.size()); - - IkePayload payload = payloads.get(0); - assertEquals(IkePayload.PAYLOAD_TYPE_NOTIFY, payload.payloadType); - assertEquals(ERROR_TYPE_INVALID_SYNTAX, ((IkeNotifyPayload) payload).notifyType); - - // Verify IKE Session is closed properly - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - } - - @Test - public void testHandlesInvalidRequest() throws Exception { - setupIdleStateMachine(); - - mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, - mIkeSessionStateMachine.mChildProcedureOngoing); - - // Receive an IKE AUTH request - ReceivedIkePacket request = - makeDummyEncryptedReceivedIkePacketWithPayloadList( - mSpyCurrentIkeSaRecord, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - false /*isResp*/, - new LinkedList<IkePayload>()); - mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, request); - mLooper.dispatchAll(); - - // Verify error notification was sent - List<IkePayload> ikePayloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); - assertEquals(1, ikePayloadList.size()); - assertEquals( - ERROR_TYPE_INVALID_SYNTAX, ((IkeNotifyPayload) ikePayloadList.get(0)).notifyType); - - // Verify IKE Session has quit - assertNull(mIkeSessionStateMachine.getCurrentState()); - verify(mMockIkeSessionCallback).onClosedExceptionally(any(InvalidSyntaxException.class)); - } - - @Test - public void testIdleHandlesUnprotectedPacket() throws Exception { - setupIdleStateMachine(); - - ReceivedIkePacket req = - makeDummyReceivedIkePacketWithUnprotectedError( - mSpyCurrentIkeSaRecord, - false /*isResp*/, - EXCHANGE_TYPE_INFORMATIONAL, - mock(IkeException.class)); - - mLooper.dispatchAll(); - assertTrue( - mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java deleted file mode 100644 index 646b9d9a..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.aryEq; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecManager.SecurityParameterIndex; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.IpSecTransform; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest; -import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest; -import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IkeSecurityParameterIndex; -import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecord; -import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecordConfig; -import com.android.internal.net.ipsec.ike.SaRecord.IIpSecTransformHelper; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecord; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecordConfig; -import com.android.internal.net.ipsec.ike.SaRecord.IpSecTransformHelper; -import com.android.internal.net.ipsec.ike.SaRecord.SaRecordHelper; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils; -import com.android.server.IpSecService; - -import libcore.net.InetAddressUtils; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.net.Inet4Address; - -@RunWith(JUnit4.class) -public final class SaRecordTest { - private static final Inet4Address LOCAL_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private static final Inet4Address REMOTE_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - - private static final String PRF_KEY_HEX_STRING = "094787780EE466E2CB049FA327B43908BC57E485"; - private static final String DATA_TO_SIGN_HEX_STRING = "010000000a50500d"; - private static final String CALCULATED_MAC_HEX_STRING = - "D83B20CC6A0932B2A7CEF26E4020ABAAB64F0C6A"; - - private static final long IKE_INIT_SPI = 0x5F54BF6D8B48E6E1L; - private static final long IKE_RESP_SPI = 0x909232B3D1EDCB5CL; - - private static final String IKE_NONCE_INIT_HEX_STRING = - "C39B7F368F4681B89FA9B7BE6465ABD7C5F68B6ED5D3B4C72CB4240EB5C46412"; - private static final String IKE_NONCE_RESP_HEX_STRING = - "9756112CA539F5C25ABACC7EE92B73091942A9C06950F98848F1AF1694C4DDFF"; - - private static final String IKE_SHARED_DH_KEY_HEX_STRING = - "C14155DEA40056BD9C76FB4819687B7A397582F4CD5AFF4B" - + "8F441C56E0C08C84234147A0BA249A555835A048E3CA2980" - + "7D057A61DD26EEFAD9AF9C01497005E52858E29FB42EB849" - + "6731DF96A11CCE1F51137A9A1B900FA81AEE7898E373D4E4" - + "8B899BBECA091314ECD4B6E412EF4B0FEF798F54735F3180" - + "7424A318287F20E8"; - - private static final String IKE_SKEYSEED_HEX_STRING = - "8C42F3B1F5F81C7BAAC5F33E9A4F01987B2F9657"; - private static final String IKE_SK_D_HEX_STRING = "C86B56EFCF684DCC2877578AEF3137167FE0EBF6"; - private static final String IKE_SK_AUTH_INIT_HEX_STRING = - "554FBF5A05B7F511E05A30CE23D874DB9EF55E51"; - private static final String IKE_SK_AUTH_RESP_HEX_STRING = - "36D83420788337CA32ECAA46892C48808DCD58B1"; - private static final String IKE_SK_ENCR_INIT_HEX_STRING = "5CBFD33F75796C0188C4A3A546AEC4A1"; - private static final String IKE_SK_ENCR_RESP_HEX_STRING = "C33B35FCF29514CD9D8B4A695E1A816E"; - private static final String IKE_SK_PRF_INIT_HEX_STRING = - "094787780EE466E2CB049FA327B43908BC57E485"; - private static final String IKE_SK_PRF_RESP_HEX_STRING = - "A30E6B08BE56C0E6BFF4744143C75219299E1BEB"; - private static final String IKE_KEY_MAT = - IKE_SK_D_HEX_STRING - + IKE_SK_AUTH_INIT_HEX_STRING - + IKE_SK_AUTH_RESP_HEX_STRING - + IKE_SK_ENCR_INIT_HEX_STRING - + IKE_SK_ENCR_RESP_HEX_STRING - + IKE_SK_PRF_INIT_HEX_STRING - + IKE_SK_PRF_RESP_HEX_STRING; - - private static final int IKE_AUTH_ALGO_KEY_LEN = 20; - private static final int IKE_ENCR_ALGO_KEY_LEN = 16; - private static final int IKE_PRF_KEY_LEN = 20; - private static final int IKE_SK_D_KEY_LEN = IKE_PRF_KEY_LEN; - - private static final int FIRST_CHILD_INIT_SPI = 0x2ad4c0a2; - private static final int FIRST_CHILD_RESP_SPI = 0xcae7019f; - - private static final String FIRST_CHILD_ENCR_INIT_HEX_STRING = - "1B865CEA6E2C23973E8C5452ADC5CD7D"; - private static final String FIRST_CHILD_ENCR_RESP_HEX_STRING = - "5E82FEDACC6DCB0756DDD7553907EBD1"; - private static final String FIRST_CHILD_AUTH_INIT_HEX_STRING = - "A7A5A44F7EF4409657206C7DC52B7E692593B51E"; - private static final String FIRST_CHILD_AUTH_RESP_HEX_STRING = - "CDE612189FD46DE870FAEC04F92B40B0BFDBD9E1"; - private static final String FIRST_CHILD_KEY_MAT = - FIRST_CHILD_ENCR_INIT_HEX_STRING - + FIRST_CHILD_AUTH_INIT_HEX_STRING - + FIRST_CHILD_ENCR_RESP_HEX_STRING - + FIRST_CHILD_AUTH_RESP_HEX_STRING; - - private static final int FIRST_CHILD_AUTH_ALGO_KEY_LEN = 20; - private static final int FIRST_CHILD_ENCR_ALGO_KEY_LEN = 16; - - private IkeMacPrf mIkeHmacSha1Prf; - private IkeMacIntegrity mHmacSha1IntegrityMac; - private IkeCipher mAesCbcCipher; - - private LocalRequest mMockFutureRekeyIkeEvent; - private ChildLocalRequest mMockFutureRekeyChildEvent; - - private SaRecordHelper mSaRecordHelper = new SaRecordHelper(); - - @Before - public void setUp() throws Exception { - mIkeHmacSha1Prf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - mHmacSha1IntegrityMac = - IkeMacIntegrity.create( - new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96), - IkeMessage.getSecurityProvider()); - mAesCbcCipher = - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, - SaProposal.KEY_LEN_AES_128), - IkeMessage.getSecurityProvider()); - - mMockFutureRekeyIkeEvent = mock(LocalRequest.class); - mMockFutureRekeyChildEvent = mock(ChildLocalRequest.class); - } - - // Test generating keying material for making IKE SA. - @Test - public void testMakeIkeSaRecord() throws Exception { - byte[] sKeySeed = TestUtils.hexStringToByteArray(IKE_SKEYSEED_HEX_STRING); - byte[] nonceInit = TestUtils.hexStringToByteArray(IKE_NONCE_INIT_HEX_STRING); - byte[] nonceResp = TestUtils.hexStringToByteArray(IKE_NONCE_RESP_HEX_STRING); - - IkeSecurityParameterIndex ikeInitSpi = - IkeSecurityParameterIndex.allocateSecurityParameterIndex( - LOCAL_ADDRESS, IKE_INIT_SPI); - IkeSecurityParameterIndex ikeRespSpi = - IkeSecurityParameterIndex.allocateSecurityParameterIndex( - REMOTE_ADDRESS, IKE_RESP_SPI); - IkeSaRecordConfig ikeSaRecordConfig = - new IkeSaRecordConfig( - ikeInitSpi, - ikeRespSpi, - mIkeHmacSha1Prf, - IKE_AUTH_ALGO_KEY_LEN, - IKE_ENCR_ALGO_KEY_LEN, - true /*isLocalInit*/, - mMockFutureRekeyIkeEvent); - - int keyMaterialLen = - IKE_SK_D_KEY_LEN - + IKE_AUTH_ALGO_KEY_LEN * 2 - + IKE_ENCR_ALGO_KEY_LEN * 2 - + IKE_PRF_KEY_LEN * 2; - - IkeSaRecord ikeSaRecord = - mSaRecordHelper.makeIkeSaRecord(sKeySeed, nonceInit, nonceResp, ikeSaRecordConfig); - - assertTrue(ikeSaRecord.isLocalInit); - assertEquals(IKE_INIT_SPI, ikeSaRecord.getInitiatorSpi()); - assertEquals(IKE_RESP_SPI, ikeSaRecord.getResponderSpi()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING), ikeSaRecord.getSkD()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_AUTH_INIT_HEX_STRING), - ikeSaRecord.getOutboundIntegrityKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_AUTH_RESP_HEX_STRING), - ikeSaRecord.getInboundIntegrityKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_ENCR_INIT_HEX_STRING), - ikeSaRecord.getOutboundEncryptionKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_ENCR_RESP_HEX_STRING), - ikeSaRecord.getInboundDecryptionKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_PRF_INIT_HEX_STRING), ikeSaRecord.getSkPi()); - assertArrayEquals( - TestUtils.hexStringToByteArray(IKE_SK_PRF_RESP_HEX_STRING), ikeSaRecord.getSkPr()); - - ikeSaRecord.close(); - - verify(mMockFutureRekeyIkeEvent).cancel(); - } - - // Test generating keying material and building IpSecTransform for making Child SA. - @Test - public void testMakeChildSaRecord() throws Exception { - byte[] sharedKey = new byte[0]; - byte[] nonceInit = TestUtils.hexStringToByteArray(IKE_NONCE_INIT_HEX_STRING); - byte[] nonceResp = TestUtils.hexStringToByteArray(IKE_NONCE_RESP_HEX_STRING); - - MockIpSecTestUtils mockIpSecTestUtils = MockIpSecTestUtils.setUpMockIpSec(); - IpSecManager ipSecManager = mockIpSecTestUtils.getIpSecManager(); - IpSecService mockIpSecService = mockIpSecTestUtils.getIpSecService(); - Context context = mockIpSecTestUtils.getContext(); - - when(mockIpSecService.allocateSecurityParameterIndex( - eq(LOCAL_ADDRESS.getHostAddress()), anyInt(), anyObject())) - .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(FIRST_CHILD_INIT_SPI)); - when(mockIpSecService.allocateSecurityParameterIndex( - eq(REMOTE_ADDRESS.getHostAddress()), anyInt(), anyObject())) - .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(FIRST_CHILD_RESP_SPI)); - - SecurityParameterIndex childInitSpi = - ipSecManager.allocateSecurityParameterIndex(LOCAL_ADDRESS); - SecurityParameterIndex childRespSpi = - ipSecManager.allocateSecurityParameterIndex(REMOTE_ADDRESS); - - byte[] initAuthKey = TestUtils.hexStringToByteArray(FIRST_CHILD_AUTH_INIT_HEX_STRING); - byte[] respAuthKey = TestUtils.hexStringToByteArray(FIRST_CHILD_AUTH_RESP_HEX_STRING); - byte[] initEncryptionKey = TestUtils.hexStringToByteArray(FIRST_CHILD_ENCR_INIT_HEX_STRING); - byte[] respEncryptionKey = TestUtils.hexStringToByteArray(FIRST_CHILD_ENCR_RESP_HEX_STRING); - - IIpSecTransformHelper mockIpSecHelper; - mockIpSecHelper = mock(IIpSecTransformHelper.class); - SaRecord.setIpSecTransformHelper(mockIpSecHelper); - - IpSecTransform mockInTransform = mock(IpSecTransform.class); - IpSecTransform mockOutTransform = mock(IpSecTransform.class); - UdpEncapsulationSocket mockUdpEncapSocket = mock(UdpEncapsulationSocket.class); - - when(mockIpSecHelper.makeIpSecTransform( - eq(context), - eq(LOCAL_ADDRESS), - eq(mockUdpEncapSocket), - eq(childRespSpi), - eq(mHmacSha1IntegrityMac), - eq(mAesCbcCipher), - aryEq(initAuthKey), - aryEq(initEncryptionKey), - eq(false))) - .thenReturn(mockOutTransform); - - when(mockIpSecHelper.makeIpSecTransform( - eq(context), - eq(REMOTE_ADDRESS), - eq(mockUdpEncapSocket), - eq(childInitSpi), - eq(mHmacSha1IntegrityMac), - eq(mAesCbcCipher), - aryEq(respAuthKey), - aryEq(respEncryptionKey), - eq(false))) - .thenReturn(mockInTransform); - - ChildSaRecordConfig childSaRecordConfig = - new ChildSaRecordConfig( - mockIpSecTestUtils.getContext(), - childInitSpi, - childRespSpi, - LOCAL_ADDRESS, - REMOTE_ADDRESS, - mockUdpEncapSocket, - mIkeHmacSha1Prf, - mHmacSha1IntegrityMac, - mAesCbcCipher, - TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING), - false /*isTransport*/, - true /*isLocalInit*/, - mMockFutureRekeyChildEvent); - - ChildSaRecord childSaRecord = - mSaRecordHelper.makeChildSaRecord( - sharedKey, nonceInit, nonceResp, childSaRecordConfig); - - assertTrue(childSaRecord.isLocalInit); - assertEquals(FIRST_CHILD_INIT_SPI, childSaRecord.getLocalSpi()); - assertEquals(FIRST_CHILD_RESP_SPI, childSaRecord.getRemoteSpi()); - assertEquals(mockInTransform, childSaRecord.getInboundIpSecTransform()); - assertEquals(mockOutTransform, childSaRecord.getOutboundIpSecTransform()); - - assertArrayEquals( - TestUtils.hexStringToByteArray(FIRST_CHILD_AUTH_INIT_HEX_STRING), - childSaRecord.getOutboundIntegrityKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(FIRST_CHILD_AUTH_RESP_HEX_STRING), - childSaRecord.getInboundIntegrityKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(FIRST_CHILD_ENCR_INIT_HEX_STRING), - childSaRecord.getOutboundEncryptionKey()); - assertArrayEquals( - TestUtils.hexStringToByteArray(FIRST_CHILD_ENCR_RESP_HEX_STRING), - childSaRecord.getInboundDecryptionKey()); - - childSaRecord.close(); - verify(mMockFutureRekeyChildEvent).cancel(); - - SaRecord.setIpSecTransformHelper(new IpSecTransformHelper()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCombinedModeCipherTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCombinedModeCipherTest.java deleted file mode 100644 index a3b2253e..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeCombinedModeCipherTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; -import java.util.Random; - -import javax.crypto.AEADBadTagException; - -@RunWith(JUnit4.class) -public final class IkeCombinedModeCipherTest { - private static final String IV = "fbd69d9de2dafc5e"; - private static final String ENCRYPTED_PADDED_DATA_WITH_CHECKSUM = - "f4109834e9f3559758c05edf119917521b885f67f0d14ced43"; - private static final String UNENCRYPTED_PADDED_DATA = "000000080000400f00"; - private static final String ADDITIONAL_AUTH_DATA = - "77c708b4523e39a471dc683c1d4f21362e202508000000060000004129000025"; - private static final String KEY = - "7C04513660DEC572D896105254EF92608054F8E6EE19E79CE52AB8697B2B5F2C2AA90C29"; - - private static final int AES_GCM_IV_LEN = 8; - private static final int AES_GCM_16_CHECKSUM_LEN = 128; - - private IkeCombinedModeCipher mAesGcm16Cipher; - - private byte[] mAesGcmKey; - private byte[] mIv; - private byte[] mEncryptedPaddedDataWithChecksum; - private byte[] mUnencryptedPaddedData; - private byte[] mAdditionalAuthData; - - @Before - public void setUp() { - mAesGcm16Cipher = - (IkeCombinedModeCipher) - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, - SaProposal.KEY_LEN_AES_256), - IkeMessage.getSecurityProvider()); - - mAesGcmKey = TestUtils.hexStringToByteArray(KEY); - mIv = TestUtils.hexStringToByteArray(IV); - mEncryptedPaddedDataWithChecksum = - TestUtils.hexStringToByteArray(ENCRYPTED_PADDED_DATA_WITH_CHECKSUM); - mUnencryptedPaddedData = TestUtils.hexStringToByteArray(UNENCRYPTED_PADDED_DATA); - mAdditionalAuthData = TestUtils.hexStringToByteArray(ADDITIONAL_AUTH_DATA); - } - - @Test - public void testBuild() throws Exception { - assertTrue(mAesGcm16Cipher.isAead()); - assertEquals(AES_GCM_IV_LEN, mAesGcm16Cipher.generateIv().length); - } - - @Test - public void testGenerateRandomIv() throws Exception { - assertFalse(Arrays.equals(mAesGcm16Cipher.generateIv(), mAesGcm16Cipher.generateIv())); - } - - @Test - public void testEncrypt() throws Exception { - byte[] calculatedData = - mAesGcm16Cipher.encrypt( - mUnencryptedPaddedData, mAdditionalAuthData, mAesGcmKey, mIv); - - assertArrayEquals(mEncryptedPaddedDataWithChecksum, calculatedData); - } - - @Test - public void testDecrypt() throws Exception { - byte[] calculatedData = - mAesGcm16Cipher.decrypt( - mEncryptedPaddedDataWithChecksum, mAdditionalAuthData, mAesGcmKey, mIv); - - assertArrayEquals(mUnencryptedPaddedData, calculatedData); - } - - @Test - public void testEncryptWithWrongKeyLen() throws Exception { - byte[] encryptionKey = TestUtils.hexStringToByteArray(KEY + "00"); - - try { - mAesGcm16Cipher.encrypt( - mUnencryptedPaddedData, mAdditionalAuthData, encryptionKey, mIv); - fail("Expected to fail because encryption key has wrong length."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testDecrypWithWrongKey() throws Exception { - byte[] encryptionKey = new byte[mAesGcmKey.length]; - new Random().nextBytes(encryptionKey); - - try { - mAesGcm16Cipher.decrypt( - mEncryptedPaddedDataWithChecksum, mAdditionalAuthData, encryptionKey, mIv); - fail("Expected to fail because decryption key is wrong"); - } catch (AEADBadTagException expected) { - - } - } - - @Test - public void testBuildIpSecAlgorithm() throws Exception { - IpSecAlgorithm ipsecAlgorithm = mAesGcm16Cipher.buildIpSecAlgorithmWithKey(mAesGcmKey); - - IpSecAlgorithm expectedIpSecAlgorithm = - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_CRYPT_AES_GCM, mAesGcmKey, AES_GCM_16_CHECKSUM_LEN); - - assertTrue(IpSecAlgorithm.equals(expectedIpSecAlgorithm, ipsecAlgorithm)); - } - - @Test - public void buildIpSecAlgorithmWithInvalidKey() throws Exception { - byte[] encryptionKey = TestUtils.hexStringToByteArray(KEY + "00"); - - try { - mAesGcm16Cipher.buildIpSecAlgorithmWithKey(encryptionKey); - fail("Expected to fail because encryption key has wrong length."); - } catch (IllegalArgumentException expected) { - - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacIntegrityTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacIntegrityTest.java deleted file mode 100644 index ed625660..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeMacIntegrityTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; - -@RunWith(JUnit4.class) -public final class IkeMacIntegrityTest { - private static final String DATA_TO_AUTH_HEX_STRING = - "5f54bf6d8b48e6e1909232b3d1edcb5c2e20230800000001000000ec" - + "230000d0b9132b7bb9f658dfdc648e5017a6322a030c316c" - + "e55f365760d46426ce5cfc78bd1ed9abff63eb9594c1bd58" - + "46de333ecd3ea2b705d18293b130395300ba92a351041345" - + "0a10525cea51b2753b4e92b081fd78d995659a98f742278f" - + "f9b8fd3e21554865c15c79a5134d66b2744966089e416c60" - + "a274e44a9a3f084eb02f3bdce1e7de9de8d9a62773ab563b" - + "9a69ba1db03c752acb6136452b8a86c41addb4210d68c423" - + "efed80e26edca5fa3fe5d0a5ca9375ce332c474b93fb1fa3" - + "59eb4e81"; - private static final String INTEGRITY_KEY_HEX_STRING = - "554fbf5a05b7f511e05a30ce23d874db9ef55e51"; - private static final String CHECKSUM_HEX_STRING = "ae6e0f22abdad69ba8007d50"; - - private IkeMacIntegrity mHmacSha1IntegrityMac; - private byte[] mHmacSha1IntegrityKey; - - private byte[] mDataToAuthenticate; - - @Before - public void setUp() throws Exception { - mHmacSha1IntegrityMac = - IkeMacIntegrity.create( - new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96), - IkeMessage.getSecurityProvider()); - mHmacSha1IntegrityKey = TestUtils.hexStringToByteArray(INTEGRITY_KEY_HEX_STRING); - - mDataToAuthenticate = TestUtils.hexStringToByteArray(DATA_TO_AUTH_HEX_STRING); - } - - @Test - public void testGenerateChecksum() throws Exception { - byte[] calculatedChecksum = - mHmacSha1IntegrityMac.generateChecksum(mHmacSha1IntegrityKey, mDataToAuthenticate); - - byte[] expectedChecksum = TestUtils.hexStringToByteArray(CHECKSUM_HEX_STRING); - assertArrayEquals(expectedChecksum, calculatedChecksum); - } - - @Test - public void testGenerateChecksumWithDifferentKey() throws Exception { - byte[] integrityKey = mHmacSha1IntegrityKey.clone(); - integrityKey[0]++; - - byte[] calculatedChecksum = - mHmacSha1IntegrityMac.generateChecksum(integrityKey, mDataToAuthenticate); - - byte[] expectedChecksum = TestUtils.hexStringToByteArray(CHECKSUM_HEX_STRING); - assertFalse(Arrays.equals(expectedChecksum, calculatedChecksum)); - } - - @Test - public void testGenerateChecksumWithInvalidKey() throws Exception { - byte[] integrityKey = TestUtils.hexStringToByteArray(INTEGRITY_KEY_HEX_STRING + "0000"); - - try { - byte[] calculatedChecksum = - mHmacSha1IntegrityMac.generateChecksum(integrityKey, mDataToAuthenticate); - fail("Expected to fail due to invalid authentication key."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testBuildIpSecAlgorithm() throws Exception { - IpSecAlgorithm ipsecAlgorithm = - mHmacSha1IntegrityMac.buildIpSecAlgorithmWithKey(mHmacSha1IntegrityKey); - - IpSecAlgorithm expectedIpSecAlgorithm = - new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, mHmacSha1IntegrityKey, 96); - - assertTrue(IpSecAlgorithm.equals(expectedIpSecAlgorithm, ipsecAlgorithm)); - } - - @Test - public void buildIpSecAlgorithmWithInvalidKey() throws Exception { - byte[] encryptionKey = TestUtils.hexStringToByteArray(INTEGRITY_KEY_HEX_STRING + "00"); - - try { - mHmacSha1IntegrityMac.buildIpSecAlgorithmWithKey(encryptionKey); - - fail("Expected to fail due to integrity key with wrong length."); - } catch (IllegalArgumentException expected) { - - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeNormalModeCipherTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeNormalModeCipherTest.java deleted file mode 100644 index 3f3a0e10..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/crypto/IkeNormalModeCipherTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.crypto; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.IpSecAlgorithm; -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; - -import javax.crypto.IllegalBlockSizeException; - -@RunWith(JUnit4.class) -public final class IkeNormalModeCipherTest { - private static final String IKE_AUTH_INIT_REQUEST_IV = "b9132b7bb9f658dfdc648e5017a6322a"; - private static final String IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA = - "030c316ce55f365760d46426ce5cfc78bd1ed9abff63eb9594c1bd58" - + "46de333ecd3ea2b705d18293b130395300ba92a351041345" - + "0a10525cea51b2753b4e92b081fd78d995659a98f742278f" - + "f9b8fd3e21554865c15c79a5134d66b2744966089e416c60" - + "a274e44a9a3f084eb02f3bdce1e7de9de8d9a62773ab563b" - + "9a69ba1db03c752acb6136452b8a86c41addb4210d68c423" - + "efed80e26edca5fa3fe5d0a5ca9375ce332c474b93fb1fa3" - + "59eb4e81"; - private static final String IKE_AUTH_INIT_REQUEST_UNENCRYPTED_PADDED_DATA = - "2400000c010000000a50500d2700000c010000000a505050" - + "2100001c02000000df7c038aefaaa32d3f44b228b52a3327" - + "44dfb2c12c00002c00000028010304032ad4c0a20300000c" - + "0100000c800e008003000008030000020000000805000000" - + "2d00001801000000070000100000ffff00000000ffffffff" - + "2900001801000000070000100000ffff00000000ffffffff" - + "29000008000040000000000c000040010000000100000000" - + "000000000000000b"; - - private static final String ENCR_KEY_FROM_INIT_TO_RESP = "5cbfd33f75796c0188c4a3a546aec4a1"; - - private static final int AES_BLOCK_SIZE = 16; - - private IkeNormalModeCipher mAesCbcCipher; - private byte[] mAesCbcKey; - - private byte[] mIv; - private byte[] mEncryptedPaddedData; - private byte[] mUnencryptedPaddedData; - - @Before - public void setUp() throws Exception { - mAesCbcCipher = - (IkeNormalModeCipher) - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, - SaProposal.KEY_LEN_AES_128), - IkeMessage.getSecurityProvider()); - mAesCbcKey = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP); - - mIv = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_IV); - mEncryptedPaddedData = - TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA); - mUnencryptedPaddedData = - TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_UNENCRYPTED_PADDED_DATA); - } - - @Test - public void testBuild() throws Exception { - assertFalse(mAesCbcCipher.isAead()); - assertEquals(AES_BLOCK_SIZE, mAesCbcCipher.getBlockSize()); - assertEquals(AES_BLOCK_SIZE, mAesCbcCipher.generateIv().length); - } - - @Test - public void testGenerateRandomIv() throws Exception { - assertFalse(Arrays.equals(mAesCbcCipher.generateIv(), mAesCbcCipher.generateIv())); - } - - @Test - public void testEncryptWithNormalCipher() throws Exception { - byte[] calculatedData = mAesCbcCipher.encrypt(mUnencryptedPaddedData, mAesCbcKey, mIv); - - assertArrayEquals(mEncryptedPaddedData, calculatedData); - } - - @Test - public void testDecryptWithNormalCipher() throws Exception { - byte[] calculatedData = mAesCbcCipher.decrypt(mEncryptedPaddedData, mAesCbcKey, mIv); - assertArrayEquals(mUnencryptedPaddedData, calculatedData); - } - - @Test - public void testEncryptWithWrongKey() throws Exception { - byte[] encryptionKey = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP + "00"); - - try { - mAesCbcCipher.encrypt(mEncryptedPaddedData, encryptionKey, mIv); - fail("Expected to fail due to encryption key with wrong length."); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testDecryptWithNormalCipherWithBadPad() throws Exception { - byte[] dataToDecrypt = - TestUtils.hexStringToByteArray( - IKE_AUTH_INIT_REQUEST_UNENCRYPTED_PADDED_DATA + "00"); - try { - mAesCbcCipher.decrypt(dataToDecrypt, mAesCbcKey, mIv); - fail("Expected to fail when try to decrypt data with bad padding"); - } catch (IllegalBlockSizeException expected) { - - } - } - - @Test - public void testBuildIpSecAlgorithm() throws Exception { - IpSecAlgorithm ipsecAlgorithm = mAesCbcCipher.buildIpSecAlgorithmWithKey(mAesCbcKey); - - IpSecAlgorithm expectedIpSecAlgorithm = - new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, mAesCbcKey); - - assertTrue(IpSecAlgorithm.equals(expectedIpSecAlgorithm, ipsecAlgorithm)); - } - - @Test - public void buildIpSecAlgorithmWithInvalidKey() throws Exception { - byte[] encryptionKey = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP + "00"); - - try { - mAesCbcCipher.buildIpSecAlgorithmWithKey(encryptionKey); - - fail("Expected to fail due to encryption key with wrong length."); - } catch (IllegalArgumentException expected) { - - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java deleted file mode 100644 index 96921c30..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static com.android.internal.net.ipsec.ike.message.IkeAuthDigitalSignPayload.SIGNATURE_ALGO_RSA_SHA2_256; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; -import com.android.internal.net.ipsec.ike.testutils.CertUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -public final class IkeAuthDigitalSignPayloadTest { - // TODO: Build a RSA_SHA1 signature and add tests for it. - - // RSA_SHA2_256 - private static final String AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING = - "0e0000000f300d06092a864886f70d01010b05006f76af4150d653c5d4136b9f" - + "69d905849bf075c563e6d14ccda42361ec3e7d12c72e2dece5711ea1d952f7b8" - + "e12c5d982aa4efdaeac36a02b222aa96242cc424"; - private static final String SIGNATURE = - "6f76af4150d653c5d4136b9f69d905849bf075c563e6d14ccda42361ec3e7d12" - + "c72e2dece5711ea1d952f7b8e12c5d982aa4efdaeac36a02b222aa96242cc424"; - - private static final String IKE_INIT_RESP_HEX_STRING = - "02458497587b09d488d5b76480bce53d2120222000000000000001cc2200002c" - + "00000028010100040300000801000003030000080300000203000008020000020" - + "00000080400000e28000108000e000013d60e51c40922cb121e395bacbd627cdd" - + "d3240baa4fcefd29f65f8dd37329d68d4fb4854f8b8f07cfb60900e276d99a396" - + "1112ee866b5456cf588dc1092fd3bc19668fb8fa42872f51c0ee748bdb665dcbe" - + "15ac454f6ed966149954dac5187638d1ab61869d97a4873c4733c48cbe3acc8a6" - + "5cfea3ce83fd09fba174bf0ec56d73a0585859399e61c2c38e695841f8df8a511" - + "aadd438f56634165ad9b88e858c1585f1bee646943b8a96f5397721079a127b87" - + "fd286e8f869ae021ce82adf91fa360217ac32268b39b698bf06a4e89b8d0267af" - + "1c5b979b6493adb10a0e14aa707309e914b8d377903e75cb13cffbfde9c26842f" - + "b49a07a4497c9907d39515b290000244b8aed6297c09a5a0dda06c873f5573b34" - + "886dd779e90c19beca3fc54ab3cae02900001c00004004d8e7cb9d1e689ae8c84" - + "c5078355436f3347376ff2900001c0000400545bc3f2113770de91c769094f1bd" - + "614534e765ea290000080000402e290000100000402f000100020003000400000" - + "00800004014"; - private static final String NONCE_INIT_HEX_STRING = - "a5dded450b5ffd2670f37954367fce28279a085c830a03358b10b0872c0578f9"; - private static final String ID_RESP_PAYLOAD_BODY_HEX_STRING = "01000000c0a82b8a"; - private static final String SKP_RESP_HEX_STRING = "8FE8EC3153EDE924C23D6630D3C992A494E2F256"; - - private static final byte[] IKE_INIT_RESP_REQUEST = - TestUtils.hexStringToByteArray(IKE_INIT_RESP_HEX_STRING); - private static final byte[] NONCE_INIT_RESP = - TestUtils.hexStringToByteArray(NONCE_INIT_HEX_STRING); - private static final byte[] ID_RESP_PAYLOAD_BODY = - TestUtils.hexStringToByteArray(ID_RESP_PAYLOAD_BODY_HEX_STRING); - private static final byte[] PRF_RESP_KEY = TestUtils.hexStringToByteArray(SKP_RESP_HEX_STRING); - - private IkeMacPrf mIkeHmacSha1Prf; - - @Before - public void setUp() throws Exception { - mIkeHmacSha1Prf = - IkeMacPrf.create( - new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1), - IkeMessage.getSecurityProvider()); - } - - @Test - public void testDecodeGenericDigitalSignPayload() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING); - IkeAuthPayload payload = IkeAuthPayload.getIkeAuthPayload(false, inputPacket); - - assertTrue(payload instanceof IkeAuthDigitalSignPayload); - IkeAuthDigitalSignPayload dsPayload = (IkeAuthDigitalSignPayload) payload; - assertEquals(SIGNATURE_ALGO_RSA_SHA2_256, dsPayload.signatureAlgoAndHash); - assertArrayEquals(dsPayload.signature, TestUtils.hexStringToByteArray(SIGNATURE)); - } - - @Test - public void testVerifyInboundSignature() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING); - IkeAuthDigitalSignPayload payload = - (IkeAuthDigitalSignPayload) IkeAuthPayload.getIkeAuthPayload(false, inputPacket); - - X509Certificate cert = CertUtils.createCertFromPemFile("end-cert-small.pem"); - - payload.verifyInboundSignature( - cert, - IKE_INIT_RESP_REQUEST, - NONCE_INIT_RESP, - ID_RESP_PAYLOAD_BODY, - mIkeHmacSha1Prf, - PRF_RESP_KEY); - } - - @Test - public void testVerifyInboundSignatureFail() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING); - IkeAuthDigitalSignPayload payload = - (IkeAuthDigitalSignPayload) IkeAuthPayload.getIkeAuthPayload(false, inputPacket); - - assertArrayEquals(payload.signature, TestUtils.hexStringToByteArray(SIGNATURE)); - X509Certificate cert = CertUtils.createCertFromPemFile("end-cert-a.pem"); - - try { - payload.verifyInboundSignature( - cert, - IKE_INIT_RESP_REQUEST, - NONCE_INIT_RESP, - ID_RESP_PAYLOAD_BODY, - mIkeHmacSha1Prf, - PRF_RESP_KEY); - fail("Expected to fail due to wrong certificate."); - } catch (AuthenticationFailedException expected) { - } - } - - @Test - public void testGenerateSignature() throws Exception { - PrivateKey key = CertUtils.createRsaPrivateKeyFromKeyFile("end-cert-key-a.key"); - - IkeAuthDigitalSignPayload authPayload = - new IkeAuthDigitalSignPayload( - SIGNATURE_ALGO_RSA_SHA2_256, - key, - IKE_INIT_RESP_REQUEST, - NONCE_INIT_RESP, - ID_RESP_PAYLOAD_BODY, - mIkeHmacSha1Prf, - PRF_RESP_KEY); - - assertEquals(SIGNATURE_ALGO_RSA_SHA2_256, authPayload.signatureAlgoAndHash); - assertArrayEquals(authPayload.signature, TestUtils.hexStringToByteArray(SIGNATURE)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayloadTest.java deleted file mode 100644 index 2bb72e33..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayloadTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static org.junit.Assert.fail; - -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.testutils.CertUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.security.cert.TrustAnchor; -import java.security.cert.X509Certificate; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -public final class IkeCertPayloadTest { - private X509Certificate mEndCertA; - private X509Certificate mEndCertB; - private X509Certificate mEndCertSmall; - - private X509Certificate mIntermediateCertBOne; - private X509Certificate mIntermediateCertBTwo; - - private TrustAnchor mTrustAnchorA; - private TrustAnchor mTrustAnchorB; - private TrustAnchor mTrustAnchorSmall; - - @Before - public void setUp() throws Exception { - mEndCertA = CertUtils.createCertFromPemFile("end-cert-a.pem"); - mTrustAnchorA = - new TrustAnchor( - CertUtils.createCertFromPemFile("self-signed-ca-a.pem"), - null /*nameConstraints*/); - - mEndCertB = CertUtils.createCertFromPemFile("end-cert-b.pem"); - mIntermediateCertBOne = CertUtils.createCertFromPemFile("intermediate-ca-b-one.pem"); - mIntermediateCertBTwo = CertUtils.createCertFromPemFile("intermediate-ca-b-two.pem"); - mTrustAnchorB = - new TrustAnchor( - CertUtils.createCertFromPemFile("self-signed-ca-b.pem"), - null /*nameConstraints*/); - - mEndCertSmall = CertUtils.createCertFromPemFile("end-cert-small.pem"); - mTrustAnchorSmall = - new TrustAnchor( - CertUtils.createCertFromPemFile("self-signed-ca-small.pem"), - null /*nameConstraints*/); - } - - @Test - public void testValidateCertsNoIntermediateCerts() throws Exception { - List<X509Certificate> certList = new LinkedList<>(); - certList.add(mEndCertA); - - Set<TrustAnchor> trustAnchors = new HashSet<>(); - trustAnchors.add(mTrustAnchorA); - - IkeCertPayload.validateCertificates(mEndCertA, certList, null /*crlList*/, trustAnchors); - } - - @Test - public void testValidateCertsWithIntermediateCerts() throws Exception { - List<X509Certificate> certList = new LinkedList<>(); - - certList.add(mEndCertB); - certList.add(mIntermediateCertBTwo); - certList.add(mIntermediateCertBOne); - - Set<TrustAnchor> trustAnchors = new HashSet<>(); - trustAnchors.add(mTrustAnchorB); - - IkeCertPayload.validateCertificates(mEndCertB, certList, null /*crlList*/, trustAnchors); - } - - @Test - public void testValidateCertsWithMultiTrustAnchors() throws Exception { - List<X509Certificate> certList = new LinkedList<>(); - certList.add(mEndCertA); - - Set<TrustAnchor> trustAnchors = new HashSet<>(); - trustAnchors.add(mTrustAnchorA); - trustAnchors.add(mTrustAnchorB); - - IkeCertPayload.validateCertificates(mEndCertA, certList, null /*crlList*/, trustAnchors); - } - - @Test - public void testValidateCertsWithWrongTrustAnchor() throws Exception { - List<X509Certificate> certList = new LinkedList<>(); - certList.add(mEndCertA); - - Set<TrustAnchor> trustAnchors = new HashSet<>(); - trustAnchors.add(mTrustAnchorB); - - try { - IkeCertPayload.validateCertificates( - mEndCertA, certList, null /*crlList*/, trustAnchors); - fail("Expected to fail due to absence of valid trust anchor."); - } catch (AuthenticationFailedException expected) { - } - } - - @Test - public void testValidateCertsWithMissingIntermediateCerts() throws Exception { - List<X509Certificate> certList = new LinkedList<>(); - certList.add(mEndCertB); - certList.add(mIntermediateCertBOne); - - Set<TrustAnchor> trustAnchors = new HashSet<>(); - trustAnchors.add(mTrustAnchorB); - - try { - IkeCertPayload.validateCertificates( - mEndCertA, certList, null /*crlList*/, trustAnchors); - fail("Expected to fail due to absence of intermediate certificate."); - } catch (AuthenticationFailedException expected) { - } - } - - @Test - public void testValidateCertsWithSmallSizeKey() throws Exception { - List<X509Certificate> certList = new LinkedList<>(); - certList.add(mEndCertSmall); - - Set<TrustAnchor> trustAnchors = new HashSet<>(); - trustAnchors.add(mTrustAnchorSmall); - - try { - IkeCertPayload.validateCertificates( - mEndCertSmall, certList, null /*crlList*/, trustAnchors); - fail("Expected to fail because certificates use small size key"); - } catch (AuthenticationFailedException expected) { - } - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayloadTest.java deleted file mode 100644 index 9fc32229..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayloadTest.java +++ /dev/null @@ -1,631 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_ADDRESS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_DHCP; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_DNS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_NETMASK; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_SUBNET; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_ADDRESS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_DNS; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_SUBNET; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_TYPE_REPLY; -import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_TYPE_REQUEST; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.net.LinkAddress; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttrIpv4AddressBase; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttrIpv6AddrRangeBase; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Dhcp; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Dns; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Subnet; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Address; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Dns; -import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Subnet; - -import libcore.net.InetAddressUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.util.LinkedList; -import java.util.List; - -public final class IkeConfigPayloadTest { - private static final String CONFIG_REQ_PAYLOAD_HEX = - "2900001801000000000100000008000000030000000a0000"; - private static final String CONFIG_RESP_PAYLOAD_HEX = - "210000200200000000010004c000026400030004080808080003000408080404"; - private static final String CONFIG_RESP_PAYLOAD_INVALID_ONE_HEX = - "210000200200000000010004c000026400020004fffffffe00020004fffffffe"; - private static final String CONFIG_RESP_PAYLOAD_INVALID_TWO_HEX = - "210000100200000000020004fffffffe"; - - private static final byte[] CONFIG_REQ_PAYLOAD = - TestUtils.hexStringToByteArray(CONFIG_REQ_PAYLOAD_HEX); - private static final byte[] CONFIG_RESP_PAYLOAD = - TestUtils.hexStringToByteArray(CONFIG_RESP_PAYLOAD_HEX); - - private static final Inet4Address IPV4_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - private static final Inet4Address IPV4_NETMASK = - (Inet4Address) (InetAddressUtils.parseNumericAddress("255.255.255.240")); - private static final int IP4_PREFIX_LEN = 28; - private static final LinkAddress IPV4_LINK_ADDRESS = - new LinkAddress(IPV4_ADDRESS, IP4_PREFIX_LEN); - - private static final byte[] IPV4_ADDRESS_ATTRIBUTE_WITH_VALUE = - TestUtils.hexStringToByteArray("00010004c0000264"); - private static final byte[] IPV4_ADDRESS_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("00010000"); - - private static final byte[] IPV4_NETMASK_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("00020000"); - - private static final Inet4Address IPV4_DNS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("8.8.8.8")); - private static final byte[] IPV4_DNS_ATTRIBUTE_VALUE = - TestUtils.hexStringToByteArray("08080808"); - private static final byte[] IPV4_DNS_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("00030000"); - - private static final Inet4Address IPV4_DHCP = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")); - private static final byte[] IPV4_DHCP_ATTRIBUTE_WITH_VALUE = - TestUtils.hexStringToByteArray("00060004c00002c8"); - private static final byte[] IPV4_DHCP_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("00060000"); - - private static final byte[] IPV4_SUBNET_ATTRIBUTE_VALUE = - TestUtils.hexStringToByteArray("c0000264fffffff0"); - private static final byte[] IPV4_SUBNET_ATTRIBUTE_WITH_VALUE = - TestUtils.hexStringToByteArray("000d0008c0000264fffffff0"); - private static final byte[] IPV4_SUBNET_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("000d0000"); - - private static final Inet6Address IPV6_ADDRESS = - (Inet6Address) (InetAddressUtils.parseNumericAddress("2001:db8::1")); - private static final int IP6_PREFIX_LEN = 64; - private static final LinkAddress IPV6_LINK_ADDRESS = - new LinkAddress(IPV6_ADDRESS, IP6_PREFIX_LEN); - - private static final byte[] IPV6_ADDRESS_ATTRIBUTE_VALUE = - TestUtils.hexStringToByteArray("20010db800000000000000000000000140"); - private static final byte[] IPV6_ADDRESS_ATTRIBUTE_WITH_VALUE = - TestUtils.hexStringToByteArray("0008001120010db800000000000000000000000140"); - private static final byte[] IPV6_ADDRESS_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("00080000"); - - private static final byte[] IPV6_SUBNET_ATTRIBUTE_VALUE = IPV6_ADDRESS_ATTRIBUTE_VALUE; - private static final byte[] IPV6_SUBNET_ATTRIBUTE_WITH_VALUE = - TestUtils.hexStringToByteArray("000f001120010db800000000000000000000000140"); - private static final byte[] IPV6_SUBNET_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("000f0000"); - - private static final Inet6Address IPV6_DNS = - (Inet6Address) (InetAddressUtils.parseNumericAddress("2001:db8:100::1")); - private static final byte[] IPV6_DNS_ATTRIBUTE_WITHOUT_VALUE = - TestUtils.hexStringToByteArray("000a0000"); - - private Inet4Address[] mNetMasks; - private int[] mIpv4PrefixLens; - - @Before - public void setUp() throws Exception { - mNetMasks = - new Inet4Address[] { - (Inet4Address) (InetAddressUtils.parseNumericAddress("0.0.0.0")), - (Inet4Address) (InetAddressUtils.parseNumericAddress("255.255.255.255")), - (Inet4Address) (InetAddressUtils.parseNumericAddress("255.255.255.240")) - }; - mIpv4PrefixLens = new int[] {0, 32, 28}; - } - - private IkeConfigPayload verifyDecodeHeaderAndGetPayload( - IkePayload payload, int expectedConfigType) { - assertEquals(PAYLOAD_TYPE_CP, payload.payloadType); - assertFalse(payload.isCritical); - assertTrue(payload instanceof IkeConfigPayload); - - IkeConfigPayload configPayload = (IkeConfigPayload) payload; - assertEquals(expectedConfigType, configPayload.configType); - - return configPayload; - } - - @Test - public void testDecodeConfigRequest() throws Exception { - IkePayload payload = - IkePayloadFactory.getIkePayload( - PAYLOAD_TYPE_CP, - false /*isResp*/, - ByteBuffer.wrap(CONFIG_REQ_PAYLOAD)) - .first; - - IkeConfigPayload configPayload = - verifyDecodeHeaderAndGetPayload(payload, CONFIG_TYPE_REQUEST); - - List<ConfigAttribute> recognizedAttributeList = configPayload.recognizedAttributeList; - assertEquals(4, recognizedAttributeList.size()); - - ConfigAttribute att = recognizedAttributeList.get(0); - assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, att.attributeType); - assertNull(((ConfigAttributeIpv4Address) att).address); - - att = recognizedAttributeList.get(1); - assertEquals(CONFIG_ATTR_INTERNAL_IP6_ADDRESS, att.attributeType); - assertNull(((ConfigAttributeIpv6Address) att).linkAddress); - - att = recognizedAttributeList.get(2); - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DNS, att.attributeType); - assertNull(((ConfigAttributeIpv4Dns) att).address); - - att = recognizedAttributeList.get(3); - assertEquals(CONFIG_ATTR_INTERNAL_IP6_DNS, att.attributeType); - assertNull(((ConfigAttributeIpv6Dns) att).address); - } - - @Test - public void testDecodeConfigResponse() throws Exception { - IkePayload payload = - IkePayloadFactory.getIkePayload( - PAYLOAD_TYPE_CP, - true /*isResp*/, - ByteBuffer.wrap(CONFIG_RESP_PAYLOAD)) - .first; - - IkeConfigPayload configPayload = - verifyDecodeHeaderAndGetPayload(payload, CONFIG_TYPE_REPLY); - - List<ConfigAttribute> recognizedAttributeList = configPayload.recognizedAttributeList; - assertEquals(3, recognizedAttributeList.size()); - - ConfigAttribute att = recognizedAttributeList.get(0); - assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, att.attributeType); - assertEquals(IPV4_ADDRESS, ((ConfigAttributeIpv4Address) att).address); - - att = recognizedAttributeList.get(1); - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DNS, att.attributeType); - assertEquals(IPV4_DNS, ((ConfigAttributeIpv4Dns) att).address); - - att = recognizedAttributeList.get(2); - InetAddress expectedDns = InetAddress.getByName("8.8.4.4"); - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DNS, att.attributeType); - assertEquals(expectedDns, ((ConfigAttributeIpv4Dns) att).address); - } - - @Test - public void testDecodeConfigRespWithTwoNetmask() throws Exception { - byte[] configPayloadBytes = - TestUtils.hexStringToByteArray(CONFIG_RESP_PAYLOAD_INVALID_ONE_HEX); - try { - IkePayloadFactory.getIkePayload( - PAYLOAD_TYPE_CP, true /*isResp*/, ByteBuffer.wrap(configPayloadBytes)); - fail("Expected to fail because more than on netmask found"); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testDecodeConfigRespNetmaskFoundWithoutIpv4Addr() throws Exception { - byte[] configPayloadBytes = - TestUtils.hexStringToByteArray(CONFIG_RESP_PAYLOAD_INVALID_TWO_HEX); - try { - IkePayloadFactory.getIkePayload( - PAYLOAD_TYPE_CP, true /*isResp*/, ByteBuffer.wrap(configPayloadBytes)); - fail("Expected to fail because netmask is found without a IPv4 address"); - } catch (InvalidSyntaxException expected) { - } - } - - private ConfigAttribute makeMockAttribute(byte[] encodedAttribute) { - ConfigAttribute mockAttribute = mock(ConfigAttribute.class); - - when(mockAttribute.getAttributeLen()).thenReturn(encodedAttribute.length); - - doAnswer( - (invocation) -> { - ByteBuffer buffer = (ByteBuffer) invocation.getArguments()[0]; - buffer.put(encodedAttribute); - return null; - }) - .when(mockAttribute) - .encodeAttributeToByteBuffer(any(ByteBuffer.class)); - - return mockAttribute; - } - - @Test - public void testBuildAndEncodeOutboundConfig() throws Exception { - List<ConfigAttribute> mockAttributeList = new LinkedList<>(); - mockAttributeList.add(makeMockAttribute(IPV4_ADDRESS_ATTRIBUTE_WITHOUT_VALUE)); - mockAttributeList.add(makeMockAttribute(IPV6_ADDRESS_ATTRIBUTE_WITHOUT_VALUE)); - mockAttributeList.add(makeMockAttribute(IPV4_DNS_ATTRIBUTE_WITHOUT_VALUE)); - mockAttributeList.add(makeMockAttribute(IPV6_DNS_ATTRIBUTE_WITHOUT_VALUE)); - IkeConfigPayload configPayload = new IkeConfigPayload(false /*isReply*/, mockAttributeList); - - assertEquals(PAYLOAD_TYPE_CP, configPayload.payloadType); - assertFalse(configPayload.isCritical); - assertEquals(CONFIG_TYPE_REQUEST, configPayload.configType); - assertEquals(mockAttributeList, configPayload.recognizedAttributeList); - - ByteBuffer buffer = ByteBuffer.allocate(configPayload.getPayloadLength()); - configPayload.encodeToByteBuffer(PAYLOAD_TYPE_NOTIFY, buffer); - assertArrayEquals(CONFIG_REQ_PAYLOAD, buffer.array()); - } - - private void verifyBuildAndEncodeAttributeCommon( - ConfigAttribute attribute, int expectedAttributeType, byte[] expectedEncodedAttribute) { - assertEquals(expectedAttributeType, attribute.attributeType); - - ByteBuffer buffer = ByteBuffer.allocate(attribute.getAttributeLen()); - attribute.encodeAttributeToByteBuffer(buffer); - assertArrayEquals(expectedEncodedAttribute, buffer.array()); - } - - private void verifyEncodeIpv4AddresBaseAttribute( - ConfigAttrIpv4AddressBase attribute, - int expectedAttributeType, - byte[] expectedEncodedAttribute, - Inet4Address expectedAddress) { - verifyBuildAndEncodeAttributeCommon( - attribute, expectedAttributeType, expectedEncodedAttribute); - assertEquals(expectedAddress, attribute.address); - } - - private void verifyEncodeIpv6RangeBaseAttribute( - ConfigAttrIpv6AddrRangeBase attribute, - int expectedAttributeType, - byte[] expectedEncodedAttribute, - LinkAddress expectedLinkAddress) { - verifyBuildAndEncodeAttributeCommon( - attribute, expectedAttributeType, expectedEncodedAttribute); - assertEquals(expectedLinkAddress, attribute.linkAddress); - } - - @Test - public void testDecodeIpv4AddressWithValue() throws Exception { - ConfigAttributeIpv4Address attributeIp4Address = - new ConfigAttributeIpv4Address(IPV4_ADDRESS.getAddress()); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, attributeIp4Address.attributeType); - assertEquals(IPV4_ADDRESS, attributeIp4Address.address); - } - - @Test - public void testDecodeIpv4AddressWithoutValue() throws Exception { - ConfigAttributeIpv4Address attributeIp4Address = - new ConfigAttributeIpv4Address(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, attributeIp4Address.attributeType); - assertNull(attributeIp4Address.address); - } - - @Test - public void testDecodeIpv4AddressWithInvalidValue() throws Exception { - byte[] invalidValue = new byte[] {1}; - - try { - ConfigAttributeIpv4Address attributeIp4Address = - new ConfigAttributeIpv4Address(invalidValue); - fail("Expected to fail due to invalid attribute value"); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testEncodeIpv4AddressWithValue() throws Exception { - ConfigAttributeIpv4Address attributeIp4Address = - new ConfigAttributeIpv4Address(IPV4_ADDRESS); - - verifyEncodeIpv4AddresBaseAttribute( - attributeIp4Address, - CONFIG_ATTR_INTERNAL_IP4_ADDRESS, - IPV4_ADDRESS_ATTRIBUTE_WITH_VALUE, - IPV4_ADDRESS); - } - - @Test - public void testEncodeIpv4AddressWithoutValue() throws Exception { - ConfigAttributeIpv4Address attributeIp4Address = new ConfigAttributeIpv4Address(); - - verifyEncodeIpv4AddresBaseAttribute( - attributeIp4Address, - CONFIG_ATTR_INTERNAL_IP4_ADDRESS, - IPV4_ADDRESS_ATTRIBUTE_WITHOUT_VALUE, - null /*expectedAddress*/); - } - - @Test - public void testDecodeIpv4NetmaskWithValue() throws Exception { - ConfigAttributeIpv4Netmask attribute = - new ConfigAttributeIpv4Netmask(IPV4_NETMASK.getAddress()); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_NETMASK, attribute.attributeType); - assertEquals(IPV4_NETMASK, attribute.address); - } - - @Test - public void testDecodeIpv4NetmaskWithoutValue() throws Exception { - ConfigAttributeIpv4Netmask attribute = new ConfigAttributeIpv4Netmask(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_NETMASK, attribute.attributeType); - assertNull(attribute.address); - } - - @Test - public void testEncodeIpv4Netmask() throws Exception { - ConfigAttributeIpv4Netmask attribute = new ConfigAttributeIpv4Netmask(); - - verifyEncodeIpv4AddresBaseAttribute( - attribute, - CONFIG_ATTR_INTERNAL_IP4_NETMASK, - IPV4_NETMASK_ATTRIBUTE_WITHOUT_VALUE, - null /*expectedAddress*/); - } - - @Test - public void testDecodeIpv4DnsWithValue() throws Exception { - ConfigAttributeIpv4Dns attribute = new ConfigAttributeIpv4Dns(IPV4_DNS.getAddress()); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DNS, attribute.attributeType); - assertEquals(IPV4_DNS, attribute.address); - } - - @Test - public void testDecodeIpv4DnsWithoutValue() throws Exception { - ConfigAttributeIpv4Dns attribute = new ConfigAttributeIpv4Dns(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DNS, attribute.attributeType); - assertNull(attribute.address); - } - - @Test - public void testEncodeIpv4Dns() throws Exception { - ConfigAttributeIpv4Dns attribute = new ConfigAttributeIpv4Dns(); - - verifyEncodeIpv4AddresBaseAttribute( - attribute, - CONFIG_ATTR_INTERNAL_IP4_DNS, - IPV4_DNS_ATTRIBUTE_WITHOUT_VALUE, - null /*expectedAddress*/); - } - - @Test - public void testDecodeIpv4DhcpWithValue() throws Exception { - ConfigAttributeIpv4Dhcp attribute = new ConfigAttributeIpv4Dhcp(IPV4_DHCP.getAddress()); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DHCP, attribute.attributeType); - assertEquals(IPV4_DHCP, attribute.address); - } - - @Test - public void testDecodeIpv4DhcpWithoutValue() throws Exception { - ConfigAttributeIpv4Dhcp attribute = new ConfigAttributeIpv4Dhcp(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_DHCP, attribute.attributeType); - assertNull(attribute.address); - } - - @Test - public void testEncodeIpv4DhcpWithValue() throws Exception { - ConfigAttributeIpv4Dhcp attributeIp4Dhcp = new ConfigAttributeIpv4Dhcp(IPV4_DHCP); - - verifyEncodeIpv4AddresBaseAttribute( - attributeIp4Dhcp, - CONFIG_ATTR_INTERNAL_IP4_DHCP, - IPV4_DHCP_ATTRIBUTE_WITH_VALUE, - IPV4_DHCP); - } - - @Test - public void testEncodeIpv4DhcpWithoutValue() throws Exception { - ConfigAttributeIpv4Dhcp attribute = new ConfigAttributeIpv4Dhcp(); - - verifyEncodeIpv4AddresBaseAttribute( - attribute, - CONFIG_ATTR_INTERNAL_IP4_DHCP, - IPV4_DHCP_ATTRIBUTE_WITHOUT_VALUE, - null /*expectedAddress*/); - } - - @Test - public void testDecodeIpv4SubnetWithValue() throws Exception { - ConfigAttributeIpv4Subnet attributeIp4Subnet = - new ConfigAttributeIpv4Subnet(IPV4_SUBNET_ATTRIBUTE_VALUE); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_SUBNET, attributeIp4Subnet.attributeType); - assertEquals(IPV4_LINK_ADDRESS, attributeIp4Subnet.linkAddress); - } - - @Test - public void testDecodeIpv4SubnetWithoutValue() throws Exception { - ConfigAttributeIpv4Subnet attributeIp4Subnet = new ConfigAttributeIpv4Subnet(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP4_SUBNET, attributeIp4Subnet.attributeType); - assertNull(attributeIp4Subnet.linkAddress); - } - - @Test - public void testDecodeIpv4SubnetWithInvalidValue() throws Exception { - byte[] ipAddress = IPV4_ADDRESS.getAddress(); - ByteBuffer buffer = ByteBuffer.allocate(ipAddress.length * 2); - buffer.put(ipAddress).put(ipAddress); - - try { - new ConfigAttributeIpv4Subnet(buffer.array()); - fail("Expected to fail due to invalid netmask."); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testEncodeIpv4SubnetWithoutValue() throws Exception { - ConfigAttributeIpv4Subnet attributeIp4Subnet = new ConfigAttributeIpv4Subnet(); - - verifyBuildAndEncodeAttributeCommon( - attributeIp4Subnet, - CONFIG_ATTR_INTERNAL_IP4_SUBNET, - IPV4_SUBNET_ATTRIBUTE_WITHOUT_VALUE); - assertNull(attributeIp4Subnet.linkAddress); - } - - @Test - public void testNetmaskToPrefixLen() throws Exception { - for (int i = 0; i < mNetMasks.length; i++) { - assertEquals(mIpv4PrefixLens[i], ConfigAttribute.netmaskToPrefixLen(mNetMasks[i])); - } - } - - @Test - public void testPrefixToNetmaskBytes() throws Exception { - for (int i = 0; i < mIpv4PrefixLens.length; i++) { - assertArrayEquals( - mNetMasks[i].getAddress(), - ConfigAttribute.prefixToNetmaskBytes(mIpv4PrefixLens[i])); - } - } - - @Test - public void testDecodeIpv6AddressWithValue() throws Exception { - ConfigAttributeIpv6Address attributeIp6Address = - new ConfigAttributeIpv6Address(IPV6_ADDRESS_ATTRIBUTE_VALUE); - - assertEquals(CONFIG_ATTR_INTERNAL_IP6_ADDRESS, attributeIp6Address.attributeType); - assertEquals(IPV6_LINK_ADDRESS, attributeIp6Address.linkAddress); - } - - @Test - public void testDecodeIpv6AddressWithoutValue() throws Exception { - ConfigAttributeIpv6Address attributeIp6Address = - new ConfigAttributeIpv6Address(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP6_ADDRESS, attributeIp6Address.attributeType); - assertNull(attributeIp6Address.linkAddress); - } - - @Test - public void testDecodeIpv6AddressWithInvalidValue() throws Exception { - byte[] invalidValue = new byte[] {1}; - - try { - ConfigAttributeIpv6Address attributeIp6Address = - new ConfigAttributeIpv6Address(invalidValue); - fail("Expected to fail due to invalid attribute value"); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testEncodeIpv6AddressWithValue() throws Exception { - ConfigAttributeIpv6Address attributeIp6Address = - new ConfigAttributeIpv6Address(IPV6_LINK_ADDRESS); - - verifyEncodeIpv6RangeBaseAttribute( - attributeIp6Address, - CONFIG_ATTR_INTERNAL_IP6_ADDRESS, - IPV6_ADDRESS_ATTRIBUTE_WITH_VALUE, - IPV6_LINK_ADDRESS); - } - - @Test - public void testEncodeIpv6AddressWithoutValue() throws Exception { - ConfigAttributeIpv6Address attributeIp6Address = new ConfigAttributeIpv6Address(); - - verifyEncodeIpv6RangeBaseAttribute( - attributeIp6Address, - CONFIG_ATTR_INTERNAL_IP6_ADDRESS, - IPV6_ADDRESS_ATTRIBUTE_WITHOUT_VALUE, - null /*expectedLinkAddress*/); - } - - @Test - public void testDecodeIpv6SubnetWithValue() throws Exception { - ConfigAttributeIpv6Subnet attributeIp6Subnet = - new ConfigAttributeIpv6Subnet(IPV6_SUBNET_ATTRIBUTE_VALUE); - - assertEquals(CONFIG_ATTR_INTERNAL_IP6_SUBNET, attributeIp6Subnet.attributeType); - assertEquals(IPV6_LINK_ADDRESS, attributeIp6Subnet.linkAddress); - } - - @Test - public void testDecodeIpv6SubnetWithoutValue() throws Exception { - ConfigAttributeIpv6Subnet attributeIp6Subnet = new ConfigAttributeIpv6Subnet(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP6_SUBNET, attributeIp6Subnet.attributeType); - assertNull(attributeIp6Subnet.linkAddress); - } - - @Test - public void testEncodeIpv6SubnetWithoutValue() throws Exception { - ConfigAttributeIpv6Subnet attributeIp6Subnet = new ConfigAttributeIpv6Subnet(); - - verifyEncodeIpv6RangeBaseAttribute( - attributeIp6Subnet, - CONFIG_ATTR_INTERNAL_IP6_SUBNET, - IPV6_SUBNET_ATTRIBUTE_WITHOUT_VALUE, - null /*expectedLinkAddress*/); - } - - @Test - public void testDecodeIpv6DnsWithValue() throws Exception { - ConfigAttributeIpv6Dns attribute = new ConfigAttributeIpv6Dns(IPV6_DNS.getAddress()); - - assertEquals(CONFIG_ATTR_INTERNAL_IP6_DNS, attribute.attributeType); - assertEquals(IPV6_DNS, attribute.address); - } - - @Test - public void testDecodeIpv6DnsWithoutValue() throws Exception { - ConfigAttributeIpv6Dns attribute = new ConfigAttributeIpv6Dns(new byte[0]); - - assertEquals(CONFIG_ATTR_INTERNAL_IP6_DNS, attribute.attributeType); - assertNull(attribute.address); - } - - @Test - public void testEncodeIpv6Dns() throws Exception { - ConfigAttributeIpv6Dns attribute = new ConfigAttributeIpv6Dns(); - - verifyBuildAndEncodeAttributeCommon( - attribute, CONFIG_ATTR_INTERNAL_IP6_DNS, IPV6_DNS_ATTRIBUTE_WITHOUT_VALUE); - assertNull(attribute.address); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeDeletePayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeDeletePayloadTest.java deleted file mode 100644 index 6be5336f..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeDeletePayloadTest.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static com.android.internal.net.ipsec.ike.message.IkePayload.PROTOCOL_ID_ESP; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PROTOCOL_ID_IKE; -import static com.android.internal.net.ipsec.ike.message.IkePayload.SPI_LEN_IPSEC; -import static com.android.internal.net.ipsec.ike.message.IkePayload.SPI_LEN_NOT_INCLUDED; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; - -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.util.Arrays; - -public final class IkeDeletePayloadTest { - private static final String DELETE_IKE_PAYLOAD_HEX_STRING = "0000000801000000"; - private static final String DELETE_CHILD_PAYLOAD_HEX_STRING = "0000000c030400012ad4c0a2"; - private static final String CHILD_SPI = "2ad4c0a2"; - - private static final String DELETE_MULTIPLE_CHILD_PAYLOAD_HEX_STRING = - "0000001003040002abcdef0120fedcba"; - private static final String[] MULTIPLE_CHILD_SPIS = new String[] {"abcdef01", "20fedcba"}; - - private static final int NUM_CHILD_SPI = 1; - - private static final int PROTOCOL_ID_OFFSET = 4; - private static final int SPI_SIZE_OFFSET = 5; - private static final int NUM_OF_SPI_OFFSET = 6; - - @Test - public void testDecodeDeleteIkePayload() throws Exception { - ByteBuffer inputBuffer = - ByteBuffer.wrap(TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING)); - - IkePayload payload = - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer) - .first; - - assertTrue(payload instanceof IkeDeletePayload); - - IkeDeletePayload deletePayload = (IkeDeletePayload) payload; - assertEquals(IkePayload.PROTOCOL_ID_IKE, deletePayload.protocolId); - assertEquals(IkePayload.SPI_LEN_NOT_INCLUDED, deletePayload.spiSize); - assertEquals(0, deletePayload.numSpi); - assertArrayEquals(new int[0], deletePayload.spisToDelete); - } - - @Test - public void testDecodeDeleteChildPayload() throws Exception { - ByteBuffer inputBuffer = - ByteBuffer.wrap(TestUtils.hexStringToByteArray(DELETE_CHILD_PAYLOAD_HEX_STRING)); - - IkePayload payload = - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer) - .first; - - assertTrue(payload instanceof IkeDeletePayload); - - IkeDeletePayload deletePayload = (IkeDeletePayload) payload; - assertEquals(IkePayload.PROTOCOL_ID_ESP, deletePayload.protocolId); - assertEquals(IkePayload.SPI_LEN_IPSEC, deletePayload.spiSize); - assertEquals(NUM_CHILD_SPI, deletePayload.numSpi); - - int expectedChildSpi = TestUtils.hexStringToInt(CHILD_SPI); - assertArrayEquals(new int[] {expectedChildSpi}, deletePayload.spisToDelete); - } - - @Test - public void testDecodeWithInvalidProtocol() throws Exception { - byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); - deletePayloadBytes[PROTOCOL_ID_OFFSET] = -1; - ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); - - try { - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); - fail("Expected to fail due to unrecognized protocol ID."); - } catch (InvalidSyntaxException expected) { - - } - } - - @Test - public void testDecodeWithInvalidSpiSize() throws Exception { - byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); - deletePayloadBytes[SPI_SIZE_OFFSET] = IkePayload.SPI_LEN_IPSEC; - ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); - - try { - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); - fail("Expected to fail due to invalid SPI size in Delete IKE Payload."); - } catch (InvalidSyntaxException expected) { - - } - } - - @Test - public void testDecodeWithInvalidNumSpi() throws Exception { - byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); - deletePayloadBytes[NUM_OF_SPI_OFFSET] = 1; - ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); - - try { - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); - fail("Expected to fail because number of SPI is not zero in Delete IKE Payload."); - } catch (InvalidSyntaxException expected) { - - } - } - - @Test - public void testDecodeWithInvalidNumSpiAndSpiSize() throws Exception { - byte[] deletePayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); - deletePayloadBytes[SPI_SIZE_OFFSET] = 1; - deletePayloadBytes[NUM_CHILD_SPI] = 4; - ByteBuffer inputBuffer = ByteBuffer.wrap(deletePayloadBytes); - - try { - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_DELETE, false /*is request*/, inputBuffer); - fail("Expected to fail due to invalid SPI size in Delete IKE Payload."); - } catch (InvalidSyntaxException expected) { - - } - } - - @Test - public void testOutboundConstructorForIke() throws Exception { - IkeDeletePayload deletePayload = new IkeDeletePayload(); - - assertEquals(PROTOCOL_ID_IKE, deletePayload.protocolId); - assertEquals(SPI_LEN_NOT_INCLUDED, deletePayload.spiSize); - assertEquals(0, deletePayload.numSpi); - assertEquals(0, deletePayload.spisToDelete.length); - } - - @Test - public void testOutboundConstructorWithSingleChildSa() throws Exception { - int[] childSpis = new int[] {TestUtils.hexStringToInt(CHILD_SPI)}; - IkeDeletePayload deletePayload = new IkeDeletePayload(childSpis); - - assertEquals(PROTOCOL_ID_ESP, deletePayload.protocolId); - assertEquals(SPI_LEN_IPSEC, deletePayload.spiSize); - assertEquals(NUM_CHILD_SPI, deletePayload.numSpi); - assertArrayEquals(childSpis, deletePayload.spisToDelete); - } - - @Test - public void testOutboundConstructorWithMultipleChildSas() throws Exception { - int[] childSpis = new int[] {0x1, 0x2, 0xfffffffd, 0xffffffff}; - IkeDeletePayload deletePayload = new IkeDeletePayload(childSpis); - - assertEquals(PROTOCOL_ID_ESP, deletePayload.protocolId); - assertEquals(SPI_LEN_IPSEC, deletePayload.spiSize); - assertEquals(childSpis.length, deletePayload.numSpi); - assertArrayEquals(childSpis, deletePayload.spisToDelete); - } - - @Test - public void testOutboundConstructorWithNoChildSas() throws Exception { - try { - IkeDeletePayload deletePayload = new IkeDeletePayload(new int[] {}); - fail("Expected exception for invalid SPI list"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testEncodeForIke() throws Exception { - IkeDeletePayload deletePayload = new IkeDeletePayload(); - ByteBuffer bb = ByteBuffer.allocate(deletePayload.getPayloadLength()); - - deletePayload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, bb); - - byte[] expectedPayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedPayloadBytes, bb.array()); - } - - @Test - public void testEncodeWithSingleChildSa() throws Exception { - int[] childSpis = new int[] {TestUtils.hexStringToInt(CHILD_SPI)}; - IkeDeletePayload deletePayload = new IkeDeletePayload(childSpis); - ByteBuffer bb = ByteBuffer.allocate(deletePayload.getPayloadLength()); - - deletePayload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, bb); - - byte[] expectedPayloadBytes = - TestUtils.hexStringToByteArray(DELETE_CHILD_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedPayloadBytes, bb.array()); - } - - @Test - public void testEncodeWithMultipleChildSas() throws Exception { - int[] childSpis = - Arrays.stream(MULTIPLE_CHILD_SPIS) - .mapToInt(val -> TestUtils.hexStringToInt(val)) - .toArray(); - IkeDeletePayload deletePayload = new IkeDeletePayload(childSpis); - ByteBuffer bb = ByteBuffer.allocate(deletePayload.getPayloadLength()); - - deletePayload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, bb); - - byte[] expectedPayloadBytes = - TestUtils.hexStringToByteArray(DELETE_MULTIPLE_CHILD_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedPayloadBytes, bb.array()); - } - - @Test - public void testPayloadLengthForIke() throws Exception { - IkeDeletePayload deletePayload = new IkeDeletePayload(); - - byte[] expectedPayloadBytes = TestUtils.hexStringToByteArray(DELETE_IKE_PAYLOAD_HEX_STRING); - assertEquals(expectedPayloadBytes.length, deletePayload.getPayloadLength()); - } - - @Test - public void testPayloadLengthWithSingleChildSa() throws Exception { - int[] childSpis = new int[] {TestUtils.hexStringToInt(CHILD_SPI)}; - IkeDeletePayload deletePayload = new IkeDeletePayload(childSpis); - - byte[] expectedPayloadBytes = - TestUtils.hexStringToByteArray(DELETE_CHILD_PAYLOAD_HEX_STRING); - assertEquals(expectedPayloadBytes.length, deletePayload.getPayloadLength()); - } - - @Test - public void testPayloadLengthWithMultipleChildSas() throws Exception { - int[] childSpis = - Arrays.stream(MULTIPLE_CHILD_SPIS) - .mapToInt(val -> TestUtils.hexStringToInt(val)) - .toArray(); - IkeDeletePayload deletePayload = new IkeDeletePayload(childSpis); - - byte[] expectedPayloadBytes = - TestUtils.hexStringToByteArray(DELETE_MULTIPLE_CHILD_PAYLOAD_HEX_STRING); - assertEquals(expectedPayloadBytes.length, deletePayload.getPayloadLength()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeEapPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeEapPayloadTest.java deleted file mode 100644 index fff82b60..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeEapPayloadTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import static com.android.internal.net.TestUtils.hexStringToByteArray; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import java.nio.ByteBuffer; - -public class IkeEapPayloadTest { - private static final String EAP_SUCCESS_STRING = "03010004"; - private static final byte[] EAP_SUCCESS_PACKET = hexStringToByteArray(EAP_SUCCESS_STRING); - - private static final byte[] IKE_EAP_PAYLOAD = - hexStringToByteArray( - "00000008" + EAP_SUCCESS_STRING); - - @Test - public void testDecodeIkeEapPayload() throws Exception { - ByteBuffer input = ByteBuffer.wrap(IKE_EAP_PAYLOAD); - IkePayload result = IkePayloadFactory - .getIkePayload(IkePayload.PAYLOAD_TYPE_EAP, true, input).first; - - assertTrue(result instanceof IkeEapPayload); - IkeEapPayload ikeEapPayload = (IkeEapPayload) result; - assertArrayEquals(EAP_SUCCESS_PACKET, ikeEapPayload.eapMessage); - } - - @Test - public void testEncodeToByteBuffer() { - IkeEapPayload ikeEapPayload = new IkeEapPayload(EAP_SUCCESS_PACKET); - ByteBuffer result = ByteBuffer.allocate(IKE_EAP_PAYLOAD.length); - - ikeEapPayload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, result); - assertArrayEquals(IKE_EAP_PAYLOAD, result.array()); - } - - @Test - public void testOutboundConstructorForIke() { - IkeEapPayload ikeEapPayload = new IkeEapPayload(EAP_SUCCESS_PACKET); - assertArrayEquals(EAP_SUCCESS_PACKET, ikeEapPayload.eapMessage); - } - - @Test - public void testGetPayloadLength() { - IkeEapPayload ikeEapPayload = new IkeEapPayload(EAP_SUCCESS_PACKET); - assertEquals(IKE_EAP_PAYLOAD.length, ikeEapPayload.getPayloadLength()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeEncryptedPayloadBodyTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeEncryptedPayloadBodyTest.java deleted file mode 100644 index 36c7648a..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeEncryptedPayloadBodyTest.java +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeCombinedModeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeNormalModeCipher; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; - -import org.junit.Before; -import org.junit.Test; - -import java.security.GeneralSecurityException; -import java.util.Arrays; - -public final class IkeEncryptedPayloadBodyTest { - private static final String IKE_AUTH_INIT_REQUEST_HEADER = - "5f54bf6d8b48e6e1909232b3d1edcb5c2e20230800000001000000ec"; - private static final String IKE_AUTH_INIT_REQUEST_SK_HEADER = "230000d0"; - private static final String IKE_AUTH_INIT_REQUEST_IV = "b9132b7bb9f658dfdc648e5017a6322a"; - private static final String IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA = - "030c316ce55f365760d46426ce5cfc78bd1ed9abff63eb9594c1bd58" - + "46de333ecd3ea2b705d18293b130395300ba92a351041345" - + "0a10525cea51b2753b4e92b081fd78d995659a98f742278f" - + "f9b8fd3e21554865c15c79a5134d66b2744966089e416c60" - + "a274e44a9a3f084eb02f3bdce1e7de9de8d9a62773ab563b" - + "9a69ba1db03c752acb6136452b8a86c41addb4210d68c423" - + "efed80e26edca5fa3fe5d0a5ca9375ce332c474b93fb1fa3" - + "59eb4e81"; - private static final String IKE_AUTH_INIT_REQUEST_CHECKSUM = "ae6e0f22abdad69ba8007d50"; - - private static final String IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA = - "2400000c010000000a50500d2700000c010000000a505050" - + "2100001c02000000df7c038aefaaa32d3f44b228b52a3327" - + "44dfb2c12c00002c00000028010304032ad4c0a20300000c" - + "0100000c800e008003000008030000020000000805000000" - + "2d00001801000000070000100000ffff00000000ffffffff" - + "2900001801000000070000100000ffff00000000ffffffff" - + "29000008000040000000000c0000400100000001"; - private static final String IKE_AUTH_INIT_REQUEST_PADDING = "0000000000000000000000"; - private static final int HMAC_SHA1_CHECKSUM_LEN = 12; - - private static final String ENCR_KEY_FROM_INIT_TO_RESP = "5cbfd33f75796c0188c4a3a546aec4a1"; - private static final String INTE_KEY_FROM_INIT_TO_RESP = - "554fbf5a05b7f511e05a30ce23d874db9ef55e51"; - - private static final String ENCR_ALGO_AES_CBC = "AES/CBC/NoPadding"; - - // Test vectors for IKE message protected by HmacSha1 and 3DES - private static final String HMAC_SHA1_3DES_MSG_HEX_STRING = - "5837b1bd28ec424f85ddd0c609c8dbfe2e20232000000002" - + "00000064300000488beaf41d88544baabd95eac60269f19a" - + "5986295fe318ce02f65368cd957985f36b183794c4c78d35" - + "437762297a131a773d7f7806aaa0c590f48b9d71001f4d65" - + "70a44533"; - - private static final String HMAC_SHA1_3DES_DECRYPTED_BODY_HEX_STRING = - "00000028013c00241a013c001f10dac4f8b759138776091dd0f00033c5b07374726f6e675377616e"; - - private static final String HMAC_SHA1_3DES_MSG_ENCR_KEY = - "ee0fdd6d35bbdbe9eeef2f24495b6632e5047bdd8e413c87"; - private static final String HMAC_SHA1_3DES_MSG_INTE_KEY = - "867a0bd019108db856cf6984fc9fb62d70c0de74"; - - // TODO: b/142753861 Test IKE fragment protected by AES_CBC instead of 3DES - - // Test vectors for IKE fragment protected by HmacSha1 and 3DES - private static final String HMAC_SHA1_3DES_FRAG_HEX_STRING = - "939ae1251d18eb9077a99551b15c6e933520232000000001" - + "000000c0000000a400050005fd7c7931705af184b7be76bb" - + "d45a8ecbb3ffd58b9438b93f67e9fe86b06229f80e9b52d2" - + "ff6afde3f2c13ae93ce55a801f62e1a818c9003880a36bbe" - + "986fe6979ba233b9f4f0ddc992d06dbad5a2b998be18fae9" - + "47e5ccfb37775d069344e711fbf499bb289cf4cca245bd45" - + "0ad89d18689207759507ba18d47247e920b9e000a25a7596" - + "e4130929e5cdc37d5c1b0d90bbaae946c260f4d3cf815f6d"; - private static final String HMAC_SHA1_3DES_FRAG_DECRYPTED_BODY_HEX_STRING = - "54ebd95c572100002002000000000100040a0a0a01000300" - + "040808080800030004080804042c00002c00000028020304" - + "03cc86090a0300000c0100000c800e010003000008030000" - + "0200000008050000002d00001801000000070000100000ff" - + "ff0a0a0a010a0a0a010000001801000000070000100000ff" - + "ff00000000ffffffff"; - private static final String HMAC_SHA1_3DES_FRAG_IV = "fd7c7931705af184"; - private static final String HMAC_SHA1_3DES_FRAG_PADDING = "dcf0fa5e2b64"; - - private static final int HMAC_SHA1_3DES_FRAGMENT_NUM = 5; - private static final int HMAC_SHA1_3DES_TOTAL_FRAGMENTS = 5; - - private static final String HMAC_SHA1_3DES_FRAG_ENCR_KEY = - "6BBF6CB3526D6492F4DA0AF45E9B9FD3E1FF534280352073"; - private static final String HMAC_SHA1_3DES_FRAG_INTE_KEY = - "293449E8E518060780B9C06F15838A06EEF57814"; - - // Test vectors for IKE message protected by AES_GCM_16 - private static final String AES_GCM_MSG_HEX_STRING = - "77c708b4523e39a471dc683c1d4f21362e20230800000005" - + "0000006127000045fbd69d9ee2dafc5e7c03a0106761065b2" - + "8fa8d11aed6046f7f8af117e44da7635be6e0dfafcb0a387c" - + "53fb46ba5d6fa9509161915929de97b7fbe23dc65723b0fe"; - private static final String AES_GCM_MSG_DECRYPTED_BODY_HEX_STRING = - "000000280200000033233837e909ec805d56151bef5b1fa9b8e25b32419c9b3fc96ee699ec29d501"; - private static final String AES_GCM_MSG_IV = "fbd69d9ee2dafc5e"; - private static final String AES_GCM_MSG_ENCR_KEY = - "7C04513660DEC572D896105254EF92608054F8E6EE19E79CE52AB8697B2B5F2C2AA90C29"; - - // Test vectors for IKE fragment protected by AES_GCM_16 - private static final String AES_GCM_FRAG_HEX_STRING = - "77c708b4523e39a471dc683c1d4f213635202320000000010" - + "0000113000000f7000200026faf9e5c04c67571871681d443" - + "01489f99fd78d318b0517a5a99bf6a3e1770f43d7d997c9e0" - + "d186038d16df3fd525eda821f80b3a40fc6bce397ac67539e" - + "40042919a5e9af38c70881d092a8571f0e131f594c0e8d6b8" - + "4ea116f0c95619439b0a267b35bc47dac72bbfb3d3776feb3" - + "86d7d4f819b0248f52f60bf4371ab6384e37819a9685c27d8" - + "e41abe30cd6f60905dd5c05c351ec0a1fcf9b99360161d2f3" - + "4dcf6401829df9392121d88e2201d279200e25d750678af6a" - + "7f4892a5c8d4a7358ec50cdf12cfa7652488f756ba6d07441" - + "e9a27aad3976ac8a705ff796857cb2df9ce360c3992e0285b" - + "34834255b06"; - private static final String AES_GCM_FRAG_DECRYPTED_BODY_HEX_STRING = - "0fce6e996f4936ec8db8097339c371c686be75f4bed3f259c" - + "14d39c3ad90cb864085c6375f75b724d9f9daa8e7b22a106a" - + "488bc48c081997b7416fd33b146882e51ff6a640edf760212" - + "7f2454d502e92262ba3dd07cff52ee1bb1ea85f582db41a68" - + "aaf6dace362e5d8b10cfeb65eebc7572690e2c415a11cae57" - + "020810cf7aa56d9f2d5c2be3a633f8e4c6af5483a2b1f05bd" - + "4340ab551ddf7f51def57eaf5a37793ff6aa1e1ec288a2adf" - + "a647c369f15efa61a619966a320f24e1765c0e00c5ed394aa" - + "ef14512032b005827c000000090100000501"; - private static final String AES_GCM_FRAG_IV = "6faf9e5c04c67571"; - - private static final int AES_GCM_FRAGMENT_NUM = 2; - private static final int AES_GCM_TOTAL_FRAGMENTS = 2; - - private static final String AES_GCM_FRAG_ENCR_KEY = - "955ED949D6F18857220E97B17D9285C830A39F8D4DC46AB43943668093C62A3D66664F8C"; - - private static final int ENCRYPTED_BODY_SK_OFFSET = - IkeHeader.IKE_HEADER_LENGTH + IkePayload.GENERIC_HEADER_LENGTH; - private static final int ENCRYPTED_BODY_SKF_OFFSET = - ENCRYPTED_BODY_SK_OFFSET + IkeSkfPayload.SKF_HEADER_LEN; - - private IkeNormalModeCipher mAesCbcCipher; - private byte[] mAesCbcKey; - - private IkeMacIntegrity mHmacSha1IntegrityMac; - private byte[] mHmacSha1IntegrityKey; - - private byte[] mDataToPadAndEncrypt; - private byte[] mDataToAuthenticate; - private byte[] mEncryptedPaddedData; - private byte[] mIkeMessage; - - private byte[] mChecksum; - private byte[] mIv; - private byte[] mPadding; - - private IkeNormalModeCipher m3DesCipher; - - private IkeCombinedModeCipher mAesGcm16Cipher; - - private byte[] mAesGcmMsgKey; - private byte[] mAesGcmMsg; - private byte[] mAesGcmUnencryptedData; - - private byte[] mAesGcmFragKey; - private byte[] mAesGcmFragMsg; - private byte[] mAesGcmFragUnencryptedData; - - @Before - public void setUp() throws Exception { - mDataToPadAndEncrypt = - TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA); - String hexStringToAuthenticate = - IKE_AUTH_INIT_REQUEST_HEADER - + IKE_AUTH_INIT_REQUEST_SK_HEADER - + IKE_AUTH_INIT_REQUEST_IV - + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA; - mDataToAuthenticate = TestUtils.hexStringToByteArray(hexStringToAuthenticate); - mEncryptedPaddedData = - TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA); - mIkeMessage = - TestUtils.hexStringToByteArray( - IKE_AUTH_INIT_REQUEST_HEADER - + IKE_AUTH_INIT_REQUEST_SK_HEADER - + IKE_AUTH_INIT_REQUEST_IV - + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA - + IKE_AUTH_INIT_REQUEST_CHECKSUM); - - mChecksum = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_CHECKSUM); - mIv = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_IV); - mPadding = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_PADDING); - - m3DesCipher = - (IkeNormalModeCipher) - IkeCipher.create( - new EncryptionTransform(SaProposal.ENCRYPTION_ALGORITHM_3DES), - IkeMessage.getSecurityProvider()); - - mAesCbcCipher = - (IkeNormalModeCipher) - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, - SaProposal.KEY_LEN_AES_128), - IkeMessage.getSecurityProvider()); - mAesCbcKey = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP); - - mHmacSha1IntegrityMac = - IkeMacIntegrity.create( - new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96), - IkeMessage.getSecurityProvider()); - mHmacSha1IntegrityKey = TestUtils.hexStringToByteArray(INTE_KEY_FROM_INIT_TO_RESP); - - mAesGcm16Cipher = - (IkeCombinedModeCipher) - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, - SaProposal.KEY_LEN_AES_256), - IkeMessage.getSecurityProvider()); - - mAesGcmMsgKey = TestUtils.hexStringToByteArray(AES_GCM_MSG_ENCR_KEY); - mAesGcmMsg = TestUtils.hexStringToByteArray(AES_GCM_MSG_HEX_STRING); - mAesGcmUnencryptedData = - TestUtils.hexStringToByteArray(AES_GCM_MSG_DECRYPTED_BODY_HEX_STRING); - - mAesGcmFragKey = TestUtils.hexStringToByteArray(AES_GCM_FRAG_ENCR_KEY); - mAesGcmFragMsg = TestUtils.hexStringToByteArray(AES_GCM_FRAG_HEX_STRING); - mAesGcmFragUnencryptedData = - TestUtils.hexStringToByteArray(AES_GCM_FRAG_DECRYPTED_BODY_HEX_STRING); - } - - @Test - public void testValidateChecksum() throws Exception { - IkeEncryptedPayloadBody.validateInboundChecksumOrThrow( - mDataToAuthenticate, mHmacSha1IntegrityMac, mHmacSha1IntegrityKey, mChecksum); - } - - @Test - public void testThrowForInvalidChecksum() throws Exception { - byte[] dataToAuthenticate = Arrays.copyOf(mDataToAuthenticate, mDataToAuthenticate.length); - dataToAuthenticate[0]++; - - try { - IkeEncryptedPayloadBody.validateInboundChecksumOrThrow( - dataToAuthenticate, mHmacSha1IntegrityMac, mHmacSha1IntegrityKey, mChecksum); - fail("Expected GeneralSecurityException due to mismatched checksum."); - } catch (GeneralSecurityException expected) { - } - } - - @Test - public void testCalculatePaddingPlaintextShorterThanBlockSize() throws Exception { - int blockSize = 16; - int plainTextLength = 15; - int expectedPadLength = 0; - - byte[] calculatedPadding = - IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize); - assertEquals(expectedPadLength, calculatedPadding.length); - } - - @Test - public void testCalculatePaddingPlaintextInBlockSize() throws Exception { - int blockSize = 16; - int plainTextLength = 16; - int expectedPadLength = 15; - - byte[] calculatedPadding = - IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize); - assertEquals(expectedPadLength, calculatedPadding.length); - } - - @Test - public void testCalculatePaddingPlaintextLongerThanBlockSize() throws Exception { - int blockSize = 16; - int plainTextLength = 17; - int expectedPadLength = 14; - - byte[] calculatedPadding = - IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize); - assertEquals(expectedPadLength, calculatedPadding.length); - } - - @Test - public void testEncrypt() throws Exception { - byte[] calculatedData = - IkeEncryptedPayloadBody.normalModeEncrypt( - mDataToPadAndEncrypt, mAesCbcCipher, mAesCbcKey, mIv, mPadding); - - assertArrayEquals(mEncryptedPaddedData, calculatedData); - } - - @Test - public void testDecrypt() throws Exception { - byte[] calculatedPlainText = - IkeEncryptedPayloadBody.normalModeDecrypt( - mEncryptedPaddedData, mAesCbcCipher, mAesCbcKey, mIv); - - assertArrayEquals(mDataToPadAndEncrypt, calculatedPlainText); - } - - @Test - public void testBuildAndEncodeOutboundIkeEncryptedPayloadBody() throws Exception { - IkeHeader ikeHeader = new IkeHeader(mIkeMessage); - - IkeEncryptedPayloadBody payloadBody = - new IkeEncryptedPayloadBody( - ikeHeader, - IkePayload.PAYLOAD_TYPE_ID_INITIATOR, - new byte[0] /*skfHeader*/, - mDataToPadAndEncrypt, - mHmacSha1IntegrityMac, - mAesCbcCipher, - mHmacSha1IntegrityKey, - mAesCbcKey, - mIv, - mPadding); - - byte[] expectedEncodedData = - TestUtils.hexStringToByteArray( - IKE_AUTH_INIT_REQUEST_IV - + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA - + IKE_AUTH_INIT_REQUEST_CHECKSUM); - assertArrayEquals(expectedEncodedData, payloadBody.encode()); - } - - @Test - public void testAuthAndDecodeHmacSha1AesCbc() throws Exception { - IkeEncryptedPayloadBody payloadBody = - new IkeEncryptedPayloadBody( - mIkeMessage, - ENCRYPTED_BODY_SK_OFFSET, - mHmacSha1IntegrityMac, - mAesCbcCipher, - mHmacSha1IntegrityKey, - mAesCbcKey); - - assertArrayEquals(mDataToPadAndEncrypt, payloadBody.getUnencryptedData()); - } - - @Test - public void testAuthAndDecodeHmacSha13Des() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_MSG_HEX_STRING); - byte[] expectedDecryptedData = - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_DECRYPTED_BODY_HEX_STRING); - - IkeEncryptedPayloadBody payloadBody = - new IkeEncryptedPayloadBody( - message, - ENCRYPTED_BODY_SK_OFFSET, - mHmacSha1IntegrityMac, - m3DesCipher, - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_MSG_INTE_KEY), - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_MSG_ENCR_KEY)); - - assertArrayEquals(expectedDecryptedData, payloadBody.getUnencryptedData()); - } - - @Test - public void testBuildAndEncodeWithHmacSha13Des() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_FRAG_HEX_STRING); - IkeHeader ikeHeader = new IkeHeader(message); - - byte[] skfHeaderBytes = - IkeSkfPayload.encodeSkfHeader( - HMAC_SHA1_3DES_FRAGMENT_NUM, HMAC_SHA1_3DES_TOTAL_FRAGMENTS); - - IkeEncryptedPayloadBody payloadBody = - new IkeEncryptedPayloadBody( - ikeHeader, - IkePayload.PAYLOAD_TYPE_NO_NEXT, - skfHeaderBytes, - TestUtils.hexStringToByteArray( - HMAC_SHA1_3DES_FRAG_DECRYPTED_BODY_HEX_STRING), - mHmacSha1IntegrityMac, - m3DesCipher, - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_FRAG_INTE_KEY), - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_FRAG_ENCR_KEY), - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_FRAG_IV), - TestUtils.hexStringToByteArray(HMAC_SHA1_3DES_FRAG_PADDING)); - - byte[] expectedEncodedData = - Arrays.copyOfRange(message, ENCRYPTED_BODY_SKF_OFFSET, message.length); - - assertArrayEquals(expectedEncodedData, payloadBody.encode()); - } - - @Test - public void testAuthAndDecodeFullMsgWithAesGcm() throws Exception { - IkeEncryptedPayloadBody encryptedBody = - new IkeEncryptedPayloadBody( - mAesGcmMsg, - ENCRYPTED_BODY_SK_OFFSET, - null /*integrityMac*/, - mAesGcm16Cipher, - null /*integrityKey*/, - mAesGcmMsgKey); - - assertArrayEquals(mAesGcmUnencryptedData, encryptedBody.getUnencryptedData()); - } - - @Test - public void testBuildAndEncodeMsgWithAesGcm() throws Exception { - IkeHeader ikeHeader = new IkeHeader(mAesGcmMsg); - - IkeEncryptedPayloadBody payloadBody = - new IkeEncryptedPayloadBody( - ikeHeader, - IkePayload.PAYLOAD_TYPE_AUTH, - new byte[0], - mAesGcmUnencryptedData, - null /*integrityMac*/, - mAesGcm16Cipher, - null /*integrityKey*/, - mAesGcmMsgKey, - TestUtils.hexStringToByteArray(AES_GCM_MSG_IV), - new byte[0] /*padding*/); - - byte[] expectedEncodedData = - Arrays.copyOfRange(mAesGcmMsg, ENCRYPTED_BODY_SK_OFFSET, mAesGcmMsg.length); - - assertArrayEquals(expectedEncodedData, payloadBody.encode()); - } - - @Test - public void testAuthAndDecodeFragMsgWithAesGcm() throws Exception { - IkeEncryptedPayloadBody encryptedBody = - new IkeEncryptedPayloadBody( - mAesGcmFragMsg, - ENCRYPTED_BODY_SKF_OFFSET, - null /*integrityMac*/, - mAesGcm16Cipher, - null /*integrityKey*/, - mAesGcmFragKey); - - assertArrayEquals(mAesGcmFragUnencryptedData, encryptedBody.getUnencryptedData()); - } - - @Test - public void testBuildAndEncodeFragMsgWithAesGcm() throws Exception { - IkeHeader ikeHeader = new IkeHeader(mAesGcmFragMsg); - byte[] skfHeaderBytes = - IkeSkfPayload.encodeSkfHeader(AES_GCM_FRAGMENT_NUM, AES_GCM_TOTAL_FRAGMENTS); - - IkeEncryptedPayloadBody payloadBody = - new IkeEncryptedPayloadBody( - ikeHeader, - IkePayload.PAYLOAD_TYPE_NO_NEXT, - skfHeaderBytes, - mAesGcmFragUnencryptedData, - null /*integrityMac*/, - mAesGcm16Cipher, - null /*integrityKey*/, - mAesGcmFragKey, - TestUtils.hexStringToByteArray(AES_GCM_FRAG_IV), - new byte[0] /*padding*/); - - byte[] expectedEncodedData = - Arrays.copyOfRange( - mAesGcmFragMsg, ENCRYPTED_BODY_SKF_OFFSET, mAesGcmFragMsg.length); - - assertArrayEquals(expectedEncodedData, payloadBody.encode()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeIdPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeIdPayloadTest.java deleted file mode 100644 index 75552b31..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeIdPayloadTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -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 com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; - -import org.junit.Test; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.nio.ByteBuffer; - -public final class IkeIdPayloadTest { - - private static final String IPV4_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING = - "2700000c01000000c0000264"; - private static final String IPV4_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING = "01000000c0000264"; - private static final String IPV4_ADDR_STRING = "192.0.2.100"; - - private static final String IPV6_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING = - "27000018050000000000200100000db80000000000000001"; - private static final String IPV6_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING = - "050000000000200100000db80000000000000001"; - private static final String IPV6_ADDR_STRING = "0:2001:0:db8::1"; - - private static final String FQDN_ID_PAYLOAD_HEX_STRING = - "2500001702000000696B652E616E64726F69642E6E6574"; - private static final String FQDN_ID_PAYLOAD_BODY_HEX_STRING = - "02000000696B652E616E64726F69642E6E6574"; - private static final String FQDN = "ike.android.net"; - - private static final String RFC822_ADDR_ID_PAYLOAD_HEX_STRING = - "2500001e03000000616e64726f6964696b65406578616d706c652e636f6d"; - private static final String RFC822_ADDR_ID_PAYLOAD_BODY_HEX_STRING = - "03000000616e64726f6964696b65406578616d706c652e636f6d"; - private static final String RFC822_NAME = "androidike@example.com"; - - private static final String KEY_ID_PAYLOAD_HEX_STRING = - "250000170b000000616E64726F6964496B654B65794964"; - private static final String KEY_ID_PAYLOAD_BODY_HEX_STRING = - "0b000000616E64726F6964496B654B65794964"; - private static final byte[] KEY_ID = "androidIkeKeyId".getBytes(); - - private static final int ID_TYPE_OFFSET = 0; - - @Test - public void testDecodeIpv4AddrIdPayload() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(IPV4_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING); - IkeIdPayload payload = new IkeIdPayload(false, inputPacket, false); - - assertEquals(IkePayload.PAYLOAD_TYPE_ID_RESPONDER, payload.payloadType); - assertEquals(IkeIdentification.ID_TYPE_IPV4_ADDR, payload.ikeId.idType); - IkeIpv4AddrIdentification ikeId = (IkeIpv4AddrIdentification) payload.ikeId; - Inet4Address expectedAddr = (Inet4Address) Inet4Address.getByName(IPV4_ADDR_STRING); - assertEquals(expectedAddr, ikeId.ipv4Address); - } - - @Test - public void testDecodeIpv6AddrIdPayload() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(IPV6_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING); - IkeIdPayload payload = new IkeIdPayload(false, inputPacket, false); - - assertEquals(IkePayload.PAYLOAD_TYPE_ID_RESPONDER, payload.payloadType); - assertEquals(IkeIdentification.ID_TYPE_IPV6_ADDR, payload.ikeId.idType); - IkeIpv6AddrIdentification ikeId = (IkeIpv6AddrIdentification) payload.ikeId; - Inet6Address expectedAddr = (Inet6Address) Inet6Address.getByName(IPV6_ADDR_STRING); - assertEquals(expectedAddr, ikeId.ipv6Address); - } - - @Test - public void testDecodeFqdnIdPayload() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(FQDN_ID_PAYLOAD_BODY_HEX_STRING); - IkeIdPayload payload = - new IkeIdPayload(false /*critical*/, inputPacket, false /*isInitiator*/); - - assertEquals(IkePayload.PAYLOAD_TYPE_ID_RESPONDER, payload.payloadType); - assertArrayEquals(inputPacket, payload.getEncodedPayloadBody()); - assertEquals(IkeIdentification.ID_TYPE_FQDN, payload.ikeId.idType); - IkeFqdnIdentification ikeId = (IkeFqdnIdentification) payload.ikeId; - assertEquals(FQDN, ikeId.fqdn); - } - - @Test - public void testDecodeRfc822AddrIdPayload() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(RFC822_ADDR_ID_PAYLOAD_BODY_HEX_STRING); - IkeIdPayload payload = - new IkeIdPayload(false /*critical*/, inputPacket, true /*isInitiator*/); - - assertEquals(IkePayload.PAYLOAD_TYPE_ID_INITIATOR, payload.payloadType); - assertEquals(IkeIdentification.ID_TYPE_RFC822_ADDR, payload.ikeId.idType); - IkeRfc822AddrIdentification ikeId = (IkeRfc822AddrIdentification) payload.ikeId; - assertEquals(RFC822_NAME, ikeId.rfc822Name); - } - - @Test - public void testDecodeKeyIdPayload() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(KEY_ID_PAYLOAD_BODY_HEX_STRING); - IkeIdPayload payload = - new IkeIdPayload(false /*critical*/, inputPacket, true /*isInitiator*/); - - assertEquals(IkePayload.PAYLOAD_TYPE_ID_INITIATOR, payload.payloadType); - assertEquals(IkeIdentification.ID_TYPE_KEY_ID, payload.ikeId.idType); - IkeKeyIdIdentification ikeId = (IkeKeyIdIdentification) payload.ikeId; - assertArrayEquals(KEY_ID, ikeId.keyId); - } - - @Test - public void testDecodeUnsupportedIdType() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(IPV4_ADDR_ID_PAYLOAD_RESPONDER_BODY_HEX_STRING); - inputPacket[ID_TYPE_OFFSET] = 0; - - try { - new IkeIdPayload(false, inputPacket, true); - fail("Expected AuthenticationFailedException: ID Type is unsupported."); - } catch (AuthenticationFailedException expected) { - } - } - - @Test - public void testConstructAndEncodeIpv4AddrIdPayload() throws Exception { - Inet4Address ipv4Address = (Inet4Address) Inet4Address.getByName(IPV4_ADDR_STRING); - IkeIdPayload payload = new IkeIdPayload(false, new IkeIpv4AddrIdentification(ipv4Address)); - - ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_AUTH, inputBuffer); - - byte[] expectedBytes = - TestUtils.hexStringToByteArray(IPV4_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING); - assertArrayEquals(expectedBytes, inputBuffer.array()); - } - - @Test - public void testConstructAndEncodeIpv6AddrIdPayload() throws Exception { - Inet6Address ipv6Address = (Inet6Address) Inet6Address.getByName(IPV6_ADDR_STRING); - IkeIdPayload payload = new IkeIdPayload(false, new IkeIpv6AddrIdentification(ipv6Address)); - - ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_AUTH, inputBuffer); - - byte[] expectedBytes = - TestUtils.hexStringToByteArray(IPV6_ADDR_ID_PAYLOAD_RESPONDER_HEX_STRING); - assertArrayEquals(expectedBytes, inputBuffer.array()); - } - - @Test - public void testConstructAndEncodeFqdnIdPayload() throws Exception { - IkeIdPayload payload = - new IkeIdPayload(false /*isInitiator*/, new IkeFqdnIdentification(FQDN)); - - ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_CERT, inputBuffer); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(FQDN_ID_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedBytes, inputBuffer.array()); - } - - @Test - public void testConstructAndEncodeRfc822AddrIdPayload() throws Exception { - IkeIdPayload payload = - new IkeIdPayload( - true /*isInitiator*/, new IkeRfc822AddrIdentification(RFC822_NAME)); - - ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_CERT, inputBuffer); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(RFC822_ADDR_ID_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedBytes, inputBuffer.array()); - } - - @Test - public void testConstructAndEncodeKeyIdPayload() throws Exception { - IkeIdPayload payload = - new IkeIdPayload(true /*isInitiator*/, new IkeKeyIdIdentification(KEY_ID)); - - ByteBuffer inputBuffer = ByteBuffer.allocate(payload.getPayloadLength()); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_CERT, inputBuffer); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(KEY_ID_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedBytes, inputBuffer.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeMessageTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeMessageTest.java deleted file mode 100644 index 38b41527..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeMessageTest.java +++ /dev/null @@ -1,935 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_OK; -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_PROTECTED_ERROR; -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_UNPROTECTED_ERROR; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_AUTH; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_ID_INITIATOR; -import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NO_NEXT; -import static com.android.internal.net.ipsec.ike.message.IkeTestUtils.makeDummySkfPayload; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.net.ipsec.ike.exceptions.IkeException; -import android.net.ipsec.ike.exceptions.IkeInternalException; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecord; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeNormalModeCipher; -import com.android.internal.net.ipsec.ike.exceptions.InvalidMessageIdException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResult; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultError; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultOk; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultPartial; -import com.android.internal.net.ipsec.ike.message.IkePayloadFactory.IIkePayloadDecoder; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.LinkedList; - -import javax.crypto.IllegalBlockSizeException; - -public final class IkeMessageTest { - private static final String IKE_SA_INIT_HEADER_RAW_PACKET = - "8f54bf6d8b48e6e10000000000000000212022080000000000000150"; - private static final String IKE_SA_INIT_BODY_RAW_PACKET = - "220000300000002c010100040300000c0100000c" - + "800e00800300000803000002030000080400000200000008" - + "020000022800008800020000b4a2faf4bb54878ae21d6385" - + "12ece55d9236fc5046ab6cef82220f421f3ce6361faf3656" - + "4ecb6d28798a94aad7b2b4b603ddeaaa5630adb9ece8ac37" - + "534036040610ebdd92f46bef84f0be7db860351843858f8a" - + "cf87056e272377f70c9f2d81e29c7b0ce4f291a3a72476bb" - + "0b278fd4b7b0a4c26bbeb08214c707137607958729000024" - + "c39b7f368f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c7" - + "2cb4240eb5c464122900001c00004004e54f73b7d83f6beb" - + "881eab2051d8663f421d10b02b00001c00004005d915368c" - + "a036004cb578ae3e3fb268509aeab1900000002069936922" - + "8741c6d4ca094c93e242c9de19e7b7c60000000500000500"; - private static final String IKE_SA_INIT_RAW_PACKET = - IKE_SA_INIT_HEADER_RAW_PACKET + IKE_SA_INIT_BODY_RAW_PACKET; - - // Byte offsets of first payload type in IKE message header. - private static final int FIRST_PAYLOAD_TYPE_OFFSET = 16; - // Byte offsets of first payload's critical bit in IKE message body. - private static final int PAYLOAD_CRITICAL_BIT_OFFSET = 1; - // Byte offsets of first payload length in IKE message body. - private static final int FIRST_PAYLOAD_LENGTH_OFFSET = 2; - // Byte offsets of last payload length in IKE message body. - private static final int LAST_PAYLOAD_LENGTH_OFFSET = 278; - - private static final String IKE_AUTH_HEADER_HEX_STRING = - "5f54bf6d8b48e6e1909232b3d1edcb5c2e20230800000001000000ec"; - private static final String IKE_AUTH_BODY_HEX_STRING = - "230000d0b9132b7bb9f658dfdc648e5017a6322a030c316c" - + "e55f365760d46426ce5cfc78bd1ed9abff63eb9594c1bd58" - + "46de333ecd3ea2b705d18293b130395300ba92a351041345" - + "0a10525cea51b2753b4e92b081fd78d995659a98f742278f" - + "f9b8fd3e21554865c15c79a5134d66b2744966089e416c60" - + "a274e44a9a3f084eb02f3bdce1e7de9de8d9a62773ab563b" - + "9a69ba1db03c752acb6136452b8a86c41addb4210d68c423" - + "efed80e26edca5fa3fe5d0a5ca9375ce332c474b93fb1fa3" - + "59eb4e81ae6e0f22abdad69ba8007d50"; - - private static final String IKE_AUTH_EXPECTED_CHECKSUM_HEX_STRING = "ae6e0f22abdad69ba8007d50"; - private static final String IKE_AUTH_HEX_STRING = - IKE_AUTH_HEADER_HEX_STRING + IKE_AUTH_BODY_HEX_STRING; - - private static final String IKE_AUTH_UNENCRYPTED_PADDED_DATA_HEX_STRING = - "2400000c010000000a50500d2700000c010000000a505050" - + "2100001c02000000df7c038aefaaa32d3f44b228b52a3327" - + "44dfb2c12c00002c00000028010304032ad4c0a20300000c" - + "0100000c800e008003000008030000020000000805000000" - + "2d00001801000000070000100000ffff00000000ffffffff" - + "2900001801000000070000100000ffff00000000ffffffff" - + "29000008000040000000000c000040010000000100000000" - + "000000000000000b"; - - private static final String IKE_FRAG_HEX_STRING = - "939ae1251d18eb9077a99551b15c6e9335202320000000010000" - + "00c0000000a400020002fd7c7931705af184b7be76bbd45a" - + "8ecbb3ffd58b9438b93f67e9fe86b06229f80e9b52d2ff6a" - + "fde3f2c13ae93ce55a801f62e1a818c9003880a36bbe986f" - + "e6979ba233b9f4f0ddc992d06dbad5a2b998be18fae947e5" - + "ccfb37775d069344e711fbf499bb289cf4cca245bd450ad8" - + "9d18689207759507ba18d47247e920b9e000a25a7596e413" - + "0929e5cdc37d5c1b0d90bbaae946c260f4d3cf815f6d"; - private static final String ID_INIT_PAYLOAD_HEX_STRING = "2400000c010000000a50500d"; - private static final String ID_RESP_PAYLOAD_HEX_STRING = "0000000c010000000a505050"; - - private static final long INIT_SPI = 0x5f54bf6d8b48e6e1L; - private static final long RESP_SPI = 0x909232b3d1edcb5cL; - private static final String IKE_EMPTY_INFO_MSG_HEX_STRING = - "5f54bf6d8b48e6e1909232b3d1edcb5c2e20252800000000" - + "0000004c00000030e376871750fdba9f7012446c5dc3f97a" - + "f83b48ba0dbc68bcc4a78136832100aa4192f251cd4d1b97" - + "d298e550"; - private static final String IKE_EMPTY_INFO_MSG_IV_HEX_STRING = - "e376871750fdba9f7012446c5dc3f97a"; - private static final String IKE_EMPTY_INFO_MSG_ENCRYPTED_DATA_HEX_STRING = - "f83b48ba0dbc68bcc4a78136832100aa"; - private static final String IKE_EMPTY_INFO_MSG_CHECKSUM_HEX_STRING = "4192f251cd4d1b97d298e550"; - - private static final byte[] FRAGMENT_ONE_UNENCRYPTED_DATA = "fragmentOne".getBytes(); - private static final byte[] FRAGMENT_TWO_UNENCRYPTED_DATA = "fragmentTwo".getBytes(); - - private static final int TOTAL_FRAGMENTS = 2; - private static final int FRAGMENT_NUM_ONE = 1; - private static final int FRAGMENT_NUM_TWO = 2; - - private static final int IKE_FRAG_EXPECTED_MESSAGE_ID = 1; - - private static final int IKE_AUTH_EXPECTED_MESSAGE_ID = 1; - private static final int IKE_AUTH_CIPHER_IV_SIZE = 16; - private static final int IKE_AUTH_CIPHER_BLOCK_SIZE = 16; - private static final int IKE_AUTH_PAYLOAD_SIZE = 8; - - private byte[] mIkeAuthPacket; - private byte[] mUnencryptedPaddedData; - private IkeHeader mIkeAuthHeader; - - private IIkePayloadDecoder mSpyIkePayloadDecoder; - - private IkeMacIntegrity mMockIntegrity; - private IkeNormalModeCipher mMockCipher; - private IkeSaRecord mMockIkeSaRecord; - - private byte[] mIkeFragPacketOne; - private byte[] mIkeFragPacketTwo; - private IkeHeader mFragOneHeader; - private IkeHeader mFragTwoHeader; - - private IkeSkfPayload mDummySkfPayloadOne; - private IkeSkfPayload mDummySkfPayloadTwo; - - private static final int[] EXPECTED_IKE_INIT_PAYLOAD_LIST = { - IkePayload.PAYLOAD_TYPE_SA, - IkePayload.PAYLOAD_TYPE_KE, - IkePayload.PAYLOAD_TYPE_NONCE, - IkePayload.PAYLOAD_TYPE_NOTIFY, - IkePayload.PAYLOAD_TYPE_NOTIFY, - IkePayload.PAYLOAD_TYPE_VENDOR - }; - - class TestIkeSupportedPayload extends IkePayload { - TestIkeSupportedPayload(int payload, boolean critical) { - super(payload, critical); - } - - @Override - protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { - throw new UnsupportedOperationException( - "It is not supported to encode " + getTypeString()); - } - - @Override - protected int getPayloadLength() { - throw new UnsupportedOperationException( - "It is not supported to get payload length of " + getTypeString()); - } - - @Override - public String getTypeString() { - return "Test(" + payloadType + ")"; - } - } - - @Before - public void setUp() throws Exception { - mSpyIkePayloadDecoder = spy(new IkePayloadFactory.IkePayloadDecoder()); - doAnswer( - (invocation) -> { - int payloadType = (int) invocation.getArguments()[0]; - boolean isCritical = (boolean) invocation.getArguments()[1]; - if (support(payloadType)) { - return new TestIkeSupportedPayload(payloadType, isCritical); - } - return new IkeUnsupportedPayload(payloadType, isCritical); - }) - .when(mSpyIkePayloadDecoder) - .decodeIkePayload(anyInt(), anyBoolean(), anyBoolean(), any()); - - IkePayloadFactory.sDecoderInstance = mSpyIkePayloadDecoder; - - mIkeAuthPacket = TestUtils.hexStringToByteArray(IKE_AUTH_HEX_STRING); - mUnencryptedPaddedData = - TestUtils.hexStringToByteArray(IKE_AUTH_UNENCRYPTED_PADDED_DATA_HEX_STRING); - mIkeAuthHeader = new IkeHeader(mIkeAuthPacket); - - mMockIntegrity = mock(IkeMacIntegrity.class); - byte[] expectedChecksum = - TestUtils.hexStringToByteArray(IKE_AUTH_EXPECTED_CHECKSUM_HEX_STRING); - when(mMockIntegrity.generateChecksum(any(), any())).thenReturn(expectedChecksum); - when(mMockIntegrity.getChecksumLen()).thenReturn(expectedChecksum.length); - - mMockCipher = mock(IkeNormalModeCipher.class); - when(mMockCipher.getIvLen()).thenReturn(IKE_AUTH_CIPHER_IV_SIZE); - when(mMockCipher.getBlockSize()).thenReturn(IKE_AUTH_CIPHER_BLOCK_SIZE); - when(mMockCipher.decrypt(any(), any(), any())).thenReturn(mUnencryptedPaddedData); - - mMockIkeSaRecord = mock(IkeSaRecord.class); - when(mMockIkeSaRecord.getInboundDecryptionKey()).thenReturn(new byte[0]); - when(mMockIkeSaRecord.getInboundIntegrityKey()).thenReturn(new byte[0]); - - mIkeFragPacketOne = makeFragmentBytes(1 /*fragNum*/, 2 /*totalFragments*/); - mIkeFragPacketTwo = makeFragmentBytes(2 /*fragNum*/, 2 /*totalFragments*/); - - mFragOneHeader = new IkeHeader(mIkeFragPacketOne); - mFragTwoHeader = new IkeHeader(mIkeFragPacketTwo); - - mDummySkfPayloadOne = - makeDummySkfPayload( - FRAGMENT_ONE_UNENCRYPTED_DATA, FRAGMENT_NUM_ONE, TOTAL_FRAGMENTS); - mDummySkfPayloadTwo = - makeDummySkfPayload( - FRAGMENT_TWO_UNENCRYPTED_DATA, FRAGMENT_NUM_TWO, TOTAL_FRAGMENTS); - } - - private byte[] makeFragmentBytes(int fragNum, int totalFragments) { - byte[] packet = TestUtils.hexStringToByteArray(IKE_FRAG_HEX_STRING); - ByteBuffer byteBuffer = ByteBuffer.wrap(packet); - byteBuffer.get(new byte[IkeHeader.IKE_HEADER_LENGTH + IkePayload.GENERIC_HEADER_LENGTH]); - - byteBuffer.putShort((short) fragNum).putShort((short) totalFragments); - return byteBuffer.array(); - } - - @After - public void tearDown() { - IkePayloadFactory.sDecoderInstance = new IkePayloadFactory.IkePayloadDecoder(); - } - - private IkeMessage verifyDecodeResultOkAndGetMessage( - DecodeResult decodeResult, byte[] firstPacket) throws Exception { - assertEquals(DECODE_STATUS_OK, decodeResult.status); - - DecodeResultOk resultOk = (DecodeResultOk) decodeResult; - assertNotNull(resultOk.ikeMessage); - assertArrayEquals(firstPacket, resultOk.firstPacket); - - return resultOk.ikeMessage; - } - - private IkeException verifyDecodeResultErrorAndGetIkeException( - DecodeResult decodeResult, int decodeStatus, byte[] firstPacket) throws Exception { - assertEquals(decodeStatus, decodeResult.status); - - DecodeResultError resultError = (DecodeResultError) decodeResult; - assertNotNull(resultError.ikeException); - - return resultError.ikeException; - } - - @Test - public void testDecodeIkeMessage() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); - IkeHeader header = new IkeHeader(inputPacket); - - DecodeResult decodeResult = IkeMessage.decode(0, header, inputPacket); - - IkeMessage message = verifyDecodeResultOkAndGetMessage(decodeResult, inputPacket); - - assertEquals(EXPECTED_IKE_INIT_PAYLOAD_LIST.length, message.ikePayloadList.size()); - for (int i = 0; i < EXPECTED_IKE_INIT_PAYLOAD_LIST.length; i++) { - assertEquals( - EXPECTED_IKE_INIT_PAYLOAD_LIST[i], message.ikePayloadList.get(i).payloadType); - } - } - - @Test - public void testDecodeMessageWithUnsupportedUncriticalPayload() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); - // Set first payload unsupported uncritical - inputPacket[FIRST_PAYLOAD_TYPE_OFFSET] = (byte) 0xff; - IkeHeader header = new IkeHeader(inputPacket); - - DecodeResult decodeResult = IkeMessage.decode(0, header, inputPacket); - - IkeMessage message = verifyDecodeResultOkAndGetMessage(decodeResult, inputPacket); - - assertEquals(EXPECTED_IKE_INIT_PAYLOAD_LIST.length - 1, message.ikePayloadList.size()); - for (int i = 0; i < EXPECTED_IKE_INIT_PAYLOAD_LIST.length - 1; i++) { - assertEquals( - EXPECTED_IKE_INIT_PAYLOAD_LIST[i + 1], - message.ikePayloadList.get(i).payloadType); - } - } - - @Test - public void testThrowUnsupportedCriticalPayloadException() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); - // Set first payload unsupported critical - inputPacket[FIRST_PAYLOAD_TYPE_OFFSET] = (byte) 0xff; - inputPacket[IkeHeader.IKE_HEADER_LENGTH + PAYLOAD_CRITICAL_BIT_OFFSET] = (byte) 0x80; - - UnsupportedCriticalPayloadException exception = - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg( - inputPacket, UnsupportedCriticalPayloadException.class); - - assertEquals(1, exception.payloadTypeList.size()); - } - - @Test - public void testDecodeMessageWithTooShortPayloadLength() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); - // Set first payload length to 0 - inputPacket[IkeHeader.IKE_HEADER_LENGTH + FIRST_PAYLOAD_LENGTH_OFFSET] = (byte) 0; - inputPacket[IkeHeader.IKE_HEADER_LENGTH + FIRST_PAYLOAD_LENGTH_OFFSET + 1] = (byte) 0; - - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg(inputPacket, InvalidSyntaxException.class); - } - - @Test - public void testDecodeMessageWithTooLongPayloadLength() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); - // Increase last payload length by one byte - inputPacket[IkeHeader.IKE_HEADER_LENGTH + LAST_PAYLOAD_LENGTH_OFFSET]++; - - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg(inputPacket, InvalidSyntaxException.class); - } - - @Test - public void testDecodeMessageWithUnexpectedBytesInTheEnd() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET + "0000"); - - IkeTestUtils.decodeAndVerifyUnprotectedErrorMsg(inputPacket, InvalidSyntaxException.class); - } - - @Test - public void testDecodeEncryptedMessage() throws Exception { - DecodeResult decodeResult = - IkeMessage.decode( - IKE_AUTH_EXPECTED_MESSAGE_ID, - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - mIkeAuthHeader, - mIkeAuthPacket, - null /*collectedFragments*/); - IkeMessage ikeMessage = verifyDecodeResultOkAndGetMessage(decodeResult, mIkeAuthPacket); - - assertEquals(IKE_AUTH_PAYLOAD_SIZE, ikeMessage.ikePayloadList.size()); - } - - @Test - public void testDecodeEncryptedMessageWithWrongId() throws Exception { - DecodeResult decodeResult = - IkeMessage.decode( - 2, - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - mIkeAuthHeader, - mIkeAuthPacket, - null /*collectedFragments*/); - IkeException ikeException = - verifyDecodeResultErrorAndGetIkeException( - decodeResult, DECODE_STATUS_UNPROTECTED_ERROR, mIkeAuthPacket); - - assertTrue(ikeException instanceof InvalidMessageIdException); - } - - @Test - public void testDecodeEncryptedMessageWithWrongChecksum() throws Exception { - when(mMockIntegrity.generateChecksum(any(), any())).thenReturn(new byte[0]); - - DecodeResult decodeResult = - IkeMessage.decode( - IKE_AUTH_EXPECTED_MESSAGE_ID, - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - mIkeAuthHeader, - mIkeAuthPacket, - null /*collectedFragments*/); - IkeException ikeException = - verifyDecodeResultErrorAndGetIkeException( - decodeResult, DECODE_STATUS_UNPROTECTED_ERROR, mIkeAuthPacket); - - assertTrue( - ((IkeInternalException) ikeException).getCause() - instanceof GeneralSecurityException); - } - - @Test - public void testDecryptFail() throws Exception { - when(mMockCipher.decrypt(any(), any(), any())).thenThrow(IllegalBlockSizeException.class); - - DecodeResult decodeResult = - IkeMessage.decode( - IKE_AUTH_EXPECTED_MESSAGE_ID, - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - mIkeAuthHeader, - mIkeAuthPacket, - null /*collectedFragments*/); - - IkeException ikeException = - verifyDecodeResultErrorAndGetIkeException( - decodeResult, DECODE_STATUS_UNPROTECTED_ERROR, mIkeAuthPacket); - assertTrue( - ((IkeInternalException) ikeException).getCause() - instanceof IllegalBlockSizeException); - } - - @Test - public void testParsingErrorInEncryptedMessage() throws Exception { - // Set first payload length to 0 - byte[] decryptedData = - Arrays.copyOfRange(mUnencryptedPaddedData, 0, mUnencryptedPaddedData.length); - decryptedData[FIRST_PAYLOAD_LENGTH_OFFSET] = (byte) 0; - decryptedData[FIRST_PAYLOAD_LENGTH_OFFSET + 1] = (byte) 0; - when(mMockCipher.decrypt(any(), any(), any())).thenReturn(decryptedData); - - DecodeResult decodeResult = - IkeMessage.decode( - IKE_AUTH_EXPECTED_MESSAGE_ID, - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - mIkeAuthHeader, - mIkeAuthPacket, - null /*collectedFragments*/); - IkeException ikeException = - verifyDecodeResultErrorAndGetIkeException( - decodeResult, DECODE_STATUS_PROTECTED_ERROR, mIkeAuthPacket); - - assertTrue(ikeException instanceof InvalidSyntaxException); - } - - private boolean support(int payloadType) { - // Supports all payload typs from 33 to 46 - return (payloadType >= 33 && payloadType <= 46); - } - - @Test - public void testAttachEncodedHeader() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); - byte[] ikeBodyBytes = TestUtils.hexStringToByteArray(IKE_SA_INIT_BODY_RAW_PACKET); - IkeHeader header = new IkeHeader(inputPacket); - IkeMessage message = - ((DecodeResultOk) IkeMessage.decode(0, header, inputPacket)).ikeMessage; - - byte[] encodedIkeMessage = message.attachEncodedHeader(ikeBodyBytes); - assertArrayEquals(inputPacket, encodedIkeMessage); - } - - @Test - public void testEncodeAndEncryptEmptyMsg() throws Exception { - when(mMockCipher.generateIv()) - .thenReturn(TestUtils.hexStringToByteArray(IKE_EMPTY_INFO_MSG_IV_HEX_STRING)); - when(mMockCipher.encrypt(any(), any(), any())) - .thenReturn( - TestUtils.hexStringToByteArray( - IKE_EMPTY_INFO_MSG_ENCRYPTED_DATA_HEX_STRING)); - - byte[] checkSum = TestUtils.hexStringToByteArray(IKE_EMPTY_INFO_MSG_CHECKSUM_HEX_STRING); - when(mMockIntegrity.getChecksumLen()).thenReturn(checkSum.length); - when(mMockIntegrity.generateChecksum(any(), any())).thenReturn(checkSum); - - IkeHeader ikeHeader = - new IkeHeader( - INIT_SPI, - RESP_SPI, - IkePayload.PAYLOAD_TYPE_SK, - IkeHeader.EXCHANGE_TYPE_INFORMATIONAL, - true /*isResp*/, - true /*fromInit*/, - 0); - IkeMessage ikeMessage = new IkeMessage(ikeHeader, new LinkedList<>()); - - byte[][] ikeMessageBytes = - ikeMessage.encryptAndEncode( - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - true /*supportFragment*/, - 1280 /*fragSize*/); - byte[][] expectedBytes = - new byte[][] {TestUtils.hexStringToByteArray(IKE_EMPTY_INFO_MSG_HEX_STRING)}; - - assertArrayEquals(expectedBytes, ikeMessageBytes); - } - - private DecodeResultPartial makeDecodeResultForFragOne(DecodeResultPartial collectedFrags) { - return new DecodeResultPartial( - mFragOneHeader, - mIkeFragPacketOne, - mDummySkfPayloadOne, - PAYLOAD_TYPE_AUTH, - collectedFrags); - } - - private DecodeResultPartial makeDecodeResultForFragTwo(DecodeResultPartial collectedFrags) { - return new DecodeResultPartial( - mFragTwoHeader, - mIkeFragPacketTwo, - mDummySkfPayloadTwo, - PAYLOAD_TYPE_NO_NEXT, - collectedFrags); - } - - @Test - public void testConstructDecodePartialFirstFragArriveFirst() throws Exception { - DecodeResultPartial resultPartial = makeDecodeResultForFragOne(null /*collectedFragments*/); - - assertEquals(PAYLOAD_TYPE_AUTH, resultPartial.firstPayloadType); - assertArrayEquals(mIkeFragPacketOne, resultPartial.firstFragBytes); - assertEquals(mFragOneHeader, resultPartial.ikeHeader); - - assertEquals(TOTAL_FRAGMENTS, resultPartial.collectedFragsList.length); - assertArrayEquals( - FRAGMENT_ONE_UNENCRYPTED_DATA, - resultPartial.collectedFragsList[FRAGMENT_NUM_ONE - 1]); - assertFalse(resultPartial.isAllFragmentsReceived()); - } - - @Test - public void testConstructDecodePartialSecondFragArriveFirst() throws Exception { - DecodeResultPartial resultPartial = makeDecodeResultForFragTwo(null /*collectedFragments*/); - - assertEquals(PAYLOAD_TYPE_NO_NEXT, resultPartial.firstPayloadType); - assertNull(resultPartial.firstFragBytes); - assertEquals(mFragTwoHeader, resultPartial.ikeHeader); - - assertEquals(TOTAL_FRAGMENTS, resultPartial.collectedFragsList.length); - assertArrayEquals( - FRAGMENT_TWO_UNENCRYPTED_DATA, - resultPartial.collectedFragsList[FRAGMENT_NUM_TWO - 1]); - assertFalse(resultPartial.isAllFragmentsReceived()); - } - - @Test - public void testConstructDecodeResultPartialWithCollectedFrags() throws Exception { - DecodeResultPartial resultPartialIncomplete = - makeDecodeResultForFragTwo(null /*collectedFragments*/); - DecodeResultPartial resultPartialComplete = - makeDecodeResultForFragOne(resultPartialIncomplete); - - assertEquals(PAYLOAD_TYPE_AUTH, resultPartialComplete.firstPayloadType); - assertArrayEquals(mIkeFragPacketOne, resultPartialComplete.firstFragBytes); - assertEquals(mFragTwoHeader, resultPartialComplete.ikeHeader); - - assertEquals(TOTAL_FRAGMENTS, resultPartialComplete.collectedFragsList.length); - assertTrue(resultPartialComplete.isAllFragmentsReceived()); - } - - @Test - public void testReassembleAllFrags() throws Exception { - DecodeResultPartial resultPartialIncomplete = - makeDecodeResultForFragOne(null /*collectedFragments*/); - DecodeResultPartial resultPartialComplete = - makeDecodeResultForFragTwo(resultPartialIncomplete); - - assertEquals(PAYLOAD_TYPE_AUTH, resultPartialIncomplete.firstPayloadType); - assertArrayEquals(mIkeFragPacketOne, resultPartialIncomplete.firstFragBytes); - assertEquals(mFragOneHeader, resultPartialIncomplete.ikeHeader); - - assertEquals(TOTAL_FRAGMENTS, resultPartialIncomplete.collectedFragsList.length); - assertTrue(resultPartialIncomplete.isAllFragmentsReceived()); - - // Verify reassembly result - ByteBuffer expectedBuffer = - ByteBuffer.allocate( - FRAGMENT_ONE_UNENCRYPTED_DATA.length - + FRAGMENT_TWO_UNENCRYPTED_DATA.length); - expectedBuffer.put(FRAGMENT_ONE_UNENCRYPTED_DATA).put(FRAGMENT_TWO_UNENCRYPTED_DATA); - - byte[] reassembledBytes = resultPartialComplete.reassembleAllFrags(); - assertArrayEquals(expectedBuffer.array(), reassembledBytes); - } - - @Test - public void testReassembleIncompleteFragmentsThrows() throws Exception { - DecodeResultPartial resultPartial = makeDecodeResultForFragTwo(null /*collectedFragments*/); - - assertFalse(resultPartial.isAllFragmentsReceived()); - - try { - resultPartial.reassembleAllFrags(); - fail("Expected to fail because reassembly is not done"); - } catch (IllegalStateException expected) { - - } - } - - private void setDecryptSkfPayload(IkeSkfPayload skf) throws Exception { - doReturn(skf) - .when(mSpyIkePayloadDecoder) - .decodeIkeSkPayload( - eq(true), - anyBoolean(), - any(), - eq(mMockIntegrity), - eq(mMockCipher), - any(), - any()); - } - - private DecodeResult decodeSkf( - int expectedMsgId, - IkeHeader header, - byte[] packet, - DecodeResultPartial collectFragments) - throws Exception { - return IkeMessage.decode( - expectedMsgId, - mMockIntegrity, - mMockCipher, - mMockIkeSaRecord, - header, - packet, - collectFragments); - } - - @Test - public void testRcvFirstArrivedFrag() throws Exception { - setDecryptSkfPayload(mDummySkfPayloadTwo); - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID, - mFragTwoHeader, - mIkeFragPacketTwo, - null /* collectedFragments*/); - - // Verify decoding result - assertTrue(decodeResult instanceof DecodeResultPartial); - DecodeResultPartial resultPartial = (DecodeResultPartial) decodeResult; - - assertEquals(PAYLOAD_TYPE_NO_NEXT, resultPartial.firstPayloadType); - assertNull(resultPartial.firstFragBytes); - assertEquals(mFragTwoHeader, resultPartial.ikeHeader); - - assertEquals(TOTAL_FRAGMENTS, resultPartial.collectedFragsList.length); - assertArrayEquals( - FRAGMENT_TWO_UNENCRYPTED_DATA, - resultPartial.collectedFragsList[FRAGMENT_NUM_TWO - 1]); - assertFalse(resultPartial.isAllFragmentsReceived()); - } - - @Test - public void testRcvLastArrivedFrag() throws Exception { - // Create two dummy SKF Payloads so that the complete unencrypted data is two ID payloads - byte[] idInitPayloadBytes = TestUtils.hexStringToByteArray(ID_INIT_PAYLOAD_HEX_STRING); - byte[] idRespPayloadBytes = TestUtils.hexStringToByteArray(ID_RESP_PAYLOAD_HEX_STRING); - IkeSkfPayload skfOne = - makeDummySkfPayload(idInitPayloadBytes, FRAGMENT_NUM_ONE, TOTAL_FRAGMENTS); - IkeSkfPayload skfTwo = - makeDummySkfPayload(idRespPayloadBytes, FRAGMENT_NUM_TWO, TOTAL_FRAGMENTS); - - DecodeResultPartial resultPartialIncomplete = - new DecodeResultPartial( - mFragOneHeader, - mIkeFragPacketOne, - skfOne, - PAYLOAD_TYPE_ID_INITIATOR, - null /* collectedFragments*/); - - setDecryptSkfPayload(skfTwo); - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID, - mFragTwoHeader, - mIkeFragPacketTwo, - resultPartialIncomplete); - - // Verify fragments reassembly has been finished and complete message has been decoded. - assertTrue(decodeResult instanceof DecodeResultOk); - DecodeResultOk resultOk = (DecodeResultOk) decodeResult; - assertArrayEquals(mIkeFragPacketOne, resultOk.firstPacket); - assertEquals(2, resultOk.ikeMessage.ikePayloadList.size()); - } - - @Test - public void testRcvFirstArrivedFragWithUnprotectedError() throws Exception { - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID + 1, - mFragTwoHeader, - mIkeFragPacketTwo, - null /* collectedFragments*/); - - // Verify that unprotected error was returned - IkeException ikeException = - verifyDecodeResultErrorAndGetIkeException( - decodeResult, DECODE_STATUS_UNPROTECTED_ERROR, mIkeAuthPacket); - assertTrue(ikeException instanceof InvalidMessageIdException); - } - - @Test - public void testRcvLastArrivedFragWithUnprotectedError() throws Exception { - DecodeResultPartial resultPartialIncomplete = - makeDecodeResultForFragOne(null /* collectedFragments*/); - - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID + 1, - mFragTwoHeader, - mIkeFragPacketTwo, - resultPartialIncomplete); - - // Verify that newly received fragment was discarded - assertEquals(resultPartialIncomplete, decodeResult); - } - - @Test - public void testRcvFragWithLargerTotalFragments() throws Exception { - DecodeResultPartial resultPartialIncomplete = - new DecodeResultPartial( - mFragOneHeader, - mIkeFragPacketOne, - mDummySkfPayloadOne, - PAYLOAD_TYPE_NO_NEXT, - null /* collectedFragments*/); - - // Set total fragments of inbound fragment to 5 - int totalFragments = 5; - byte[] fragPacket = makeFragmentBytes(2 /*fragNum*/, totalFragments); - - byte[] unencryptedData = "testRcvFragWithLargerTotalFragments".getBytes(); - IkeSkfPayload skfPayload = - makeDummySkfPayload(unencryptedData, FRAGMENT_NUM_TWO, totalFragments); - setDecryptSkfPayload(skfPayload); - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID, - mFragTwoHeader, - fragPacket, - resultPartialIncomplete); - - // Verify that previously collected fragments were all discarded - assertTrue(decodeResult instanceof DecodeResultPartial); - DecodeResultPartial resultPartial = (DecodeResultPartial) decodeResult; - - assertEquals(PAYLOAD_TYPE_NO_NEXT, resultPartial.firstPayloadType); - assertNull(resultPartial.firstFragBytes); - assertEquals(mFragTwoHeader, resultPartial.ikeHeader); - - assertEquals(totalFragments, resultPartial.collectedFragsList.length); - assertArrayEquals(unencryptedData, resultPartial.collectedFragsList[FRAGMENT_NUM_TWO - 1]); - assertFalse(resultPartial.isAllFragmentsReceived()); - } - - @Test - public void testRcvFragWithSmallerTotalFragments() throws Exception { - int totalFragments = 5; - byte[] unencryptedData = "testRcvFragWithSmallerTotalFragments".getBytes(); - IkeSkfPayload skfPayload = - makeDummySkfPayload(unencryptedData, FRAGMENT_NUM_ONE, totalFragments); - - DecodeResultPartial resultPartialIncomplete = - new DecodeResultPartial( - mFragOneHeader, - mIkeFragPacketOne, - skfPayload, - PAYLOAD_TYPE_AUTH, - null /* collectedFragments*/); - - setDecryptSkfPayload(mDummySkfPayloadTwo); - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID, - mFragTwoHeader, - mIkeFragPacketTwo, - resultPartialIncomplete); - - // Verify that newly received fragment was discarded - assertEquals(resultPartialIncomplete, decodeResult); - } - - @Test - public void testRcvReplayFrag() throws Exception { - DecodeResultPartial resultPartialIncomplete = - makeDecodeResultForFragTwo(null /* collectedFragments*/); - - setDecryptSkfPayload(mDummySkfPayloadTwo); - DecodeResult decodeResult = - decodeSkf( - IKE_FRAG_EXPECTED_MESSAGE_ID, - mFragTwoHeader, - mIkeFragPacketTwo, - resultPartialIncomplete); - - // Verify that newly received fragment was discarded - assertEquals(resultPartialIncomplete, decodeResult); - } - - @Test - public void testRcvCompleteMessageDuringReassembly() throws Exception { - DecodeResultPartial resultPartialIncomplete = - makeDecodeResultForFragTwo(null /* collectedFragments*/); - - DecodeResult decodeResult = - decodeSkf( - IKE_AUTH_EXPECTED_MESSAGE_ID, - mIkeAuthHeader, - mIkeAuthPacket, - resultPartialIncomplete); - - // Verify that newly received IKE message was discarded - assertEquals(resultPartialIncomplete, decodeResult); - } - - @Test - public void testEncodeAndEncryptFragments() throws Exception { - int messageId = 1; - int fragSize = 140; - int expectedTotalFragments = 3; - - byte[] integrityKey = new byte[0]; - byte[] encryptionKey = new byte[0]; - byte[] iv = new byte[IKE_AUTH_CIPHER_IV_SIZE]; - - when(mMockCipher.generateIv()).thenReturn(iv); - when(mMockCipher.encrypt(any(), any(), any())) - .thenAnswer( - (invocation) -> { - return (byte[]) invocation.getArguments()[0]; - }); - - IkeHeader ikeHeader = - new IkeHeader( - INIT_SPI, - RESP_SPI, - IkePayload.PAYLOAD_TYPE_SK, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - true /*isResp*/, - false /*fromInit*/, - messageId); - - byte[][] packetList = - new IkeMessage.IkeMessageHelper() - .encryptAndEncode( - ikeHeader, - IkePayload.PAYLOAD_TYPE_AUTH, - mUnencryptedPaddedData, - mMockIntegrity, - mMockCipher, - integrityKey, - encryptionKey, - true /*supportFragment*/, - fragSize); - - assertEquals(expectedTotalFragments, packetList.length); - - IkeHeader expectedIkeHeader = - new IkeHeader( - INIT_SPI, - RESP_SPI, - IkePayload.PAYLOAD_TYPE_SKF, - IkeHeader.EXCHANGE_TYPE_IKE_AUTH, - true /*isResp*/, - false /*fromInit*/, - messageId); - for (int i = 0; i < packetList.length; i++) { - byte[] p = packetList[i]; - - // Verify fragment length - assertNotNull(p); - assertTrue(p.length <= fragSize); - - ByteBuffer packetBuffer = ByteBuffer.wrap(p); - - // Verify IKE header - byte[] headerBytes = new byte[IkeHeader.IKE_HEADER_LENGTH]; - packetBuffer.get(headerBytes); - - ByteBuffer expectedHeadBuffer = ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH); - expectedIkeHeader.encodeToByteBuffer( - expectedHeadBuffer, p.length - IkeHeader.IKE_HEADER_LENGTH); - - assertArrayEquals(expectedHeadBuffer.array(), headerBytes); - - // Verify fragment payload header - packetBuffer.get(new byte[IkePayload.GENERIC_HEADER_LENGTH]); - assertEquals(i + 1 /*expetced fragNum*/, Short.toUnsignedInt(packetBuffer.getShort())); - assertEquals(expectedTotalFragments, Short.toUnsignedInt(packetBuffer.getShort())); - } - - verify(mMockCipher, times(expectedTotalFragments + 1)) - .encrypt(any(), eq(encryptionKey), eq(iv)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java deleted file mode 100644 index 884c76eb..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.internal.net.ipsec.ike.message; - -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD; -import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.ipsec.ike.SaProposal; -import android.net.ipsec.ike.exceptions.IkeProtocolException; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.exceptions.UnrecognizedIkeProtocolException; - -import org.junit.Test; - -import java.net.InetAddress; -import java.nio.ByteBuffer; - -public final class IkeNotifyPayloadTest { - private static final String NOTIFY_NAT_DETECTION_PAYLOAD_HEX_STRING = - "2900001c00004004e54f73b7d83f6beb881eab2051d8663f421d10b0"; - private static final String NOTIFY_NAT_DETECTION_PAYLOAD_BODY_HEX_STRING = - "00004004e54f73b7d83f6beb881eab2051d8663f421d10b0"; - private static final String NAT_DETECTION_DATA_HEX_STRING = - "e54f73b7d83f6beb881eab2051d8663f421d10b0"; - - private static final String NOTIFY_REKEY_PAYLOAD_BODY_HEX_STRING = "030440092ad4c0a2"; - private static final int REKEY_SPI = 0x2ad4c0a2; - - private static final String IKE_INITIATOR_SPI_HEX_STRING = "5f54bf6d8b48e6e1"; - private static final String IKE_RESPODNER_SPI_HEX_STRING = "0000000000000000"; - private static final String IP_ADDR = "10.80.80.13"; - private static final int PORT = 500; - - private static final int PROTOCOL_ID_OFFSET = 0; - - @Test - public void testDecodeNotifyPayloadSpiUnset() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(NOTIFY_NAT_DETECTION_PAYLOAD_BODY_HEX_STRING); - byte[] notifyData = TestUtils.hexStringToByteArray(NAT_DETECTION_DATA_HEX_STRING); - - IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); - assertEquals(IkePayload.PROTOCOL_ID_UNSET, payload.protocolId); - assertEquals(IkePayload.SPI_LEN_NOT_INCLUDED, payload.spiSize); - assertEquals(IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, payload.notifyType); - assertEquals(IkePayload.SPI_NOT_INCLUDED, payload.spi); - assertArrayEquals(notifyData, payload.notifyData); - } - - @Test - public void testDecodeNotifyPayloadSpiSet() throws Exception { - byte[] inputPacket = TestUtils.hexStringToByteArray(NOTIFY_REKEY_PAYLOAD_BODY_HEX_STRING); - - IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); - assertEquals(IkePayload.PROTOCOL_ID_ESP, payload.protocolId); - assertEquals(IkePayload.SPI_LEN_IPSEC, payload.spiSize); - assertEquals(IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA, payload.notifyType); - assertEquals(REKEY_SPI, payload.spi); - assertArrayEquals(new byte[0], payload.notifyData); - } - - @Test - public void testDecodeNotifyPayloadThrowException() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(NOTIFY_NAT_DETECTION_PAYLOAD_BODY_HEX_STRING); - // Change Protocol ID to ESP - inputPacket[PROTOCOL_ID_OFFSET] = (byte) (IkePayload.PROTOCOL_ID_ESP & 0xFF); - try { - IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); - fail("Expected InvalidSyntaxException: Protocol ID should not be ESP"); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testGenerateNatDetectionData() throws Exception { - long initiatorIkeSpi = Long.parseLong(IKE_INITIATOR_SPI_HEX_STRING, 16); - long responderIkespi = Long.parseLong(IKE_RESPODNER_SPI_HEX_STRING, 16); - InetAddress inetAddress = InetAddress.getByName(IP_ADDR); - - byte[] netDetectionData = - IkeNotifyPayload.generateNatDetectionData( - initiatorIkeSpi, responderIkespi, inetAddress, PORT); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(NAT_DETECTION_DATA_HEX_STRING); - assertArrayEquals(expectedBytes, netDetectionData); - } - - @Test - public void testBuildIkeErrorNotifyWithData() throws Exception { - int payloadType = 1; - IkeNotifyPayload notifyPayload = - new IkeNotifyPayload( - IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, - new byte[] {(byte) payloadType}); - - assertArrayEquals(new byte[] {(byte) payloadType}, notifyPayload.notifyData); - assertTrue(notifyPayload.isErrorNotify()); - assertFalse(notifyPayload.isNewChildSaNotify()); - } - - @Test - public void testBuildIkeErrorNotifyWithoutData() throws Exception { - IkeNotifyPayload notifyPayload = - new IkeNotifyPayload(IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX); - - assertArrayEquals(new byte[0], notifyPayload.notifyData); - assertTrue(notifyPayload.isErrorNotify()); - assertFalse(notifyPayload.isNewChildSaNotify()); - } - - @Test - public void testBuildChildConfigNotify() throws Exception { - IkeNotifyPayload notifyPayload = - new IkeNotifyPayload(IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE); - - assertArrayEquals(new byte[0], notifyPayload.notifyData); - assertFalse(notifyPayload.isErrorNotify()); - assertTrue(notifyPayload.isNewChildSaNotify()); - } - - @Test - public void testBuildChildErrorNotify() throws Exception { - IkeNotifyPayload notifyPayload = - new IkeNotifyPayload(IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE); - - assertArrayEquals(new byte[0], notifyPayload.notifyData); - assertTrue(notifyPayload.isErrorNotify()); - assertTrue(notifyPayload.isNewChildSaNotify()); - } - - @Test - public void testEncodeNotifyPayload() throws Exception { - byte[] inputPacket = - TestUtils.hexStringToByteArray(NOTIFY_NAT_DETECTION_PAYLOAD_BODY_HEX_STRING); - IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); - - ByteBuffer byteBuffer = ByteBuffer.allocate(payload.getPayloadLength()); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NOTIFY, byteBuffer); - - byte[] expectedNoncePayload = - TestUtils.hexStringToByteArray(NOTIFY_NAT_DETECTION_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedNoncePayload, byteBuffer.array()); - } - - @Test - public void testValidateAndBuildIkeExceptionWithData() throws Exception { - // Invalid Message ID - byte[] dhGroup = new byte[] {(byte) 0x00, (byte) 0x0e}; - int expectedDhGroup = SaProposal.DH_GROUP_2048_BIT_MODP; - - IkeNotifyPayload payload = new IkeNotifyPayload(ERROR_TYPE_INVALID_KE_PAYLOAD, dhGroup); - IkeProtocolException exception = payload.validateAndBuildIkeException(); - - assertTrue(exception instanceof InvalidKeException); - assertEquals(ERROR_TYPE_INVALID_KE_PAYLOAD, exception.getErrorType()); - assertArrayEquals(dhGroup, exception.getErrorData()); - assertEquals(expectedDhGroup, ((InvalidKeException) exception).getDhGroup()); - } - - @Test - public void testValidateAndBuildIkeExceptionWithoutData() throws Exception { - // Invalid Syntax - IkeNotifyPayload payload = new IkeNotifyPayload(ERROR_TYPE_AUTHENTICATION_FAILED); - IkeProtocolException exception = payload.validateAndBuildIkeException(); - - assertTrue(exception instanceof AuthenticationFailedException); - assertEquals(ERROR_TYPE_AUTHENTICATION_FAILED, exception.getErrorType()); - assertArrayEquals(new byte[0], exception.getErrorData()); - } - - @Test - public void testValidateAndBuildUnrecognizedIkeException() throws Exception { - int unrecognizedType = 0; - IkeNotifyPayload payload = new IkeNotifyPayload(unrecognizedType); - IkeProtocolException exception = payload.validateAndBuildIkeException(); - - assertTrue(exception instanceof UnrecognizedIkeProtocolException); - assertEquals(unrecognizedType, exception.getErrorType()); - assertArrayEquals(new byte[0], exception.getErrorData()); - } - - @Test - public void testValidateAndBuildIkeExceptionWithInvalidPayload() throws Exception { - // Build a invalid notify payload - IkeNotifyPayload payload = new IkeNotifyPayload(ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD); - - try { - payload.validateAndBuildIkeException(); - fail("Expected to fail due to invalid error data"); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testBuildIkeExceptionWithStatusNotify() throws Exception { - // Rekey notification - byte[] inputPacket = TestUtils.hexStringToByteArray(NOTIFY_REKEY_PAYLOAD_BODY_HEX_STRING); - IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket); - - assertFalse(payload.isErrorNotify()); - - try { - payload.validateAndBuildIkeException(); - fail("Expected to fail because it is not error notification"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testGetNotifyTypeString() throws Exception { - IkeNotifyPayload payload = new IkeNotifyPayload(ERROR_TYPE_AUTHENTICATION_FAILED); - - assertEquals("Notify(Authentication failed)", payload.getTypeString()); - } - - @Test - public void testGetNotifyTypeStringForUnrecoginizedNotify() throws Exception { - int unrecognizedType = 0; - IkeNotifyPayload payload = new IkeNotifyPayload(unrecognizedType); - - assertEquals("Notify(0)", payload.getTypeString()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSkfPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSkfPayloadTest.java deleted file mode 100644 index 3374a7dc..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeSkfPayloadTest.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; - -import android.net.ipsec.ike.SaProposal; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.crypto.IkeCipher; -import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; -import com.android.internal.net.ipsec.ike.crypto.IkeNormalModeCipher; -import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; -import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; - -import org.junit.Before; -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.util.Arrays; - -public final class IkeSkfPayloadTest { - private static final String IKE_FRAG_MSG_HEX_STRING = - "bb3d5237aa558779db24aeb6c9ea183e352023200000000100000134" - + "0000011800020002c1471fc7714a3b77a2bfd78dd2df4c048dfed913" - + "c5de76cb1f4463ef541df2442b43c65308a47b873502268cc1195f99" - + "4f6f1a945f56cb342969936af97d79c560c8e0f8bb1a0874ebfb5d0e" - + "610b0fcff96d4197c06e7aef07a3a9ae487555bec95c78b87fe6483c" - + "be07e3d132f8594c34dba5b5b463b871d0272af6a1ee701fc6b7b70a" - + "22a1b8f63eed50ce6b2253ee63fe2cf0289a5eb715e56b389f72b5ba" - + "ecfb7340f4abf9253a8c973d281ed62f3516d130fcaaf2c2145b3047" - + "f3a243e60beb2fc28bf05183839caf46bfbfc4f28c9a2224e7d49686" - + "52a236a403ecb203a1de1e2144a6f5ce28acc2f93989608af17381fc" - + "f965cabe1a448274264b22167abfa047dc88e4bfdc5a492847d36d8b"; - - private static final String IKE_FRAG_MSG_DECRYPTED_BODY_HEX_STRING = - "dc89d82f4fccff8d3f0c4f4848571e57205f7dbdce954203983d2147" - + "3a9e10ba36876b860d33afbdfe6ebf000240e31f2039f4213e882d1f" - + "6f0a24887aed0584f4b50a016d989990fd58297757c7b842cd72b57c" - + "2f68cba8a5f06d899ce3fcfbd0419402a1d59f1c5b5b23bd0a4ed525" - + "27ed6cef9fd238552fcf6e4cd9f794d2b01ba61438fd21714fbc3e3f" - + "443a816751e55d46009ae7fb9f52db0977e453a2d28b0453a9393778" - + "3a0b625c27d186c052a7169807537d97e731a3543fc10dca605ca86d" - + "1496882e1d009a9216d07d0000001801850014120a00000f02000200" - + "0100000d010000"; - - private static final String IKE_FRAG_MSG_CHECKSUM = "7abfa047dc88e4bfdc5a492847d36d8b"; - private static final String IKE_FRAG_MSG_PADDING = "05d925c1b3804aee08"; - - private static final int FRAGMENT_NUM = 2; - private static final int TOTAL_FRAGMENTS = 2; - - private static final int FRAGMENT_NUM_OFFSET = - IkeHeader.IKE_HEADER_LENGTH + IkePayload.GENERIC_HEADER_LENGTH; - private static final int TOTAL_FRAGMENTS_OFFET = FRAGMENT_NUM_OFFSET + 2; - - private byte[] mDecryptedData; - - private IkeMacIntegrity mSpyHmacSha256IntegrityMac; - private IkeNormalModeCipher mSpyAesCbcCipher; - - @Before - public void setUp() throws Exception { - mDecryptedData = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_DECRYPTED_BODY_HEX_STRING); - - // Set up integrity algorithm - mSpyHmacSha256IntegrityMac = - spy( - IkeMacIntegrity.create( - new IntegrityTransform( - SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128), - IkeMessage.getSecurityProvider())); - byte[] expectedChecksum = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_CHECKSUM); - doReturn(expectedChecksum).when(mSpyHmacSha256IntegrityMac).generateChecksum(any(), any()); - - // Set up encryption algorithm - mSpyAesCbcCipher = - spy( - (IkeNormalModeCipher) - IkeCipher.create( - new EncryptionTransform( - SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, - SaProposal.KEY_LEN_AES_128), - IkeMessage.getSecurityProvider())); - byte[] expectedDecryptedPaddedData = - TestUtils.hexStringToByteArray( - IKE_FRAG_MSG_DECRYPTED_BODY_HEX_STRING + IKE_FRAG_MSG_PADDING); - doReturn(expectedDecryptedPaddedData).when(mSpyAesCbcCipher).decrypt(any(), any(), any()); - } - - private IkeSkfPayload decodeAndDecryptFragMsg(byte[] message) throws Exception { - IkeSkfPayload payload = - (IkeSkfPayload) - IkePayloadFactory.getIkeSkPayload( - true /*isSkf*/, - message, - mSpyHmacSha256IntegrityMac, - mSpyAesCbcCipher, - new byte[0] /*integrityKey*/, - new byte[0] /*decryptionKey*/) - .first; - return payload; - } - - @Test - public void testDecode() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_HEX_STRING); - - IkeSkfPayload payload = decodeAndDecryptFragMsg(message); - - assertEquals(IkePayload.PAYLOAD_TYPE_SKF, payload.payloadType); - assertEquals(FRAGMENT_NUM, payload.fragmentNum); - assertEquals(TOTAL_FRAGMENTS, payload.totalFragments); - assertArrayEquals(mDecryptedData, payload.getUnencryptedData()); - } - - @Test - public void testDecodeThrowsForZeroFragNum() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_HEX_STRING); - - // Set Fragment Number to zero - message[FRAGMENT_NUM_OFFSET] = 0; - message[FRAGMENT_NUM_OFFSET + 1] = 0; - - try { - decodeAndDecryptFragMsg(message); - fail("Expected to fail because Fragment Number is zero."); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testDecodeThrowsForZeroTotalFragments() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_HEX_STRING); - - // Set Total Fragments Number to zero - message[TOTAL_FRAGMENTS_OFFET] = 0; - message[TOTAL_FRAGMENTS_OFFET + 1] = 0; - - try { - decodeAndDecryptFragMsg(message); - fail("Expected to fail because Total Fragments Number is zero."); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testDecodeThrowsWhenFragNumIsLargerThanTotalFragments() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_HEX_STRING); - - // Set Fragment Number to 5 - message[FRAGMENT_NUM_OFFSET] = 0; - message[FRAGMENT_NUM_OFFSET + 1] = 5; - - // Set Total Fragments Number to 2 - message[TOTAL_FRAGMENTS_OFFET] = 0; - message[TOTAL_FRAGMENTS_OFFET + 1] = 2; - - try { - decodeAndDecryptFragMsg(message); - fail( - "Expected to fail because Fragment Number is larger than" - + " Total Fragments Number"); - } catch (InvalidSyntaxException expected) { - } - } - - @Test - public void testEncode() throws Exception { - byte[] message = TestUtils.hexStringToByteArray(IKE_FRAG_MSG_HEX_STRING); - IkeSkfPayload payload = decodeAndDecryptFragMsg(message); - - int payloadLength = payload.getPayloadLength(); - ByteBuffer buffer = ByteBuffer.allocate(payloadLength); - payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, buffer); - - byte[] expectedPayloadBytes = - Arrays.copyOfRange(message, IkeHeader.IKE_HEADER_LENGTH, message.length); - assertArrayEquals(expectedPayloadBytes, buffer.array()); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeTestUtils.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeTestUtils.java deleted file mode 100644 index 05474379..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeTestUtils.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static com.android.internal.net.ipsec.ike.message.IkeMessage.DECODE_STATUS_UNPROTECTED_ERROR; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.util.Pair; - -import com.android.internal.net.TestUtils; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResult; -import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultError; - -import java.nio.ByteBuffer; - -/** - * IkeTestUtils provides utility methods for testing IKE library. - * - * <p>TODO: Consider moving it under ikev2/ - */ -public final class IkeTestUtils { - public static IkePayload hexStringToIkePayload( - @IkePayload.PayloadType int payloadType, boolean isResp, String payloadHexString) - throws IkeProtocolException { - byte[] payloadBytes = TestUtils.hexStringToByteArray(payloadHexString); - // Returned Pair consists of the IkePayload and the following IkePayload's type. - Pair<IkePayload, Integer> pair = - IkePayloadFactory.getIkePayload(payloadType, isResp, ByteBuffer.wrap(payloadBytes)); - return pair.first; - } - - public static <T extends IkeProtocolException> T decodeAndVerifyUnprotectedErrorMsg( - byte[] inputPacket, Class<T> expectedException) throws Exception { - IkeHeader header = new IkeHeader(inputPacket); - DecodeResult decodeResult = IkeMessage.decode(0, header, inputPacket); - - assertEquals(DECODE_STATUS_UNPROTECTED_ERROR, decodeResult.status); - DecodeResultError resultError = (DecodeResultError) decodeResult; - assertNotNull(resultError.ikeException); - assertTrue(expectedException.isInstance(resultError.ikeException)); - - return (T) resultError.ikeException; - } - - public static IkeSkfPayload makeDummySkfPayload( - byte[] unencryptedData, int fragNum, int totalFrags) throws Exception { - IkeEncryptedPayloadBody mockEncryptedBody = mock(IkeEncryptedPayloadBody.class); - when(mockEncryptedBody.getUnencryptedData()).thenReturn(unencryptedData); - return new IkeSkfPayload(mockEncryptedBody, fragNum, totalFrags); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeTsPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeTsPayloadTest.java deleted file mode 100644 index cbd6f930..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeTsPayloadTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.message; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.net.ipsec.ike.IkeTrafficSelector; - -import com.android.internal.net.TestUtils; - -import libcore.net.InetAddressUtils; - -import org.junit.Test; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -public final class IkeTsPayloadTest { - private static final String TS_INITIATOR_PAYLOAD_HEX_STRING = - "2d00002802000000070000100010fff0c0000264c0000365070000100000ffffc0000464c0000466"; - - private static final int NUMBER_OF_TS = 2; - - private static final int TS_ONE_START_PORT = 16; - private static final int TS_ONE_END_PORT = 65520; - private static final Inet4Address TS_ONE_START_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.100")); - private static final Inet4Address TS_ONE_END_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.3.101")); - - private static final int TS_TWO_START_PORT = 0; - private static final int TS_TWO_END_PORT = 65535; - private static final Inet4Address TS_TWO_START_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.4.100")); - private static final Inet4Address TS_TWO_END_ADDRESS = - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.4.102")); - - private IkeTrafficSelector mTsOne; - private IkeTrafficSelector mTsTwo; - - public IkeTsPayloadTest() { - mTsOne = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - mTsTwo = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_TWO_START_PORT, - TS_TWO_END_PORT, - TS_TWO_START_ADDRESS, - TS_TWO_END_ADDRESS); - } - - @Test - public void testDecodeTsInitiatorPayload() throws Exception { - ByteBuffer inputBuffer = - ByteBuffer.wrap(TestUtils.hexStringToByteArray(TS_INITIATOR_PAYLOAD_HEX_STRING)); - - IkePayload payload = - IkePayloadFactory.getIkePayload( - IkePayload.PAYLOAD_TYPE_TS_INITIATOR, false, inputBuffer) - .first; - assertTrue(payload instanceof IkeTsPayload); - - IkeTsPayload tsPayload = (IkeTsPayload) payload; - assertEquals(IkePayload.PAYLOAD_TYPE_TS_INITIATOR, tsPayload.payloadType); - assertEquals(NUMBER_OF_TS, tsPayload.numTs); - } - - @Test - public void testBuildAndEncodeTsPayload() throws Exception { - IkeTsPayload tsPayload = - new IkeTsPayload(true /*isInitiator*/, new IkeTrafficSelector[] {mTsOne, mTsTwo}); - - ByteBuffer byteBuffer = ByteBuffer.allocate(tsPayload.getPayloadLength()); - tsPayload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_TS_RESPONDER, byteBuffer); - - byte[] expectedBytes = TestUtils.hexStringToByteArray(TS_INITIATOR_PAYLOAD_HEX_STRING); - assertArrayEquals(expectedBytes, byteBuffer.array()); - } - - @Test - public void testContains() throws Exception { - IkeTrafficSelector tsOneNarrowPortRange = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT + 1, - TS_ONE_END_PORT, - TS_ONE_START_ADDRESS, - TS_ONE_END_ADDRESS); - - IkeTrafficSelector tsOneNarrowAddressRange = - new IkeTrafficSelector( - IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, - TS_ONE_START_PORT, - TS_ONE_END_PORT, - (Inet4Address) (InetAddressUtils.parseNumericAddress("192.0.2.200")), - TS_ONE_END_ADDRESS); - - IkeTsPayload tsPayload = - new IkeTsPayload(true /*isInitiator*/, new IkeTrafficSelector[] {mTsOne, mTsTwo}); - - IkeTsPayload tsNarrowPortRangePayload = - new IkeTsPayload( - true /*isInitiator*/, - new IkeTrafficSelector[] {tsOneNarrowPortRange, mTsTwo}); - - IkeTsPayload tsNarrowAddressRangePayload = - new IkeTsPayload( - true /*isInitiator*/, - new IkeTrafficSelector[] {tsOneNarrowAddressRange, mTsTwo}); - - assertTrue(tsPayload.contains(tsPayload)); - assertTrue(tsPayload.contains(tsNarrowPortRangePayload)); - assertTrue(tsPayload.contains(tsNarrowAddressRangePayload)); - - assertFalse(tsNarrowPortRangePayload.contains(tsPayload)); - assertFalse(tsNarrowAddressRangePayload.contains(tsPayload)); - assertFalse(tsNarrowAddressRangePayload.contains(tsNarrowPortRangePayload)); - assertFalse(tsNarrowPortRangePayload.contains(tsNarrowAddressRangePayload)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testutils/CertUtils.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testutils/CertUtils.java deleted file mode 100644 index db128c80..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testutils/CertUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.testutils; - -import android.content.Context; - -import androidx.test.InstrumentationRegistry; - -import com.android.internal.net.ipsec.ike.message.IkeMessage; -import com.android.org.bouncycastle.util.io.pem.PemObject; -import com.android.org.bouncycastle.util.io.pem.PemReader; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.security.KeyFactory; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPrivateKey; -import java.security.spec.PKCS8EncodedKeySpec; - -/** CertUtils provides utility methods for creating X509 certificate and private key. */ -public final class CertUtils { - private static final String PEM_FOLDER_NAME = "pem"; - private static final String KEY_FOLDER_NAME = "key"; - - /** Creates an X509Certificate with a pem file */ - public static X509Certificate createCertFromPemFile(String fileName) throws Exception { - Context context = InstrumentationRegistry.getContext(); - InputStream inputStream = - context.getResources().getAssets().open(PEM_FOLDER_NAME + "/" + fileName); - - CertificateFactory factory = - CertificateFactory.getInstance("X.509", IkeMessage.getSecurityProvider()); - return (X509Certificate) factory.generateCertificate(inputStream); - } - - /** Creates an private key from a PKCS8 format key file */ - public static RSAPrivateKey createRsaPrivateKeyFromKeyFile(String fileName) throws Exception { - Context context = InstrumentationRegistry.getContext(); - InputStream inputStream = - context.getResources().getAssets().open(KEY_FOLDER_NAME + "/" + fileName); - - PemObject pemObject = new PemReader(new InputStreamReader(inputStream)).readPemObject(); - - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return (RSAPrivateKey) - keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pemObject.getContent())); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testutils/MockIpSecTestUtils.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testutils/MockIpSecTestUtils.java deleted file mode 100644 index 742ff240..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testutils/MockIpSecTestUtils.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.testutils; - -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; - -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.IpSecManager; -import android.net.IpSecSpiResponse; -import android.net.IpSecUdpEncapResponse; -import android.system.Os; - -import androidx.test.InstrumentationRegistry; - -import com.android.server.IpSecService; - -/** This class provides utility methods for mocking IPsec surface. */ -public final class MockIpSecTestUtils { - private static final int DUMMY_CHILD_SPI = 0x2ad4c0a2; - private static final int DUMMY_CHILD_SPI_RESOURCE_ID = 0x1234; - - private static final int DUMMY_UDP_ENCAP_PORT = 34567; - private static final int DUMMY_UDP_ENCAP_RESOURCE_ID = 0x3234; - - private Context mContext; - private IpSecService mMockIpSecService; - private IpSecManager mIpSecManager; - - private MockIpSecTestUtils() throws Exception { - mMockIpSecService = mock(IpSecService.class); - mContext = InstrumentationRegistry.getContext(); - mIpSecManager = new IpSecManager(mContext, mMockIpSecService); - - when(mMockIpSecService.allocateSecurityParameterIndex(anyString(), anyInt(), anyObject())) - .thenReturn( - new IpSecSpiResponse( - IpSecManager.Status.OK, - DUMMY_CHILD_SPI_RESOURCE_ID, - DUMMY_CHILD_SPI)); - - when(mMockIpSecService.openUdpEncapsulationSocket(anyInt(), anyObject())) - .thenReturn( - new IpSecUdpEncapResponse( - IpSecManager.Status.OK, - DUMMY_UDP_ENCAP_RESOURCE_ID, - DUMMY_UDP_ENCAP_PORT, - Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))); - } - - public static MockIpSecTestUtils setUpMockIpSec() throws Exception { - return new MockIpSecTestUtils(); - } - - public static IpSecSpiResponse buildDummyIpSecSpiResponse(int spi) throws Exception { - return new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_CHILD_SPI_RESOURCE_ID, spi); - } - - public Context getContext() { - return mContext; - } - - public IpSecManager getIpSecManager() { - return mIpSecManager; - } - - public IpSecService getIpSecService() { - return mMockIpSecService; - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/utils/RetransmitterTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/utils/RetransmitterTest.java deleted file mode 100644 index 1fddb1d9..00000000 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/utils/RetransmitterTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.ipsec.ike.utils; - -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_RETRANSMIT; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.os.Handler; -import android.os.Message; - -import com.android.internal.net.ipsec.ike.message.IkeMessage; - -import org.junit.Before; -import org.junit.Test; - -public final class RetransmitterTest { - private Handler mMockHandler; - private IkeMessage mMockIkeMessage; - private TestRetransmitter mRetransmitter; - - private class TestRetransmitter extends Retransmitter { - int mSendCallCount; // Defaults to 0 - boolean mFailed; // Defaults to false - - TestRetransmitter(Handler handler, IkeMessage message) { - super(handler, message); - } - - @Override - public void send(IkeMessage msg) { - mSendCallCount++; - } - - @Override - public void handleRetransmissionFailure() { - mFailed = true; - } - } - - @Before - public void setUp() throws Exception { - mMockHandler = mock(Handler.class); - when(mMockHandler.obtainMessage(eq(CMD_RETRANSMIT), anyObject())) - .thenReturn(mock(Message.class)); - - mMockIkeMessage = mock(IkeMessage.class); - mRetransmitter = new TestRetransmitter(mMockHandler, mMockIkeMessage); - } - - @Test - public void testSendRequestAndQueueRetransmit() throws Exception { - mRetransmitter.retransmit(); - assertEquals(1, mRetransmitter.mSendCallCount); - verify(mMockHandler).obtainMessage(eq(CMD_RETRANSMIT), eq(mRetransmitter)); - verify(mMockHandler) - .sendMessageDelayed(any(Message.class), eq(Retransmitter.RETRANSMIT_TIMEOUT_MS)); - } - - @Test - public void testRetransmitQueuesExponentialRetransmit() throws Exception { - mRetransmitter.retransmit(); - - for (int i = 0; i <= Retransmitter.RETRANSMIT_MAX_ATTEMPTS; i++) { - long expectedTimeout = - (long) - (Retransmitter.RETRANSMIT_TIMEOUT_MS - * Math.pow(Retransmitter.RETRANSMIT_BACKOFF_FACTOR, i)); - - assertEquals(i + 1, mRetransmitter.mSendCallCount); - assertFalse(mRetransmitter.mFailed); - - // This call happens with the same arguments each time - verify(mMockHandler, times(i + 1)) - .obtainMessage(eq(CMD_RETRANSMIT), eq(mRetransmitter)); - - // But the expected timeout changes every time - verify(mMockHandler).sendMessageDelayed(any(Message.class), eq(expectedTimeout)); - - verifyNoMoreInteractions(mMockHandler); - - // Trigger next round of retransmissions. - mRetransmitter.retransmit(); - } - } - - @Test - public void testRetransmitterCallsRetranmissionsFailedOnMaxTries() throws Exception { - mRetransmitter.retransmit(); - - // Exhaust all retransmit attempts - for (int i = 0; i <= Retransmitter.RETRANSMIT_MAX_ATTEMPTS; i++) { - mRetransmitter.retransmit(); - } - - assertTrue(mRetransmitter.mFailed); - } - - @Test - public void testRetransmitterStopsRetransmitting() throws Exception { - mRetransmitter.stopRetransmitting(); - - verify(mMockHandler).removeMessages(eq(CMD_RETRANSMIT), eq(mRetransmitter)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/utils/LogTest.java b/tests/iketests/src/java/com/android/internal/net/utils/LogTest.java deleted file mode 100644 index 23305aa7..00000000 --- a/tests/iketests/src/java/com/android/internal/net/utils/LogTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.utils; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class LogTest { - private static final String TAG = "IkeLogTest"; - private static final String PII = "123456789ABCDEF"; // "IMSI" - private static final String HEX_STRING = "00112233445566778899AABBCCDDEEFF"; - private static final byte[] HEX_BYTES = new byte[] { - (byte) 0x00, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, - (byte) 0x66, (byte) 0x77, (byte) 0x88, (byte) 0x99, (byte) 0xAA, (byte) 0xBB, - (byte) 0xCC, (byte) 0xDD, (byte) 0xEE, (byte) 0xFF - }; - - @Test - public void testPii() { - // Log(String tag, boolean isEngBuild, boolean logSensitive); - String result = new Log(TAG, false, false).pii(PII); - assertEquals(Integer.toString(PII.hashCode()), result); - - result = new Log(TAG, true, false).pii(PII); - assertEquals(Integer.toString(PII.hashCode()), result); - - result = new Log(TAG, false, true).pii(PII); - assertEquals(Integer.toString(PII.hashCode()), result); - - result = new Log(TAG, true, true).pii(PII); - assertEquals(PII, result); - - result = new Log(TAG, true, true).pii(HEX_BYTES); - assertEquals(HEX_STRING, result); - } - - @Test - public void testByteArrayToHexString() { - assertEquals("", Log.byteArrayToHexString(null)); - - assertEquals("", Log.byteArrayToHexString(new byte[0])); - - assertEquals(HEX_STRING, Log.byteArrayToHexString(HEX_BYTES)); - } -} diff --git a/tests/iketests/src/java/com/android/internal/net/utils/SimpleStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/utils/SimpleStateMachineTest.java deleted file mode 100644 index 2ee8dc53..00000000 --- a/tests/iketests/src/java/com/android/internal/net/utils/SimpleStateMachineTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.internal.net.utils; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.android.internal.net.utils.SimpleStateMachine.SimpleState; - -import org.junit.Before; -import org.junit.Test; - -public class SimpleStateMachineTest { - private static final String INPUT = "input"; - private static final String OUTPUT = "output"; - - private SimpleStateMachine<String, String> mSimpleStateMachine; - private SimpleState mMockStartState; - private SimpleState mMockFinalState; - - @Before - public void setUp() { - mMockStartState = mock(SimpleState.class); - mMockFinalState = mock(SimpleState.class); - - mSimpleStateMachine = new SimpleStateMachine(){}; - mSimpleStateMachine.transitionTo(mMockStartState); - } - - @Test - public void testProcess() { - when(mMockStartState.process(INPUT)).thenReturn(OUTPUT); - String result = mSimpleStateMachine.process(INPUT); - assertEquals(OUTPUT, result); - verify(mMockStartState).process(INPUT); - } - - @Test - public void testTransitionTo() { - mSimpleStateMachine.transitionTo(mMockFinalState); - assertEquals(mMockFinalState, mSimpleStateMachine.mState); - } - - @Test - public void testTransitionToNull() { - try { - mSimpleStateMachine.transitionTo(null); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testTransitionAndProcess() { - when(mMockFinalState.process(INPUT)).thenReturn(OUTPUT); - String result = mSimpleStateMachine.transitionAndProcess(mMockFinalState, INPUT); - assertEquals(OUTPUT, result); - verify(mMockFinalState).process(INPUT); - } - - @Test - public void testTransitionAndProcessToNull() { - try { - mSimpleStateMachine.transitionAndProcess(null, INPUT); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testProcessNullState() { - SimpleStateMachine simpleStateMachine = new SimpleStateMachine() {}; - try { - simpleStateMachine.process(new Object()); - fail("IllegalStateException expected"); - } catch (IllegalStateException expected) { - } - } -} |