diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-05-27 20:46:25 +0000 |
---|---|---|
committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-05-27 20:46:25 +0000 |
commit | 3af91ef562a04671972c67e6dfebd8c3f41c7ddc (patch) | |
tree | 1e323e9f3e4c248818245dc3da05816d98641a1e | |
parent | fa195b2982f4e36fd131f8f95905356119237f3e (diff) | |
parent | 0ae59f085b7e5f718bc033a5621eee5a99a48bf9 (diff) | |
download | ike-sdk-release.tar.gz |
Snap for 6533464 from 0ae59f085b7e5f718bc033a5621eee5a99a48bf9 to sdk-releaseplatform-tools-30.0.3platform-tools-30.0.2sdk-release
Change-Id: Ibe51bbf114f3364a2a75c0d5f433da84db7dd5b2
30 files changed, 1094 insertions, 461 deletions
@@ -14,8 +14,11 @@ java_sdk_library { name: "android.net.ipsec.ike", + defaults: ["framework-module-defaults"], installable: true, - sdk_version: "module_current", + + // ike is used as a shared library. + shared_library: true, aidl: { local_include_dirs: ["src/java"], @@ -28,7 +31,6 @@ java_sdk_library { libs: [ "unsupportedappusage", - "framework-annotations-lib", ], api_packages: [ @@ -41,13 +43,13 @@ java_sdk_library { // being overwritten by the frameworks class copies. jarjar_rules: "jarjar-rules-shared.txt", - plugins: ["java_api_finder"], - hostdex: true, // for hiddenapi check apex_available: [ "com.android.ipsec", "test_com.android.ipsec", ], + + stubs_library_visibility: ["//visibility:public"], } filegroup { @@ -86,30 +88,3 @@ java_library { // being overwritten by the frameworks class copies. jarjar_rules: "jarjar-rules-shared.txt", } - -stubs_defaults { - name: "ike-stubs-defaults", - srcs: [":ike-api-srcs"], -} - -droidstubs { - name: "android.net.ipsec.ike.api.sources.module_libs_api", - defaults: [ - "framework-module-api-defaults-module_libs_api", - "ike-stubs-defaults", - ], -} - -droidstubs { - name: "android.net.ipsec.ike.stubs.sources.module_libs_api", - defaults: [ - "framework-module-stubs-defaults-module_libs_api", - "ike-stubs-defaults", - ], -} - -java_library { - name: "android.net.ipsec.ike.stubs.module_libs_api", - srcs: [":android.net.ipsec.ike.stubs.sources.module_libs_api"], - defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], -} diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt new file mode 100644 index 00000000..bec76838 --- /dev/null +++ b/api/system-lint-baseline.txt @@ -0,0 +1,75 @@ +// Baseline format: 1.0 +BuilderSetStyle: android.net.ipsec.ike.IkeSessionParams.Builder#removeIkeOption(int): + Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.ipsec.ike.IkeSessionParams.Builder.removeIkeOption(int) + + +CallbackInterface: android.net.ipsec.ike.ChildSessionCallback: + Callbacks must be abstract class instead of interface to enable extension in future API levels: ChildSessionCallback +CallbackInterface: android.net.ipsec.ike.IkeSessionCallback: + Callbacks must be abstract class instead of interface to enable extension in future API levels: IkeSessionCallback + + +MethodNameUnits: android.net.ipsec.ike.IkeSessionParams#getDpdDelaySeconds(): + Returned time values must be in milliseconds, was `getDpdDelaySeconds` + + +MissingGetterMatchingBuilder: android.net.eap.EapSessionConfig.Builder#setEapMsChapV2Config(String, String): + android.net.eap.EapSessionConfig does not declare a `getEapMsChapV2Config()` method matching method android.net.eap.EapSessionConfig.Builder.setEapMsChapV2Config(String,String) +MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addDhGroup(int): + android.net.ipsec.ike.ChildSaProposal does not declare a `getDhGroups()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addDhGroup(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addEncryptionAlgorithm(int, int): + android.net.ipsec.ike.ChildSaProposal does not declare a `getEncryptionAlgorithms()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addEncryptionAlgorithm(int,int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addIntegrityAlgorithm(int): + android.net.ipsec.ike.ChildSaProposal does not declare a `getIntegrityAlgorithms()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addIntegrityAlgorithm(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addDhGroup(int): + android.net.ipsec.ike.IkeSaProposal does not declare a `getDhGroups()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addDhGroup(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addEncryptionAlgorithm(int, int): + android.net.ipsec.ike.IkeSaProposal does not declare a `getEncryptionAlgorithms()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addEncryptionAlgorithm(int,int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addIntegrityAlgorithm(int): + android.net.ipsec.ike.IkeSaProposal does not declare a `getIntegrityAlgorithms()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addIntegrityAlgorithm(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addIkeOption(int): + android.net.ipsec.ike.IkeSessionParams does not declare a `getIkeOptions()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addIkeOption(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addPcscfServerRequest(int): + android.net.ipsec.ike.IkeSessionParams does not declare a `getPcscfServerRequests()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addPcscfServerRequest(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addPcscfServerRequest(java.net.InetAddress): + android.net.ipsec.ike.IkeSessionParams does not declare a `getPcscfServerRequests()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addPcscfServerRequest(java.net.InetAddress) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthDigitalSignature(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.security.PrivateKey): + android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthDigitalSignature()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthDigitalSignature(java.security.cert.X509Certificate,java.security.cert.X509Certificate,java.security.PrivateKey) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthDigitalSignature(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.List<java.security.cert.X509Certificate>, java.security.PrivateKey): + android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthDigitalSignature()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthDigitalSignature(java.security.cert.X509Certificate,java.security.cert.X509Certificate,java.util.List<java.security.cert.X509Certificate>,java.security.PrivateKey) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthEap(java.security.cert.X509Certificate, android.net.eap.EapSessionConfig): + android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthEap()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthEap(java.security.cert.X509Certificate,android.net.eap.EapSessionConfig) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthPsk(byte[]): + android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthPsk()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthPsk(byte[]) +MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setLifetimeSeconds(int, int): + android.net.ipsec.ike.IkeSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setLifetimeSeconds(int,int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector): + android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getInboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector): + android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getOutboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addSaProposal(android.net.ipsec.ike.ChildSaProposal): + android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getSaProposals()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addSaProposal(android.net.ipsec.ike.ChildSaProposal) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#setLifetimeSeconds(int, int): + android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.setLifetimeSeconds(int,int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(int): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(java.net.Inet4Address): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(java.net.Inet4Address) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(java.net.Inet6Address, int): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(java.net.Inet6Address,int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalDhcpServerRequest(int): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalDhcpServerRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalDhcpServerRequest(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalDnsServerRequest(int): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalDnsServerRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalDnsServerRequest(int) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getOutboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addSaProposal(android.net.ipsec.ike.ChildSaProposal): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getSaProposals()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addSaProposal(android.net.ipsec.ike.ChildSaProposal) +MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#setLifetimeSeconds(int, int): + android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.setLifetimeSeconds(int,int) + + +MissingNullability: android.net.ipsec.ike.IkeSessionParams#getRetransmissionTimeoutsMillis(): + Missing nullability on method `getRetransmissionTimeoutsMillis` return diff --git a/src/java/android/net/ipsec/ike/ChildSaProposal.java b/src/java/android/net/ipsec/ike/ChildSaProposal.java index 4d3ea284..7318659a 100644 --- a/src/java/android/net/ipsec/ike/ChildSaProposal.java +++ b/src/java/android/net/ipsec/ike/ChildSaProposal.java @@ -107,6 +107,25 @@ public final class ChildSaProposal extends SaProposal { && isTransformSelectedFrom(mEsns, ((ChildSaProposal) reqProposal).mEsns); } + /** @hide */ + public boolean isNegotiatedFromExceptDhGroup(SaProposal saProposal) { + return getProtocolId() == saProposal.getProtocolId() + && isTransformSelectedFrom( + getEncryptionTransforms(), saProposal.getEncryptionTransforms()) + && isTransformSelectedFrom( + getIntegrityTransforms(), saProposal.getIntegrityTransforms()) + && isTransformSelectedFrom(mEsns, ((ChildSaProposal) saProposal).mEsns); + } + + /** @hide */ + public ChildSaProposal getCopyWithAdditionalDhTransform(int dhGroup) { + return new ChildSaProposal( + getEncryptionTransforms(), + getIntegrityTransforms(), + new DhGroupTransform[] {new DhGroupTransform(dhGroup)}, + getEsnTransforms()); + } + /** * This class is used to incrementally construct a ChildSaProposal. ChildSaProposal instances * are immutable once built. diff --git a/src/java/android/net/ipsec/ike/ChildSessionParams.java b/src/java/android/net/ipsec/ike/ChildSessionParams.java index e34d1d6d..0221491c 100644 --- a/src/java/android/net/ipsec/ike/ChildSessionParams.java +++ b/src/java/android/net/ipsec/ike/ChildSessionParams.java @@ -35,6 +35,17 @@ import java.util.concurrent.TimeUnit; * <p>Note that references to negotiated configurations will be held, and the same parameters will * be reused during rekey. This includes SA Proposals, lifetimes and traffic selectors. * + * <p>IKE library will send out KE payload only if user has configured one or more DH groups. The KE + * payload in a request will use the first DH group from the first user provided SA proposal (or the + * peer selected SA proposal if it's a rekey request). The KE payload in a response will depend on + * the SA proposal negotiation result. + * + * <p>When requesting the first Child Session in IKE AUTH, IKE library will not propose any DH group + * even if user has configured it, as per RFC 7296. When rekeying this child session, IKE library + * will accept DH groups that are configured in its ChildSessionParams. If after rekeying user needs + * to have the same DH group as that of the IKE Session, then they need to explicitly set the same + * DH Group in ChildSessionParams. + * * @see {@link TunnelModeChildSessionParams} and {@link TransportModeChildSessionParams} * @hide */ @@ -238,8 +249,9 @@ public abstract class ChildSessionParams { break; case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: startAddress = InetAddresses.parseNumericAddress("::"); - endAddress = InetAddresses.parseNumericAddress( - "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + endAddress = + InetAddresses.parseNumericAddress( + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); break; default: throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType); diff --git a/src/java/android/net/ipsec/ike/SaProposal.java b/src/java/android/net/ipsec/ike/SaProposal.java index fa6fbf17..d5eb8c81 100644 --- a/src/java/android/net/ipsec/ike/SaProposal.java +++ b/src/java/android/net/ipsec/ike/SaProposal.java @@ -228,6 +228,11 @@ public abstract class SaProposal { * Check if the current SaProposal from the SA responder is consistent with the selected * reqProposal from the SA initiator. * + * <p>As per RFC 7296, The accepted cryptographic suite MUST contain exactly one transform of + * each type included in the proposal. But for interoperability reason, IKE library allows + * exceptions when the accepted suite or the request proposal has a NONE value transform. + * Currently only IntegrityTransform and DhGroupTransform have NONE value transform ID defined. + * * @param reqProposal selected SaProposal from SA initiator * @return if current SaProposal from SA responder is consistent with the selected reqProposal * from SA initiator. @@ -236,11 +241,16 @@ public abstract class SaProposal { public boolean isNegotiatedFrom(SaProposal reqProposal) { return this.mProtocolId == reqProposal.mProtocolId && isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms) - && isTransformSelectedFrom(mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms) - && isTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups); + && isIntegrityTransformSelectedFrom( + mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms) + && isDhGroupTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups); } - /** Package private */ + /** + * Check if the response transform can be selected from the request transforms + * + * <p>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. @@ -253,6 +263,45 @@ public abstract class SaProposal { return Arrays.asList(selectFrom).contains(selected[0]); } + /** + * Check if the response integrity transform can be selected from the request integrity + * transforms. + * + * <p>For interoperability reason, it is allowed to do not include integrity transform in the + * response proposal when the request proposal has a NONE value integrity transform; and it is + * also allowed to have a NONE value integrity transform when the request proposal does not have + * integrity transforms. + */ + private static boolean isIntegrityTransformSelectedFrom( + IntegrityTransform[] selected, IntegrityTransform[] selectFrom) { + if (selected.length == 0) { + selected = new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)}; + } + if (selectFrom.length == 0) { + selectFrom = + new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)}; + } + return isTransformSelectedFrom(selected, selectFrom); + } + + /** + * Check if the response DH group can be selected from the request DH groups + * + * <p>For interoperability reason, it is allowed to do not include DH group in the response + * proposal when the request proposal has a NONE value DH group; and it is also allowed to have + * a NONE value DH group when the request proposal does not have DH groups. + */ + private static boolean isDhGroupTransformSelectedFrom( + DhGroupTransform[] selected, DhGroupTransform[] selectFrom) { + if (selected.length == 0) { + selected = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)}; + } + if (selectFrom.length == 0) { + selectFrom = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)}; + } + return isTransformSelectedFrom(selected, selectFrom); + } + /** @hide */ @IkePayload.ProtocolId public int getProtocolId() { diff --git a/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java b/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java index 10a23dbe..c8d31f6b 100644 --- a/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java +++ b/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java @@ -95,29 +95,10 @@ public final class TunnelModeChildSessionParams extends ChildSessionParams { } /** Represents an IPv4 DHCP server request */ - public interface ConfigRequestIpv4DhcpServer extends TunnelModeChildConfigRequest { - /** - * Retrieves the requested IPv4 DHCP server address - * - * @return The requested DHCP server address, or null if no specific DHCP server was - * requested - * @hide - */ - @Nullable - Inet4Address getAddress(); - } + public interface ConfigRequestIpv4DhcpServer extends TunnelModeChildConfigRequest {} /** Represents an IPv4 DNS Server request */ - public interface ConfigRequestIpv4DnsServer extends TunnelModeChildConfigRequest { - /** - * Retrieves the requested IPv4 DNS server address - * - * @return The requested DNS server address, or null if no specific DNS server was requested - * @hide - */ - @Nullable - Inet4Address getAddress(); - } + public interface ConfigRequestIpv4DnsServer extends TunnelModeChildConfigRequest {} /** Represents an IPv4 Netmask request */ public interface ConfigRequestIpv4Netmask extends TunnelModeChildConfigRequest {} @@ -141,16 +122,7 @@ public final class TunnelModeChildSessionParams extends ChildSessionParams { } /** Represents an IPv6 DNS Server request */ - public interface ConfigRequestIpv6DnsServer extends TunnelModeChildConfigRequest { - /** - * Retrieves the requested IPv6 DNS server address - * - * @return The requested DNS server address, or null if no specific DNS server was requested - * @hide - */ - @Nullable - Inet6Address getAddress(); - } + public interface ConfigRequestIpv6DnsServer extends TunnelModeChildConfigRequest {} /** This class can be used to incrementally construct a {@link TunnelModeChildSessionParams}. */ public static final class Builder extends ChildSessionParams.Builder { diff --git a/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java index 458f2610..1ed89b97 100644 --- a/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java +++ b/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java @@ -78,6 +78,9 @@ abstract class AbstractSessionStateMachine extends StateMachine { // Use a value greater than the retransmit-failure timeout. static final long REKEY_DELETE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(180L); + // Default delay time for retrying a request + static final long RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15L); + protected final Executor mUserCbExecutor; private final String mLogTag; diff --git a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java index 8697ab50..fd500b8f 100644 --- a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java +++ b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java @@ -79,7 +79,6 @@ 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; @@ -104,8 +103,10 @@ import java.net.InetAddress; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -221,6 +222,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { @VisibleForTesting final State mRekeyChildRemoteCreate = new RekeyChildRemoteCreate(); @VisibleForTesting final State mRekeyChildLocalDelete = new RekeyChildLocalDelete(); @VisibleForTesting final State mRekeyChildRemoteDelete = new RekeyChildRemoteDelete(); + @VisibleForTesting boolean mIsFirstChild; /** * Builds a new uninitialized ChildSessionStateMachine @@ -361,6 +363,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { this.mUdpEncapSocket = udpEncapSocket; this.mIkePrf = ikePrf; this.mSkD = skD; + mIsFirstChild = true; int spi = registerProvisionalChildAndGetSpi(respPayloads); sendMessage( @@ -391,6 +394,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { this.mUdpEncapSocket = udpEncapSocket; this.mIkePrf = ikePrf; this.mSkD = skD; + mIsFirstChild = false; sendMessage(CMD_LOCAL_REQUEST_CREATE_CHILD); } @@ -744,12 +748,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { mSkD, mChildSessionParams.isTransportMode(), true /*isLocalInit*/, - rekeyLocalRequest, buildSaLifetimeAlarmSched( createChildResult.respSpi.getSpi())); - mChildSmCallback.scheduleLocalRequest(rekeyLocalRequest, getRekeyTimeout()); - ChildSessionConfiguration sessionConfig = buildChildSessionConfigFromResp(createChildResult, respPayloads); executeUserCallback( @@ -772,9 +773,12 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { // TODO: Initiate deletion mChildSmCallback.onChildSaDeleted(createChildResult.respSpi.getSpi()); + handleChildFatalError(e); + } finally { + // In the successful case the transform in ChildSaRecord has taken ownership + // of the SPI (in IpSecService), and will keep it alive. createChildResult.initSpi.close(); createChildResult.respSpi.close(); - handleChildFatalError(e); } break; case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: @@ -881,19 +885,21 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { /** Initial state of ChildSessionStateMachine. */ class Initial extends CreateChildLocalCreateBase { + List<IkePayload> mRequestPayloads; + @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; + mRequestPayloads = 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, + mRequestPayloads, respPayloads, EXCHANGE_TYPE_IKE_AUTH, EXCHANGE_TYPE_IKE_AUTH, @@ -918,6 +924,11 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { return NOT_HANDLED; } } + + @Override + public void exitState() { + CreateChildSaHelper.releaseSpiResources(mRequestPayloads); + } } /** @@ -973,6 +984,11 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { return NOT_HANDLED; } } + + @Override + public void exitState() { + CreateChildSaHelper.releaseSpiResources(mRequestPayloads); + } } /** @@ -1264,13 +1280,18 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { @Override public void enterState() { try { + ChildSaProposal saProposal = mSaProposal; + if (mIsFirstChild) { + saProposal = addDhGroupsFromChildSessionParamsIfAbsent(); + } + // Build request with negotiated proposal and TS. mRequestPayloads = CreateChildSaHelper.getRekeyChildCreateReqPayloads( mRandomFactory, mIpSecSpiGenerator, mLocalAddress, - mSaProposal, + saProposal, mLocalTs, mRemoteTs, mCurrentChildSaRecord.getLocalSpi(), @@ -1282,8 +1303,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { ChildSessionStateMachine.this); } catch (SpiUnavailableException | ResourceUnavailableException e) { loge("Fail to assign Child SPI. Schedule a retry for rekey Child"); - mChildSmCallback.scheduleRetryLocalRequest( - (ChildLocalRequest) mCurrentChildSaRecord.getFutureRekeyEvent()); + mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); transitionTo(mIdle); } } @@ -1307,8 +1327,8 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { switch (createChildResult.status) { case CREATE_STATUS_OK: try { - // Do not need to update the negotiated proposal and TS because they - // are not changed. + // Do not need to update TS because they are not changed. + mSaProposal = createChildResult.negotiatedProposal; ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest(); @@ -1328,13 +1348,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { mSkD, mChildSessionParams.isTransportMode(), true /*isLocalInit*/, - rekeyLocalRequest, buildSaLifetimeAlarmSched( createChildResult.respSpi.getSpi())); - mChildSmCallback.scheduleLocalRequest( - rekeyLocalRequest, getRekeyTimeout()); - executeUserCallback( () -> { mUserCallback.onIpSecTransformCreated( @@ -1354,6 +1370,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { | IOException e) { // #makeChildSaRecord failed handleProcessRespOrSaCreationFailAndQuit(resp.registeredSpi, e); + } finally { + // In the successful case the transform in ChildSaRecord has taken + // ownership of the SPI (in IpSecService), and will keep it alive. createChildResult.initSpi.close(); createChildResult.respSpi.close(); } @@ -1363,18 +1382,10 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { 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()); - } + loge( + "Received error notification for rekey Child. Schedule a" + + " retry"); + mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); transitionTo(mIdle); break; @@ -1403,6 +1414,34 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { } handleChildFatalError(exception); } + + @Override + public void exitState() { + CreateChildSaHelper.releaseSpiResources(mRequestPayloads); + } + } + + private ChildSaProposal addDhGroupsFromChildSessionParamsIfAbsent() { + // DH groups are excluded for the first child. Add dh groups from child session params in + // this case. + if (mSaProposal.getDhGroups().size() != 0) { + return mSaProposal; + } + + Set<DhGroupTransform> dhGroupSet = new LinkedHashSet<>(); + for (SaProposal saProposal : mChildSessionParams.getSaProposals()) { + if (!mSaProposal.isNegotiatedFromExceptDhGroup(saProposal)) continue; + dhGroupSet.addAll(Arrays.asList(saProposal.getDhGroupTransforms())); + } + + DhGroupTransform[] dhGroups = new DhGroupTransform[dhGroupSet.size()]; + dhGroupSet.toArray(dhGroups); + + return new ChildSaProposal( + mSaProposal.getEncryptionTransforms(), + mSaProposal.getIntegrityTransforms(), + dhGroups, + mSaProposal.getEsnTransforms()); } /** @@ -1448,7 +1487,21 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { IkeSaPayload reqSaPayload = IkePayload.getPayloadForTypeInProvidedList( PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); - byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(mSaProposal); + + IkeKePayload reqKePayload = + IkePayload.getPayloadForTypeInProvidedList( + PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads); + + ChildSaProposal saProposal = mSaProposal; + + // Try accepting a DH group requested during remote rekey for both first and + // additional Child Sessions even if it is different from the previously negotiated + // proposal. + if (reqKePayload != null && isKePayloadAcceptable(reqKePayload)) { + saProposal = mSaProposal.getCopyWithAdditionalDhTransform(reqKePayload.dhGroup); + } + + byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(saProposal); respPayloads = CreateChildSaHelper.getRekeyChildCreateRespPayloads( @@ -1456,7 +1509,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { mIpSecSpiGenerator, mLocalAddress, respProposalNumber, - mSaProposal, + saProposal, mLocalTs, mRemoteTs, mCurrentChildSaRecord.getLocalSpi(), @@ -1483,8 +1536,8 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { switch (createChildResult.status) { case CREATE_STATUS_OK: try { - // Do not need to update the negotiated proposal and TS - // because they are not changed. + // Do not need to update TS because they are not changed. + mSaProposal = createChildResult.negotiatedProposal; ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest(); @@ -1504,12 +1557,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { mSkD, mChildSessionParams.isTransportMode(), false /*isLocalInit*/, - rekeyLocalRequest, buildSaLifetimeAlarmSched( createChildResult.initSpi.getSpi())); - mChildSmCallback.scheduleLocalRequest(rekeyLocalRequest, getRekeyTimeout()); - mChildSmCallback.onChildSaCreated( mRemoteInitNewChildSaRecord.getRemoteSpi(), ChildSessionStateMachine.this); @@ -1537,12 +1587,14 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { | SpiUnavailableException | IOException e) { // #makeChildSaRecord failed. - createChildResult.initSpi.close(); - createChildResult.respSpi.close(); - handleCreationFailureAndBackToIdle( new NoValidProposalChosenException( "Error in Child SA creation", e)); + } finally { + // In the successful case the transform in ChildSaRecord has taken ownership + // of the SPI (in IpSecService), and will keep it alive. + createChildResult.initSpi.close(); + createChildResult.respSpi.close(); } break; case CREATE_STATUS_CHILD_ERROR_INVALID_MSG: @@ -1568,6 +1620,20 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { } } + private boolean isKePayloadAcceptable(IkeKePayload reqKePayload) { + ChildSaProposal proposal = + mSaProposal.getCopyWithAdditionalDhTransform(reqKePayload.dhGroup); + + // Verify if this proposal is accepted by user + for (SaProposal saProposal : mChildSessionParams.getSaProposals()) { + if (proposal.isNegotiatedFrom(saProposal)) { + return true; + } + } + + return false; + } + private void handleCreationFailureAndBackToIdle(IkeProtocolException e) { loge("Received invalid Rekey Child request. Reject with error notification", e); @@ -2005,6 +2071,19 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { return hasExpectedRekeyNotify; } + public static void releaseSpiResources(List<IkePayload> reqPayloads) { + if (reqPayloads == null) { + return; + } + + IkeSaPayload saPayload = + IkePayload.getPayloadForTypeInProvidedList( + IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); + if (saPayload != null) { + saPayload.releaseChildSpiResourcesIfExists(); + } + } + /** Validate the received payload list and negotiate Child SA. */ private static CreateChildResult validateAndNegotiateChild( List<IkePayload> reqPayloads, diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java index e86df232..f8008fbe 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java +++ b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java @@ -64,15 +64,18 @@ public final class IkeLocalRequestScheduler { * Notifies the scheduler that the caller is ready for a new procedure * * <p>Synchronously triggers the call to onNewProcedureReady. + * + * @return whether or not a new procedure was scheduled. */ - public void readyForNextProcedure() { + public boolean readyForNextProcedure() { while (!mRequestQueue.isEmpty()) { LocalRequest request = mRequestQueue.poll(); if (!request.isCancelled()) { mConsumer.onNewProcedureReady(request); - return; + return true; } } + return false; } /** diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java index 87eab421..1a3bf18b 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java +++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java @@ -22,6 +22,7 @@ import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_I 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 android.os.PowerManager.PARTIAL_WAKE_LOCK; import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_TYPE_REPLY; import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL; @@ -34,9 +35,14 @@ import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY 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_AUTH; 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_EAP; 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.PAYLOAD_TYPE_VENDOR; import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_CHILD; import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_IKE; @@ -74,6 +80,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.PowerManager; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; @@ -125,7 +132,6 @@ 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.IkeProposal; -import com.android.internal.net.ipsec.ike.message.IkeTsPayload; import com.android.internal.net.ipsec.ike.message.IkeVendorPayload; import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver; import com.android.internal.net.ipsec.ike.utils.IkeSecurityParameterIndex; @@ -214,9 +220,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { // Default fragment size in bytes. @VisibleForTesting static final int DEFAULT_FRAGMENT_SIZE = 1280; - // 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 @@ -368,6 +371,9 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { */ private final IpSecSpiGenerator mIpSecSpiGenerator; + /** Ensures that the system does not go to sleep in the middle of an exchange. */ + private final PowerManager.WakeLock mBusyWakeLock; + @VisibleForTesting @GuardedBy("mChildCbToSessions") final HashMap<ChildSessionCallback, ChildSessionStateMachine> mChildCbToSessions = @@ -495,6 +501,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { // callback instance in the future } + PowerManager pm = context.getSystemService(PowerManager.class); + mBusyWakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, TAG + "mBusyWakeLock"); + mBusyWakeLock.setReferenceCounted(false); + mIkeSessionId = sIkeSessionIdGenerator.getAndIncrement(); sIkeAlarmReceiver.registerIkeSession(mIkeSessionId, getHandler()); @@ -551,6 +561,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { sendMessageAtFrontOfQueue(CMD_EXECUTE_LOCAL_REQ, localReq); }); + mBusyWakeLock.acquire(); start(); } @@ -731,15 +742,13 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { } } - /** 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); + /** + * Schedule temporary failure timeout. + * + * <p>Caller of this method is responsible for scheduling retry of the rejected request. + */ + public void handleTempFailure() { + logd("TempFailureHandler: Receive TEMPORARY FAILURE"); if (!mTempFailureReceived) { sendEmptyMessageDelayed(TEMP_FAILURE_RETRY_TIMEOUT, TEMP_FAILURE_RETRY_TIMEOUT_MS); @@ -773,8 +782,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { // 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 @@ -944,6 +951,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { } } + // Release IPsec SPIs if IKE Session is terminated before receiving the IKE AUTH response + // that contains the first child SA proposal + CreateChildSaHelper.releaseSpiResources(mFirstChildReqList); + if (mIkeNattKeepalive != null) { mIkeNattKeepalive.stop(); } @@ -963,6 +974,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { } // TODO: Remove the stored ikeSessionCallback } + + mBusyWakeLock.release(); } private void closeAllSaRecords(boolean expectSaClosed) { @@ -1065,7 +1078,9 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { @Override public void enterState() { - mScheduler.readyForNextProcedure(); + if (!mScheduler.readyForNextProcedure()) { + mBusyWakeLock.release(); + } if (mDpdIntent == null) { long remoteIkeSpi = mCurrentIkeSaRecord.getRemoteSpi(); @@ -1097,6 +1112,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { // #exitState is guaranteed to be invoked when quit() or quitNow() is called mAlarmManager.cancel(mDpdIntent); logd("DPD Alarm canceled"); + + mBusyWakeLock.acquire(); } @Override @@ -1200,7 +1217,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { return obtainMessage(CMD_ALARM_FIRED, mIkeSessionId, localRequestType, spiBundle); } - private SaLifetimeAlarmScheduler buildSaLifetimeAlarmScheduler(long remoteSpi) { + @VisibleForTesting + SaLifetimeAlarmScheduler buildSaLifetimeAlarmScheduler(long remoteSpi) { PendingIntent deleteSaIntent = buildIkeAlarmIntent( mContext, @@ -1470,14 +1488,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { // Software keepalive alarm is fired mIkeNattKeepalive.onAlarmFired(); return; - case CMD_LOCAL_REQUEST_DELETE_CHILD: - // Child SA (identified by remoteChildSpi) has hit its hard lifetime + case CMD_LOCAL_REQUEST_DELETE_CHILD: // Hits hard lifetime; fall through + case CMD_LOCAL_REQUEST_REKEY_CHILD: // Hits soft lifetime enqueueChildLocalRequest(message); return; - case CMD_LOCAL_REQUEST_DELETE_IKE: - // IKE SA hits its hard lifetime - enqueueIkeLocalRequest(message); - return; + case CMD_LOCAL_REQUEST_DELETE_IKE: // Hits hard lifetime; fall through + case CMD_LOCAL_REQUEST_REKEY_IKE: // Hits soft lifetime; fall through case CMD_LOCAL_REQUEST_DPD: // IKE Session has not received any protectd IKE packet for the whole DPD delay enqueueIkeLocalRequest(message); @@ -2215,7 +2231,9 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { @Override protected void handleTempFailure() { - mTempFailHandler.handleTempFailure(mLocalRequestOngoing); + // The ChildSessionStateMachine will be responsible for rescheduling the rejected + // request. + mTempFailHandler.handleTempFailure(); } private void transitionToIdleIfAllProceduresDone() { @@ -2694,7 +2712,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { mIkePrf, mIkeIntegrity == null ? 0 : mIkeIntegrity.getKeyLength(), mIkeCipher.getKeyLength(), - new IkeLocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE), buildSaLifetimeAlarmScheduler(mRemoteIkeSpiResource.getSpi())); addIkeSaRecord(mCurrentIkeSaRecord); @@ -2882,12 +2899,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { 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."); + throw new InvalidSyntaxException("SA, KE, or Nonce payload missing."); } IkeSaPayload reqSaPayload = @@ -2915,6 +2928,20 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { throw new InvalidSyntaxException("Received KE payload with mismatched DH group."); } + if (mRemoteAddress instanceof Inet4Address) { + handleNatDetection(respMsg, natSourcePayloads, natDestPayload); + } + } + + private void handleNatDetection( + IkeMessage respMsg, + List<IkeNotifyPayload> natSourcePayloads, + IkeNotifyPayload natDestPayload) + throws InvalidSyntaxException, IOException { + if (natSourcePayloads.isEmpty() || natDestPayload == null) { + throw new InvalidSyntaxException("NAT detection notifications missing."); + } + // NAT detection long initIkeSpi = respMsg.ikeHeader.ikeInitiatorSpi; long respIkeSpi = respMsg.ikeHeader.ikeResponderSpi; @@ -2952,7 +2979,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { IkeUdpEncapSocket.getIkeUdpEncapSocket( mIkeSessionParams.getNetwork(), mIpSecManager, - IkeSessionStateMachine.this); + IkeSessionStateMachine.this, + getHandler().getLooper()); switchToIkeSocket(initIkeSpi, newSocket); } catch (ErrnoException | IOException | ResourceUnavailableException e) { handleIkeFatalError(e); @@ -3070,50 +3098,26 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { 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); - - IkeConfigPayload configPayload = - ikeMessage.getPayloadForType( - IkePayload.PAYLOAD_TYPE_CP, IkeConfigPayload.class); - - boolean hasErrorNotify = false; List<IkePayload> list = new LinkedList<>(); - for (IkeNotifyPayload payload : notifyPayloads) { - if (payload.isNewChildSaNotify()) { - list.add(payload); - if (payload.isErrorNotify()) { - hasErrorNotify = true; - } + for (IkePayload payload : ikeMessage.ikePayloadList) { + switch (payload.payloadType) { + case PAYLOAD_TYPE_SA: // fall through + case PAYLOAD_TYPE_TS_INITIATOR: // fall through + case PAYLOAD_TYPE_TS_RESPONDER: // fall through + case PAYLOAD_TYPE_CP: + list.add(payload); + break; + case PAYLOAD_TYPE_NOTIFY: + if (((IkeNotifyPayload) payload).isNewChildSaNotify()) { + list.add(payload); + } + break; + default: + // Ignore payloads unrelated with Child negotiation } } - // 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); - - if (configPayload != null) { - list.add(configPayload); - } - + // Payload validation is done in ChildSessionStateMachine return list; } @@ -3194,12 +3198,11 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { "Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType); } + validateIkeAuthResp(ikeMessage); + List<IkePayload> childReqList = extractChildPayloadsFromMessage(mRetransmitter.getMessage()); - if (mUseEap) { - validateIkeAuthRespWithEapPayload(ikeMessage); - // childReqList needed after EAP completed, so persist to IkeSessionStateMachine // state. mFirstChildReqList = childReqList; @@ -3207,12 +3210,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { IkeEapPayload ikeEapPayload = ikeMessage.getPayloadForType( IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class); - + if (ikeEapPayload == null) { + throw new AuthenticationFailedException("Missing EAP payload"); + } deferMessage(obtainMessage(CMD_EAP_START_EAP_AUTH, ikeEapPayload)); transitionTo(mCreateIkeLocalIkeAuthInEap); } else { - validateIkeAuthRespWithChildPayloads(ikeMessage); - notifyIkeSessionSetup(ikeMessage); performFirstChildNegotiation( @@ -3325,40 +3328,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { 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 { + private void validateIkeAuthResp(IkeMessage authResp) throws IkeProtocolException { // Validate IKE Authentication IkeAuthPayload authPayload = null; List<IkeCertPayload> certPayloads = new LinkedList<>(); - for (IkePayload payload : payloadList) { + for (IkePayload payload : authResp.ikePayloadList) { switch (payload.payloadType) { case IkePayload.PAYLOAD_TYPE_ID_RESPONDER: mRespIdPayload = (IkeIdPayload) payload; @@ -3380,7 +3355,18 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { case IkePayload.PAYLOAD_TYPE_NOTIFY: IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; if (notifyPayload.isErrorNotify()) { - throw notifyPayload.validateAndBuildIkeException(); + if (notifyPayload.isNewChildSaNotify() + && authResp.getPayloadForType( + PAYLOAD_TYPE_AUTH, IkeAuthPayload.class) + != null) { + // If error is for creating Child and Auth payload is included, try + // to do authentication first and let ChildSessionStateMachine + // handle the error later. + continue; + } else { + throw notifyPayload.validateAndBuildIkeException(); + } + } else { // Unknown and unexpected status notifications are ignored as per // RFC7296. @@ -3390,6 +3376,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { + notifyPayload.notifyType); } break; + case PAYLOAD_TYPE_SA: // Will be handled separately; fall through + case PAYLOAD_TYPE_CP: // Will be handled separately; fall through + case PAYLOAD_TYPE_TS_INITIATOR: // Will be handled separately; fall through + case PAYLOAD_TYPE_TS_RESPONDER: // Will be handled separately; fall through + case PAYLOAD_TYPE_EAP: // Will be handled separately + break; default: logw( "Received unexpected payload in IKE AUTH response. Payload" @@ -3675,18 +3667,11 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { "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); - + validateIkeAuthRespPostEap(ikeMessage); notifyIkeSessionSetup(ikeMessage); - performFirstChildNegotiation(mFirstChildReqList, childSaRespPayloads); + performFirstChildNegotiation( + mFirstChildReqList, extractChildPayloadsFromMessage(ikeMessage)); } catch (IkeProtocolException e) { // Notify the remote because they may have set up the IKE SA. sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord)); @@ -3703,11 +3688,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { handleIkeFatalError(ikeException); } - private void validateIkeAuthRespPostEap(List<IkePayload> payloadList) - throws IkeProtocolException { + private void validateIkeAuthRespPostEap(IkeMessage authResp) throws IkeProtocolException { IkeAuthPayload authPayload = null; - for (IkePayload payload : payloadList) { + for (IkePayload payload : authResp.ikePayloadList) { switch (payload.payloadType) { case IkePayload.PAYLOAD_TYPE_AUTH: authPayload = (IkeAuthPayload) payload; @@ -3715,7 +3699,18 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { case IkePayload.PAYLOAD_TYPE_NOTIFY: IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload; if (notifyPayload.isErrorNotify()) { - throw notifyPayload.validateAndBuildIkeException(); + if (notifyPayload.isNewChildSaNotify() + && authResp.getPayloadForType( + PAYLOAD_TYPE_AUTH, IkeAuthPayload.class) + != null) { + // If error is for creating Child and Auth payload is included, try + // to do authentication first and let ChildSessionStateMachine + // handle the error later. + continue; + } else { + throw notifyPayload.validateAndBuildIkeException(); + } + } else { // Unknown and unexpected status notifications are ignored as per // RFC7296. @@ -3725,6 +3720,11 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { + notifyPayload.notifyType); } break; + case PAYLOAD_TYPE_SA: // Will be handled separately; fall through + case PAYLOAD_TYPE_CP: // Will be handled separately; fall through + case PAYLOAD_TYPE_TS_INITIATOR: // Will be handled separately; fall through + case PAYLOAD_TYPE_TS_RESPONDER: // Will be handled separately; fall through + break; default: logw( "Received unexpected payload in IKE AUTH response. Payload" @@ -3878,7 +3878,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { + firstErrorNotifyPayload.notifyType + " for rekey IKE. Schedule a retry"); if (!isSimulRekey) { - scheduleRetry(mCurrentIkeSaRecord.getFutureRekeyEvent()); + mCurrentIkeSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); } } @@ -3940,7 +3940,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { newIntegrity == null ? 0 : newIntegrity.getKeyLength(), newCipher.getKeyLength(), isLocalInit, - new IkeLocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE), buildSaLifetimeAlarmScheduler(remoteSpi)); addIkeSaRecord(newSaRecord); @@ -3969,7 +3968,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { mRetransmitter = new EncryptedRetransmitter(buildIkeRekeyReq()); } catch (IOException e) { loge("Fail to assign IKE SPI for rekey. Schedule a retry.", e); - scheduleRetry(mCurrentIkeSaRecord.getFutureRekeyEvent()); + mCurrentIkeSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); transitionTo(mIdle); } } @@ -3981,7 +3980,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { @Override protected void handleTempFailure() { - mTempFailHandler.handleTempFailure(mCurrentIkeSaRecord.getFutureRekeyEvent()); + mTempFailHandler.handleTempFailure(); + mCurrentIkeSaRecord.rescheduleRekey(RETRY_INTERVAL_MS); } /** @@ -4666,21 +4666,23 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine { selectedDhGroup, IkeSaPayload.createInitialIkeSaPayload(saProposals), randomFactory); + if (localAddr instanceof Inet4Address) { + // 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))); + } - // 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; } diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java b/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java index 8553f30d..1dbc9c1f 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java +++ b/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java @@ -27,11 +27,13 @@ import android.util.LongSparseArray; 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.FileInputStream; +import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -53,7 +55,7 @@ import java.util.Set; * IkeSessionStateMachine}. Since all {@link IkeSessionStateMachine}s run on the same working * thread, there will not be concurrent modification problems. */ -public abstract class IkeSocket extends PacketReader implements AutoCloseable { +public abstract class IkeSocket implements AutoCloseable { private static final String TAG = "IkeSocket"; /** Non-udp-encapsulated IKE packets MUST be sent to 500. */ @@ -63,6 +65,7 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable { // Network this socket bound to. private final Network mNetwork; + private final Handler mHandler; // Map from locally generated IKE SPI to IkeSessionStateMachine instances. @VisibleForTesting @@ -74,7 +77,7 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable { protected final Set<IkeSessionStateMachine> mAliveIkeSessions = new HashSet<>(); protected IkeSocket(Network network, Handler handler) { - super(handler); + mHandler = handler; mNetwork = network; } @@ -107,6 +110,53 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable { } } + /** Starts the packet reading poll-loop. */ + public void start() { + // Start background reader thread + new Thread( + () -> { + try { + // Loop will exit and thread will quit when the retrieved fd is closed. + // Receiving either EOF or an exception will exit this reader loop. + // FileInputStream in uninterruptable, so there's no good way to ensure that + // that this thread shuts down except upon FD closure. + while (true) { + byte[] intercepted = receiveFromFd(); + if (intercepted == null) { + // Exit once we've hit EOF + return; + } else if (intercepted.length > 0) { + // Only save packet if we've received any bytes. + getIkeLog() + .d( + this.getClass().getSimpleName(), + "Received packet"); + mHandler.post( + () -> { + handlePacket(intercepted, intercepted.length); + }); + } + } + } catch (IOException ignored) { + // Simply exit this reader thread + return; + } + }).start(); + } + + private byte[] receiveFromFd() throws IOException { + FileInputStream in = new FileInputStream(getFd()); + byte[] inBytes = new byte[4096]; + int bytesRead = in.read(inBytes); + + if (bytesRead < 0) { + return null; // return null for EOF + } else if (bytesRead >= 4096) { + throw new IllegalStateException("Too big packet. Fragmentation unsupported"); + } + return Arrays.copyOf(inBytes, bytesRead); + } + /** * Returns the port that this IKE socket is listening on (bound to). */ @@ -117,6 +167,12 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable { protected abstract FileDescriptor getFd(); + protected FileDescriptor createFd() { + return getFd(); + } + + protected abstract void handlePacket(byte[] recvbuf, int length); + /** * Return Network this socket bound to * @@ -176,6 +232,12 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable { stop(); } + /** Stops the packet reading loop */ + public void stop() { + // No additional cleanup at this time. Subclasses are in charge of closing their sockets, + // which will result in the packet reading poll loop exiting. + } + /** * IPacketReceiver provides a package private interface for handling received packet. * diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java b/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java index 2eb10104..11bb9340 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java +++ b/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java @@ -26,6 +26,7 @@ import android.net.IpSecManager.ResourceUnavailableException; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.Network; import android.os.Handler; +import android.os.Looper; import android.system.ErrnoException; import android.system.Os; import android.util.LongSparseArray; @@ -81,7 +82,10 @@ public final class IkeUdpEncapSocket extends IkeSocket { * @return an IkeUdpEncapSocket instance */ public static IkeUdpEncapSocket getIkeUdpEncapSocket( - Network network, IpSecManager ipsecManager, IkeSessionStateMachine ikeSession) + Network network, + IpSecManager ipsecManager, + IkeSessionStateMachine ikeSession, + Looper looper) throws ErrnoException, IOException, ResourceUnavailableException { IkeUdpEncapSocket ikeSocket = sNetworkToIkeSocketMap.get(network); if (ikeSocket == null) { @@ -92,7 +96,7 @@ public final class IkeUdpEncapSocket extends IkeSocket { Os.fcntlInt(fd, F_SETFL, SOCK_DGRAM | SOCK_NONBLOCK); network.bindSocket(fd); - ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, network, new Handler()); + ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, network, new Handler(looper)); // Create and register FileDescriptor for receiving IKE packet on current thread. ikeSocket.start(); @@ -112,21 +116,10 @@ public final class IkeUdpEncapSocket extends IkeSocket { return mUdpEncapSocket; } + /** Get FileDescriptor for use with the packet reader polling loop */ @Override protected FileDescriptor getFd() { - return createFd(); // Returns cached FD - } - - /** - * Get FileDescriptor of mUdpEncapSocket. - * - * <p>PacketReader registers a listener for this file descriptor on the thread where - * IkeUdpEncapSocket is constructed. When there is a read event, this listener is invoked and - * then calls {@link handlePacket} to handle the received packet. - */ - @Override - protected FileDescriptor createFd() { - return mUdpEncapSocket.getFileDescriptor(); + return mUdpEncapSocket.getFileDescriptor(); // Returns cached FD } /** Package private */ diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java b/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java index a0bb29ad..b6130ad5 100644 --- a/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java +++ b/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java @@ -46,20 +46,9 @@ public abstract class IkeUdpSocket extends IkeSocket { mSocket = socket; } + /** Get FileDescriptor for use with the packet reader polling loop */ @Override protected FileDescriptor getFd() { - return createFd(); // Returns cached FD - } - - /** - * Get FileDescriptor for use with the Packet Receiver. - * - * <p>PacketReader registers a listener for this file descriptor on the thread where - * IkeUdpSocket is constructed. When there is a read event, this listener is invoked and then - * calls {@link handlePacket} to handle the received packet. - */ - @Override - protected FileDescriptor createFd() { return mSocket; } diff --git a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java index a0b585fc..c2d86dc9 100644 --- a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java +++ b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java @@ -31,8 +31,6 @@ import android.os.SystemClock; import android.util.CloseGuard; 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.crypto.IkeCipher; import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf; @@ -82,10 +80,7 @@ public abstract class SaRecord implements AutoCloseable { private final byte[] mSkEi; private final byte[] mSkEr; - private final SaLifetimeAlarmScheduler mSaLifetimeAlarmScheduler; - - // TODO(b/149058810): Use AlarmManager and PendingIntent to schedule rekey - private final LocalRequest mFutureRekeyEvent; + @VisibleForTesting final SaLifetimeAlarmScheduler mSaLifetimeAlarmScheduler; private final CloseGuard mCloseGuard = new CloseGuard(); @@ -98,7 +93,6 @@ public abstract class SaRecord implements AutoCloseable { byte[] skAr, byte[] skEi, byte[] skEr, - LocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) { isLocalInit = localInit; nonceInitiator = nonceInit; @@ -117,9 +111,6 @@ public abstract class SaRecord implements AutoCloseable { mSaLifetimeAlarmScheduler = saLifetimeAlarmScheduler; mSaLifetimeAlarmScheduler.scheduleLifetimeExpiryAlarm(getTag()); - // TODO(b/149058810): Use alarmManager to schedule rekey event and remove mFutureRekeyEvent - mFutureRekeyEvent = futureRekeyEvent; - mCloseGuard.open("close"); } @@ -167,6 +158,11 @@ public abstract class SaRecord implements AutoCloseable { return isLocalInit ? mSkEr : mSkEi; } + /** Reschedule rekey */ + public void rescheduleRekey(long retryDelayMs) { + mSaLifetimeAlarmScheduler.rescheduleRekey(retryDelayMs); + } + /** Check that the SaRecord was closed properly. */ @Override protected void finalize() throws Throwable { @@ -178,17 +174,10 @@ public abstract class SaRecord implements AutoCloseable { @Override public void close() { - mFutureRekeyEvent.cancel(); - mSaLifetimeAlarmScheduler.cancelLifetimeExpiryAlarm(getTag()); } /** Package private */ - LocalRequest getFutureRekeyEvent() { - return mFutureRekeyEvent; - } - - /** Package private */ @VisibleForTesting static void setSaRecordHelper(ISaRecordHelper helper) { sSaRecordHelper = helper; @@ -336,7 +325,6 @@ public abstract class SaRecord implements AutoCloseable { skEr, skPi, skPr, - ikeSaRecordConfig.futureRekeyEvent, ikeSaRecordConfig.saLifetimeAlarmScheduler); } @@ -471,8 +459,7 @@ public abstract class SaRecord implements AutoCloseable { skEr, inTransform, outTransform, - childSaRecordConfig.futureRekeyEvent, - childSaRecordConfig.saLifetimeAlarmSched); + childSaRecordConfig.saLifetimeAlarmScheduler); } catch (Exception e) { if (initTransform != null) initTransform.close(); @@ -560,9 +547,26 @@ public abstract class SaRecord implements AutoCloseable { AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + mDeleteDelayMs, mDeleteSaIntent); - getIkeLog().d(tag, "Hard lifetime expiry alarm set for " + mDeleteDelayMs + "ms"); + mAlarmManager.setExactAndAllowWhileIdle( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + mRekeyDelayMs, + mRekeySaIntent); + + getIkeLog() + .d( + tag, + "Lifetime alarm set: Hard lifetime (" + + mDeleteDelayMs + + "ms) Soft lifetime (" + + mRekeyDelayMs + + "ms)"); + } - // TODO: Schedule alarm for rekey + public void rescheduleRekey(long retryDelayMs) { + mAlarmManager.setExactAndAllowWhileIdle( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + retryDelayMs, + mRekeySaIntent); } public void cancelLifetimeExpiryAlarm(String tag) { @@ -591,8 +595,7 @@ public abstract class SaRecord implements AutoCloseable { public final boolean isTransport; public final boolean isLocalInit; public final boolean hasIntegrityAlgo; - public final ChildLocalRequest futureRekeyEvent; - public final SaLifetimeAlarmScheduler saLifetimeAlarmSched; + public final SaLifetimeAlarmScheduler saLifetimeAlarmScheduler; ChildSaRecordConfig( Context context, @@ -607,8 +610,7 @@ public abstract class SaRecord implements AutoCloseable { byte[] skD, boolean isTransport, boolean isLocalInit, - ChildLocalRequest futureRekeyEvent, - SaLifetimeAlarmScheduler saLifetimeAlarmSched) { + SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) { this.context = context; this.initSpi = initSpi; this.respSpi = respSpi; @@ -622,8 +624,7 @@ public abstract class SaRecord implements AutoCloseable { this.isTransport = isTransport; this.isLocalInit = isLocalInit; hasIntegrityAlgo = (integrityAlgo != null); - this.futureRekeyEvent = futureRekeyEvent; - this.saLifetimeAlarmSched = saLifetimeAlarmSched; + this.saLifetimeAlarmScheduler = saLifetimeAlarmScheduler; } } @@ -663,7 +664,6 @@ public abstract class SaRecord implements AutoCloseable { byte[] skEr, byte[] skPi, byte[] skPr, - LocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) { super( localInit, @@ -673,7 +673,6 @@ public abstract class SaRecord implements AutoCloseable { skAr, skEi, skEr, - futureRekeyEvent, saLifetimeAlarmScheduler); mInitiatorSpiResource = initSpi; @@ -706,7 +705,6 @@ public abstract class SaRecord implements AutoCloseable { IkeMacPrf prf, int integrityKeyLength, int encryptionKeyLength, - LocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) throws GeneralSecurityException { return sSaRecordHelper.makeFirstIkeSaRecord( @@ -719,7 +717,6 @@ public abstract class SaRecord implements AutoCloseable { integrityKeyLength, encryptionKeyLength, true /*isLocalInit*/, - futureRekeyEvent, saLifetimeAlarmScheduler)); } @@ -735,7 +732,6 @@ public abstract class SaRecord implements AutoCloseable { int integrityKeyLength, int encryptionKeyLength, boolean isLocalInit, - LocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) throws GeneralSecurityException { return sSaRecordHelper.makeRekeyedIkeSaRecord( @@ -750,7 +746,6 @@ public abstract class SaRecord implements AutoCloseable { integrityKeyLength, encryptionKeyLength, isLocalInit, - futureRekeyEvent, saLifetimeAlarmScheduler)); } @@ -929,7 +924,6 @@ public abstract class SaRecord implements AutoCloseable { public final int integrityKeyLength; public final int encryptionKeyLength; public final boolean isLocalInit; - public final LocalRequest futureRekeyEvent; public final SaLifetimeAlarmScheduler saLifetimeAlarmScheduler; IkeSaRecordConfig( @@ -939,7 +933,6 @@ public abstract class SaRecord implements AutoCloseable { int integrityKeyLength, int encryptionKeyLength, boolean isLocalInit, - LocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) { this.initSpi = initSpi; this.respSpi = respSpi; @@ -947,7 +940,6 @@ public abstract class SaRecord implements AutoCloseable { this.integrityKeyLength = integrityKeyLength; this.encryptionKeyLength = encryptionKeyLength; this.isLocalInit = isLocalInit; - this.futureRekeyEvent = futureRekeyEvent; this.saLifetimeAlarmScheduler = saLifetimeAlarmScheduler; } } @@ -979,7 +971,6 @@ public abstract class SaRecord implements AutoCloseable { byte[] skEr, IpSecTransform inTransform, IpSecTransform outTransform, - ChildLocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) { super( localInit, @@ -989,7 +980,6 @@ public abstract class SaRecord implements AutoCloseable { skAr, skEi, skEr, - futureRekeyEvent, saLifetimeAlarmScheduler); mInboundSpi = inSpi; @@ -1017,7 +1007,6 @@ public abstract class SaRecord implements AutoCloseable { byte[] skD, boolean isTransport, boolean isLocalInit, - ChildLocalRequest futureRekeyEvent, SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) throws GeneralSecurityException, ResourceUnavailableException, SpiUnavailableException, IOException { @@ -1037,7 +1026,6 @@ public abstract class SaRecord implements AutoCloseable { skD, isTransport, isLocalInit, - futureRekeyEvent, saLifetimeAlarmScheduler)); } diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java new file mode 100644 index 00000000..92f32074 --- /dev/null +++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.net.ipsec.ike.message; + +import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CERT_ENCODING_LEN; +import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CertificateEncoding; + +import android.net.ipsec.ike.exceptions.IkeProtocolException; + +import java.nio.ByteBuffer; + +/** + * This class represents a Certificate Request Payload + * + * <p>A Certificate Request Payload provides suggestion for an end certificate to select. Receiver + * of this payload is allowed to send an alternate. It is possible that there is a preferred CA sent + * in the IkeCertReqPayload, but an alternate is still acceptable. + * + * <p>IKE library will always ignore this payload since only one end certificate can be configured + * by users. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2)</a> + */ +public class IkeCertReqPayload extends IkePayload { + /** Certificate encoding type */ + @CertificateEncoding public final int certEncodingType; + /** Concatenated list of SHA-1 hashes of CAs' Subject Public Key Info */ + public final byte[] caSubjectPublicKeyInforHashes; + + /** + * Construct an instance of IkeCertReqPayload from decoding an inbound IKE packet. + * + * <p>NegativeArraySizeException and BufferUnderflowException will be caught in {@link + * IkeMessage} + * + * @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 + */ + public IkeCertReqPayload(boolean critical, byte[] payloadBody) throws IkeProtocolException { + super(PAYLOAD_TYPE_CERT_REQUEST, critical); + + ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); + certEncodingType = Byte.toUnsignedInt(inputBuffer.get()); + caSubjectPublicKeyInforHashes = new byte[inputBuffer.remaining()]; + inputBuffer.get(caSubjectPublicKeyInforHashes); + } + + /** + * Encode Certificate Request 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) certEncodingType).put(caSubjectPublicKeyInforHashes); + } + + /** + * Get entire payload length. + * + * @return entire payload length. + */ + @Override + protected int getPayloadLength() { + return GENERIC_HEADER_LENGTH + CERT_ENCODING_LEN + caSubjectPublicKeyInforHashes.length; + } + + /** + * Return the payload type as a String. + * + * @return the payload type as a String. + */ + @Override + public String getTypeString() { + return "CertReq"; + } +} 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 index bb5f9a39..778faa41 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java +++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java @@ -538,7 +538,6 @@ public final class IkeConfigPayload extends IkePayload { super(CONFIG_ATTR_INTERNAL_IP4_DHCP, value); } - @Override public Inet4Address getAddress() { return address; } @@ -572,7 +571,6 @@ public final class IkeConfigPayload extends IkePayload { super(CONFIG_ATTR_INTERNAL_IP4_DNS, value); } - @Override public Inet4Address getAddress() { return address; } @@ -979,7 +977,6 @@ public final class IkeConfigPayload extends IkePayload { super(CONFIG_ATTR_INTERNAL_IP6_DNS, value); } - @Override public Inet6Address getAddress() { return address; } 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 index 1c5f1f6b..3da649ac 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java +++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java @@ -503,10 +503,12 @@ public final class IkeMessage { int fragNum = i + 1; // 1-based + int fragFirstInnerPayload = + i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT; IkeSkfPayload skfPayload = new IkeSkfPayload( - ikeHeader, - firstInnerPayload, + skfHeader, + fragFirstInnerPayload, unencryptedData, integrityMac, encryptCipher, @@ -515,11 +517,7 @@ public final class IkeMessage { fragNum, totalFragments); - packetList[i] = - encodeHeaderAndBody( - skfHeader, - skfPayload, - i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT); + packetList[i] = encodeHeaderAndBody(skfHeader, skfPayload, fragFirstInnerPayload); getIkeLog() .d( "IkeMessage", @@ -528,7 +526,7 @@ public final class IkeMessage { + "/" + totalFragments + "): " - + getIkeLog().pii(packetList[0])); + + getIkeLog().pii(packetList[i])); } return packetList; 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 index b9087777..30145338 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java +++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java @@ -16,6 +16,7 @@ package com.android.internal.net.ipsec.ike.message; +import static android.net.ipsec.ike.IkeManager.getIkeLog; 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; @@ -74,6 +75,8 @@ import java.util.Set; * Version 2 (IKEv2)</a> */ public final class IkeNotifyPayload extends IkeInformationalPayload { + private static final String TAG = IkeNotifyPayload.class.getSimpleName(); + @Retention(RetentionPolicy.SOURCE) @IntDef({ NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, @@ -267,8 +270,7 @@ public final class IkeNotifyPayload extends IkeInformationalPayload { private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException { if (protocolId != PROTOCOL_ID_UNSET) { - throw new InvalidSyntaxException( - "Expected Procotol ID unset: Protocol ID is " + protocolId); + getIkeLog().w(TAG, "Expected Procotol ID unset: Protocol ID is " + protocolId); } if (notifyType == ERROR_TYPE_INVALID_SELECTORS diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java index 2f191d4e..b50f0934 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java +++ b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java @@ -60,7 +60,6 @@ final class IkePayloadFactory { int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody) throws IkeProtocolException { switch (payloadType) { - // TODO: Add cases for creating supported payloads. case IkePayload.PAYLOAD_TYPE_SA: return new IkeSaPayload(isCritical, isResp, payloadBody); case IkePayload.PAYLOAD_TYPE_KE: @@ -71,6 +70,8 @@ final class IkePayloadFactory { return new IkeIdPayload(isCritical, payloadBody, false); case IkePayload.PAYLOAD_TYPE_CERT: return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody); + case IkeCertReqPayload.PAYLOAD_TYPE_CERT_REQUEST: + return new IkeCertReqPayload(isCritical, payloadBody); case IkePayload.PAYLOAD_TYPE_AUTH: return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody); case IkePayload.PAYLOAD_TYPE_NONCE: diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java index d2dd3943..40eecf02 100644 --- a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java +++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java @@ -514,7 +514,20 @@ public final class IkeSaPayload extends IkePayload { Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) throws IkeProtocolException; } - // TODO: Add another constructor for building outbound message. + /** + * Release IPsec SPI resources in the outbound Create Child request + * + * <p>This method is usually called when an IKE library fails to receive a Create Child response + * before it is terminated. It is also safe to call after the Create Child exchange has + * succeeded because the newly created IpSecTransform pair will hold the IPsec SPI resource. + */ + public void releaseChildSpiResourcesIfExists() { + for (Proposal proposal : proposalList) { + if (proposal instanceof ChildProposal) { + proposal.releaseSpiResourceIfExists(); + } + } + } /** * This class represents the common information of an IKE Proposal and a Child Proposal. diff --git a/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java b/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java index 9ebb057a..2e5d1703 100644 --- a/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java +++ b/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java @@ -31,7 +31,7 @@ import java.security.SecureRandom; public class RandomnessFactory implements EapRandomFactory { // This constant is mirrored of android.net.NetworkCapabilities.TRANSPORT_TEST due to lack of // @TestApi guarantees in mainline modules - public static final int NETWORK_CAPABILITY_TRANSPORT_TEST = 7; + public static final int TRANSPORT_TEST = 7; private final boolean mIsTestModeEnabled; @@ -41,8 +41,7 @@ public class RandomnessFactory implements EapRandomFactory { NetworkCapabilities networkCapabilities = connectManager.getNetworkCapabilities(network); mIsTestModeEnabled = - networkCapabilities != null - && networkCapabilities.hasCapability(NETWORK_CAPABILITY_TRANSPORT_TEST); + networkCapabilities != null && networkCapabilities.hasTransport(TRANSPORT_TEST); } /** diff --git a/tests/iketests/AndroidManifest.xml b/tests/iketests/AndroidManifest.xml index 0abddbaa..bdcfdb71 100644 --- a/tests/iketests/AndroidManifest.xml +++ b/tests/iketests/AndroidManifest.xml @@ -22,6 +22,8 @@ <uses-permission android:name="android.permission.INTERNET"/> <!--Allow tests to call ConnectivityManager#getActiveNetwork()--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <!--Allow tests to use wake locks--> + <uses-permission android:name="android.permission.WAKE_LOCK"/> <!-- 'debuggable=true' is required to properly load mockito jvmti dependencies, diff --git a/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java b/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java index d4efb0c3..0c98bfa0 100644 --- a/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java +++ b/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java @@ -16,8 +16,15 @@ package android.net.ipsec.ike; +import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_NONE; import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128; import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -318,4 +325,30 @@ public final class SaProposalTest { new Transform[] {mIntegrityNoneTransform}, new Transform[] {mIntegrityHmacSha1Transform})); } + + @Test + public void testIsNegotiatedFromProposalWithIntegrityNone() throws Exception { + SaProposal respProposal = + new IkeSaProposal.Builder() + .addEncryptionAlgorithm( + ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128) + .addDhGroup(DH_GROUP_2048_BIT_MODP) + .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC) + .build(); + + SaProposal reqProposal = + new IkeSaProposal.Builder() + .addEncryptionAlgorithm( + ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128) + .addEncryptionAlgorithm( + ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128) + .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_NONE) + .addDhGroup(DH_GROUP_1024_BIT_MODP) + .addDhGroup(DH_GROUP_2048_BIT_MODP) + .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC) + .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_256) + .build(); + + assertTrue(respProposal.isNegotiatedFrom(reqProposal)); + } } 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 index 20e4b76b..e20d0b1d 100644 --- 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 @@ -22,6 +22,7 @@ import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_T import static android.system.OsConstants.AF_INET; import static com.android.internal.net.TestUtils.createMockRandomFactory; +import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.RETRY_INTERVAL_MS; 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; @@ -47,11 +48,9 @@ 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; @@ -77,6 +76,7 @@ import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionParams; import android.net.ipsec.ike.IkeManager; +import android.net.ipsec.ike.IkeSaProposal; import android.net.ipsec.ike.IkeTrafficSelector; import android.net.ipsec.ike.SaProposal; import android.net.ipsec.ike.TunnelModeChildSessionParams; @@ -224,8 +224,9 @@ public final class ChildSessionStateMachineTest { private ArgumentMatcher<ChildLocalRequest> mRekeyChildLocalReqMatcher = (argument) -> { - return CMD_LOCAL_REQUEST_REKEY_CHILD == argument.procedureType - && mMockChildSessionCallback == argument.childSessionCallback; + return (CMD_LOCAL_REQUEST_REKEY_CHILD == argument.procedureType + && mMockChildSessionCallback == argument.childSessionCallback + || CURRENT_CHILD_SA_SPI_OUT == argument.remoteSpi); }; public ChildSessionStateMachineTest() { @@ -270,19 +271,7 @@ public final class ChildSessionStateMachineTest { // Setup thread and looper mLooper = new TestLooper(); - mChildSessionStateMachine = - new ChildSessionStateMachine( - mLooper.getLooper(), - mContext, - IKE_SESSION_UNIQUE_ID, - mMockAlarmManager, - createMockRandomFactory(), - mMockIpSecManager, - mIpSecSpiGenerator, - mChildSessionParams, - mSpyUserCbExecutor, - mMockChildSessionCallback, - mMockChildSessionSmCallback); + mChildSessionStateMachine = buildChildSession(mChildSessionParams); mChildSessionStateMachine.setDbg(true); SaRecord.setSaRecordHelper(mMockSaRecordHelper); @@ -396,7 +385,6 @@ public final class ChildSessionStateMachineTest { null, mock(IpSecTransform.class), mock(IpSecTransform.class), - mock(ChildLocalRequest.class), mock(SaLifetimeAlarmScheduler.class))); doNothing().when(child).close(); return child; @@ -438,11 +426,7 @@ public final class ChildSessionStateMachineTest { assertFalse(childSaRecordConfig.isTransport); assertEquals(isLocalInit, childSaRecordConfig.isLocalInit); assertTrue(childSaRecordConfig.hasIntegrityAlgo); - assertEquals( - CMD_LOCAL_REQUEST_REKEY_CHILD, childSaRecordConfig.futureRekeyEvent.procedureType); - assertEquals( - mMockChildSessionCallback, - childSaRecordConfig.futureRekeyEvent.childSessionCallback); + assertNotNull(childSaRecordConfig.saLifetimeAlarmScheduler); } private void verifyNotifyUsersCreateIpSecSa( @@ -492,8 +476,6 @@ public final class ChildSessionStateMachineTest { verify(mMockChildSessionSmCallback) .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine)); - verify(mMockChildSessionSmCallback) - .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong()); verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine); assertTrue( mChildSessionStateMachine.getCurrentState() @@ -951,6 +933,14 @@ public final class ChildSessionStateMachineTest { (IkeSaPayload) (IkeTestUtils.hexStringToIkePayload( IkePayload.PAYLOAD_TYPE_SA, true, inboundSaHexString)); + + return makeInboundRekeyChildPayloads(remoteSpi, saPayload, isLocalInitRekey); + } + + private List<IkePayload> makeInboundRekeyChildPayloads( + int remoteSpi, IkeSaPayload saPayload, boolean isLocalInitRekey) throws Exception { + List<IkePayload> inboundPayloads = new LinkedList<>(); + inboundPayloads.add(saPayload); // Build TS Payloads @@ -1023,8 +1013,6 @@ public final class ChildSessionStateMachineTest { .onChildSaCreated( eq(mSpyLocalInitNewChildSaRecord.getRemoteSpi()), eq(mChildSessionStateMachine)); - verify(mMockChildSessionSmCallback) - .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong()); verify(mMockSaRecordHelper) .makeChildSaRecord( @@ -1061,9 +1049,7 @@ public final class ChildSessionStateMachineTest { mLooper.dispatchAll(); // Verify rekey has been rescheduled and Child Session is alive - verify(mMockChildSessionSmCallback) - .scheduleRetryLocalRequest( - (ChildLocalRequest) mSpyCurrentChildSaRecord.getFutureRekeyEvent()); + verify(mSpyCurrentChildSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS)); assertTrue( mChildSessionStateMachine.getCurrentState() instanceof ChildSessionStateMachine.Idle); @@ -1261,6 +1247,8 @@ public final class ChildSessionStateMachineTest { IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads); mLooper.dispatchAll(); + assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size()); + assertTrue( mChildSessionStateMachine.getCurrentState() instanceof ChildSessionStateMachine.RekeyChildRemoteDelete); @@ -1287,8 +1275,6 @@ public final class ChildSessionStateMachineTest { .onChildSaCreated( eq(mSpyRemoteInitNewChildSaRecord.getRemoteSpi()), eq(mChildSessionStateMachine)); - verify(mMockChildSessionSmCallback) - .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong()); verify(mMockSaRecordHelper) .makeChildSaRecord( @@ -1674,4 +1660,123 @@ public final class ChildSessionStateMachineTest { verifyHandleFatalErrorAndQuit(IkeInternalException.class); verify(spyIkeLog).wtf(anyString(), anyString(), any(RuntimeException.class)); } + + @Test + public void testFirstChildLocalRekey() throws Exception { + ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP); + ChildSessionParams childSessionParams = + new TunnelModeChildSessionParams.Builder() + .addSaProposal(saProposal) + .addInternalAddressRequest(AF_INET) + .addInternalAddressRequest(INTERNAL_ADDRESS) + .build(); + mChildSessionStateMachine = buildChildSession(childSessionParams); + mChildSessionStateMachine.mIsFirstChild = true; + mChildSessionStateMachine.setDbg(true); + mChildSessionStateMachine.start(); + + setupIdleStateMachine(); + + assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size()); + + // Send Rekey-Create request + mChildSessionStateMachine.rekeyChildSession(); + mLooper.dispatchAll(); + + assertTrue( + mChildSessionStateMachine.getCurrentState() + instanceof ChildSessionStateMachine.RekeyChildLocalCreate); + + verifyOutboundRekeyKePayload(false /*isResp*/); + } + + private void verifyOutboundRekeyKePayload(boolean isResp) { + verify(mMockChildSessionSmCallback) + .onOutboundPayloadsReady( + eq(EXCHANGE_TYPE_CREATE_CHILD_SA), + eq(isResp), + mPayloadListCaptor.capture(), + eq(mChildSessionStateMachine)); + + // Verify outbound payload list + List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue(); + + assertNotNull( + IkePayload.getPayloadForTypeInProvidedList( + PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloadList)); + } + + private ChildSessionStateMachine buildChildSession(ChildSessionParams childSessionParams) { + return new ChildSessionStateMachine( + mLooper.getLooper(), + mContext, + IKE_SESSION_UNIQUE_ID, + mMockAlarmManager, + createMockRandomFactory(), + mMockIpSecManager, + mIpSecSpiGenerator, + childSessionParams, + mSpyUserCbExecutor, + mMockChildSessionCallback, + mMockChildSessionSmCallback); + } + + private ChildSaProposal buildSaProposalWithDhGroup(int dhGroup) { + return new ChildSaProposal.Builder() + .addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addDhGroup(dhGroup) + .build(); + } + + @Test + public void testRemoteRekeyWithKePayload() throws Exception { + // Use child session params with dh group to initiate the state machine + ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP); + ChildSessionParams childSessionParams = + new TunnelModeChildSessionParams.Builder() + .addSaProposal(saProposal) + .addInternalAddressRequest(AF_INET) + .addInternalAddressRequest(INTERNAL_ADDRESS) + .build(); + mChildSessionStateMachine = buildChildSession(childSessionParams); + mChildSessionStateMachine.setDbg(true); + mChildSessionStateMachine.start(); + + 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); + + IkeSaPayload saPayload = + IkeSaPayload.createChildSaRequestPayload( + new ChildSaProposal[] {saProposal}, mIpSecSpiGenerator, LOCAL_ADDRESS); + List<IkePayload> rekeyReqPayloads = + makeInboundRekeyChildPayloads( + REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /*isLocalInitRekey*/); + + rekeyReqPayloads.add( + new IkeKePayload(IkeSaProposal.DH_GROUP_2048_BIT_MODP, createMockRandomFactory())); + + when(mMockSaRecordHelper.makeChildSaRecord( + eq(rekeyReqPayloads), any(List.class), any(ChildSaRecordConfig.class))) + .thenReturn(mSpyRemoteInitNewChildSaRecord); + + assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size()); + + // 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); + + verifyOutboundRekeyKePayload(true /*isResp*/); + + assertEquals(1, mChildSessionStateMachine.mSaProposal.getDhGroups().size()); + } } 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 index 8fb4ebba..6fe4aada 100644 --- 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 @@ -18,7 +18,9 @@ package com.android.internal.net.ipsec.ike; import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH; +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_INTERNAL_ADDRESS_FAILURE; 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; @@ -28,7 +30,7 @@ import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static com.android.internal.net.TestUtils.createMockRandomFactory; -import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE; +import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.RETRY_INTERVAL_MS; 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; @@ -67,6 +69,7 @@ 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.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @@ -641,10 +644,25 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { new byte[KEY_LEN_IKE_ENCR], TestUtils.hexStringToByteArray(PRF_KEY_INIT_HEX_STRING), TestUtils.hexStringToByteArray(PRF_KEY_RESP_HEX_STRING), - new IkeLocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE), mock(SaLifetimeAlarmScheduler.class)); } + private void mockScheduleRekey(SaLifetimeAlarmScheduler mockSaLifetimeAlarmScheduler) { + IkeLocalRequest rekeyReq = + new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE); + doAnswer( + (invocation) -> { + mIkeSessionStateMachine.sendMessageDelayed( + IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE, + rekeyReq, + mIkeSessionStateMachine.mIkeSessionParams + .getSoftLifetimeMsInternal()); + return null; + }) + .when(mockSaLifetimeAlarmScheduler) + .scheduleLifetimeExpiryAlarm(anyString()); + } + @Before public void setUp() throws Exception { super.setUp(); @@ -755,13 +773,11 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { ikeSession.mLocalAddress = LOCAL_ADDRESS; assertEquals(REMOTE_ADDRESS, ikeSession.mRemoteAddress); - // Get socket instances used by the IkeSessionStateMachine by virtue of the caching. - mSpyIkeUdp4Socket = spy(IkeUdp4Socket.getInstance(mMockDefaultNetwork, ikeSession)); - mSpyIkeUdp6Socket = spy(IkeUdp6Socket.getInstance(mMockDefaultNetwork, ikeSession)); - mSpyIkeUdpEncapSocket = - spy( - IkeUdpEncapSocket.getIkeUdpEncapSocket( - mMockDefaultNetwork, mIpSecManager, ikeSession)); + // Setup socket instances used by the IkeSessionStateMachine + // TODO: rename these from spy to mock. + mSpyIkeUdp4Socket = mock(IkeUdp4Socket.class); + mSpyIkeUdp6Socket = mock(IkeUdp6Socket.class); + mSpyIkeUdpEncapSocket = mock(IkeUdpEncapSocket.class); doNothing().when(mSpyIkeUdp4Socket).sendIkePacket(any(), any()); doNothing().when(mSpyIkeUdp6Socket).sendIkePacket(any(), any()); @@ -1182,7 +1198,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { mLooper.dispatchAll(); verify(mSpyCurrentIkeSocket).releaseReference(eq(mIkeSessionStateMachine)); - verify(mSpyCurrentIkeSocket).close(); + verify(mMockBusyWakelock).release(); } @Test @@ -1216,6 +1232,9 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { .thenAnswer( (invocation) -> { captureAndReleaseIkeSpiResource(invocation, 2); + mockScheduleRekey(mSpyCurrentIkeSaRecord.mSaLifetimeAlarmScheduler); + mSpyCurrentIkeSaRecord.mSaLifetimeAlarmScheduler + .scheduleLifetimeExpiryAlarm(anyString()); return mSpyCurrentIkeSaRecord; }); } @@ -1228,6 +1247,9 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { .thenAnswer( (invocation) -> { captureAndReleaseIkeSpiResource(invocation, 4); + mockScheduleRekey(rekeySaRecord.mSaLifetimeAlarmScheduler); + rekeySaRecord.mSaLifetimeAlarmScheduler.scheduleLifetimeExpiryAlarm( + anyString()); return rekeySaRecord; }); } @@ -1268,7 +1290,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { public void testEnableTestMode() throws Exception { doReturn(true) .when(mMockNetworkCapabilities) - .hasCapability(RandomnessFactory.NETWORK_CAPABILITY_TRANSPORT_TEST); + .hasTransport(RandomnessFactory.TRANSPORT_TEST); IkeSessionStateMachine ikeSession = makeAndStartIkeSession(buildIkeSessionParams()); @@ -1281,7 +1303,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { public void testDisableTestMode() throws Exception { doReturn(false) .when(mMockNetworkCapabilities) - .hasCapability(RandomnessFactory.NETWORK_CAPABILITY_TRANSPORT_TEST); + .hasTransport(RandomnessFactory.TRANSPORT_TEST); IkeSessionStateMachine ikeSession = makeAndStartIkeSession(buildIkeSessionParams()); @@ -1331,9 +1353,6 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { // Validate socket switched assertTrue(mIkeSessionStateMachine.mIkeSocket instanceof IkeUdpEncapSocket); verify(mSpyIkeUdp4Socket).unregisterIke(anyLong()); - - // Validate keepalive has started - verify(mMockSocketKeepalive).start(anyInt()); } @Ignore @@ -1403,7 +1422,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { 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); + assertNotNull(ikeSaRecordConfig.saLifetimeAlarmScheduler); // Validate NAT detection assertTrue(mIkeSessionStateMachine.mIsLocalBehindNat); @@ -1441,6 +1460,8 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { /** Initializes the mIkeSessionStateMachine in the IDLE state. */ private void setupIdleStateMachine() throws Exception { + verify(mMockBusyWakelock).acquire(); + setIkeInitResults(); mIkeSessionStateMachine.sendMessage( @@ -1453,6 +1474,11 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { assertTrue( mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + + verify(mMockBusyWakelock).release(); + + // For convenience to verify wakelocks in all other places. + reset(mMockBusyWakelock); } private void mockIkeInitAndTransitionToIkeAuth(State authState) throws Exception { @@ -1614,6 +1640,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.ChildProcedureOngoing); verify(mMockChildSessionStateMachine).deleteChildSession(); + verify(mMockBusyWakelock).acquire(); } @Test @@ -1648,6 +1675,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.ChildProcedureOngoing); verify(mMockChildSessionStateMachine).rekeyChildSession(); + verify(mMockBusyWakelock).acquire(); } @Test @@ -1787,6 +1815,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/); assertEquals(1, payloadList.size()); assertEquals(outDelPayload, ((IkeDeletePayload) payloadList.get(0))); + verify(mMockBusyWakelock).acquire(); } @Test @@ -2501,6 +2530,48 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { } @Test + public void testAuthHandlesIkeErrorNotify() throws Exception { + mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); + verifyRetransmissionStarted(); + resetMockIkeMessageHelper(); + + // Mock rejecting IKE AUTH with Authenticatio Failure notification + ReceivedIkePacket mockAuthFailPacket = + makeIkeAuthRespWithoutChildPayloads( + Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_AUTHENTICATION_FAILED))); + mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockAuthFailPacket); + mLooper.dispatchAll(); + + // Verify IKE Session is closed properly + assertNull(mIkeSessionStateMachine.getCurrentState()); + verify(mMockIkeSessionCallback) + .onClosedExceptionally(any(AuthenticationFailedException.class)); + } + + @Test + public void testAuthHandlesCreateChildErrorNotify() throws Exception { + mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); + verifyRetransmissionStarted(); + resetMockIkeMessageHelper(); + + // Mock rejecting IKE AUTH with a Create Child error notification + ReceivedIkePacket mockAuthFailPacket = + makeIkeAuthRespWithoutChildPayloads( + Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE))); + mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockAuthFailPacket); + mLooper.dispatchAll(); + + // Verify IKE Session is closed properly + assertNull(mIkeSessionStateMachine.getCurrentState()); + + ArgumentCaptor<IkeProtocolException> captor = + ArgumentCaptor.forClass(IkeProtocolException.class); + verify(mMockIkeSessionCallback).onClosedExceptionally(captor.capture()); + IkeProtocolException exception = captor.getValue(); + assertEquals(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, exception.getErrorType()); + } + + @Test public void testAuthPskHandleRespWithParsingError() throws Exception { mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth); verifyRetransmissionStarted(); @@ -3077,17 +3148,10 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, resp); mLooper.dispatchAll(); - // Verify IKE Session goes back to Idle + // Verify IKE Session goes back to Idle and retry is scheduled + verify(mSpyCurrentIkeSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS)); 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 @@ -3189,6 +3253,40 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); } + private void mockRescheduleRekey(IkeSaRecord spySaRecord) { + IkeLocalRequest rekeyReq = + new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE); + doAnswer( + (invocation) -> { + mIkeSessionStateMachine.sendMessageDelayed( + IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE, + rekeyReq, + RETRY_INTERVAL_MS); + return null; + }) + .when(spySaRecord) + .rescheduleRekey(eq(RETRY_INTERVAL_MS)); + } + + @Test + public void testRekeyIkeLocalCreateHandleRespWithTempFailure() throws Exception { + setupIdleStateMachine(); + + // Send Rekey-Create request + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, + new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); + mLooper.dispatchAll(); + + // Mock sending TEMPORARY_FAILURE response + mockRcvTempFail(); + mLooper.dispatchAll(); + + verify(mSpyCurrentIkeSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS)); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + } + private void mockCreateAndTransitionToRekeyDeleteLocal() { // Seed fake rekey data and force transition to RekeyIkeLocalDelete mIkeSessionStateMachine.mLocalInitNewIkeSaRecord = mSpyLocalInitIkeSaRecord; @@ -4065,6 +4163,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { // Verify state machine quit properly assertNull(mIkeSessionStateMachine.getCurrentState()); + verify(mMockBusyWakelock).release(); } @Test @@ -4392,24 +4491,6 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { 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(); - } - @Ignore public void disableTestTempFailureHandlerTimeout() throws Exception { long currentTime = 0; @@ -4437,28 +4518,48 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { @Test public void testTempFailureHandlerCancelTimer() throws Exception { - mockSendRekeyChildReq(); + mockRescheduleRekey(mSpyCurrentIkeSaRecord); + setupIdleStateMachine(); + + // Send Rekey-Create request + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ, + new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE)); + mLooper.dispatchAll(); + verifyRetransmissionStarted(); // Mock sending TEMPORARY_FAILURE response mockRcvTempFail(); + mLooper.dispatchAll(); + verify(mSpyCurrentIkeSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS)); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); // Move time forward to trigger retry mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS); mLooper.dispatchAll(); - verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession(); + assertTrue( + mIkeSessionStateMachine.getCurrentState() + instanceof IkeSessionStateMachine.RekeyIkeLocalCreate); - // 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); + // Prepare "rekeyed" SA + setupRekeyedIkeSa(mSpyLocalInitIkeSaRecord); + + // Receive valid Rekey-Create response + ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = makeRekeyIkeResponse(); mIkeSessionStateMachine.sendMessage( - IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle); + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket); mLooper.dispatchAll(); + // Receive Delete response + ReceivedIkePacket dummyDeleteIkeRespReceivedPacket = + makeDeleteIkeResponse(mSpyCurrentIkeSaRecord); + mIkeSessionStateMachine.sendMessage( + IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRespReceivedPacket); + mLooper.dispatchAll(); + assertTrue( + mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); + // Move time forward mLooper.moveTimeForward(IkeSessionStateMachine.TEMP_FAILURE_RETRY_TIMEOUT_MS); mLooper.dispatchAll(); @@ -4466,8 +4567,6 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase { // Validate IKE Session is not closed assertTrue( mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle); - // Validate no more retry - verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession(); } @Ignore diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java index 3728f368..e4e83794 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java +++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java @@ -18,8 +18,11 @@ package com.android.internal.net.ipsec.ike; import static org.mockito.Matchers.any; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -33,6 +36,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.SocketKeepalive; import android.os.Handler; +import android.os.PowerManager; import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils; import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver; @@ -50,9 +54,12 @@ public abstract class IkeSessionTestBase { (Inet4Address) (InetAddresses.parseNumericAddress("127.0.0.1")); protected static final String REMOTE_HOSTNAME = "ike.test.android.com"; + protected PowerManager.WakeLock mMockBusyWakelock; + protected MockIpSecTestUtils mMockIpSecTestUtils; protected Context mSpyContext; protected IpSecManager mIpSecManager; + protected PowerManager mPowerManager; protected ConnectivityManager mMockConnectManager; protected Network mMockDefaultNetwork; @@ -74,6 +81,11 @@ public abstract class IkeSessionTestBase { any(Handler.class)); doNothing().when(mSpyContext).unregisterReceiver(any(IkeAlarmReceiver.class)); + mPowerManager = mock(PowerManager.class); + mMockBusyWakelock = mock(PowerManager.WakeLock.class); + doReturn(mPowerManager).when(mSpyContext).getSystemService(eq(PowerManager.class)); + doReturn(mMockBusyWakelock).when(mPowerManager).newWakeLock(anyInt(), anyString()); + mMockConnectManager = mock(ConnectivityManager.class); mMockDefaultNetwork = mock(Network.class); doReturn(mMockDefaultNetwork).when(mMockConnectManager).getActiveNetwork(); @@ -102,6 +114,6 @@ public abstract class IkeSessionTestBase { .getNetworkCapabilities(any(Network.class)); doReturn(false) .when(mMockNetworkCapabilities) - .hasCapability(RandomnessFactory.NETWORK_CAPABILITY_TRANSPORT_TEST); + .hasTransport(RandomnessFactory.TRANSPORT_TEST); } } diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java index 8c0bf4d5..439fa27e 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java +++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java @@ -118,12 +118,12 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase { IkeUdpEncapSocket ikeSocketOne = IkeUdpEncapSocket.getIkeUdpEncapSocket( - mMockNetwork, mSpyIpSecManager, mockIkeSessionOne); + mMockNetwork, mSpyIpSecManager, mockIkeSessionOne, Looper.myLooper()); assertEquals(1, ikeSocketOne.mAliveIkeSessions.size()); IkeUdpEncapSocket ikeSocketTwo = IkeUdpEncapSocket.getIkeUdpEncapSocket( - mMockNetwork, mSpyIpSecManager, mockIkeSessionTwo); + mMockNetwork, mSpyIpSecManager, mockIkeSessionTwo, Looper.myLooper()); assertEquals(2, ikeSocketTwo.mAliveIkeSessions.size()); assertEquals(ikeSocketOne, ikeSocketTwo); @@ -154,12 +154,12 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase { IkeUdpEncapSocket ikeSocketOne = IkeUdpEncapSocket.getIkeUdpEncapSocket( - mockNetworkOne, mSpyIpSecManager, mockIkeSessionOne); + mockNetworkOne, mSpyIpSecManager, mockIkeSessionOne, Looper.myLooper()); assertEquals(1, ikeSocketOne.mAliveIkeSessions.size()); IkeUdpEncapSocket ikeSocketTwo = IkeUdpEncapSocket.getIkeUdpEncapSocket( - mockNetworkTwo, mSpyIpSecManager, mockIkeSessionTwo); + mockNetworkTwo, mSpyIpSecManager, mockIkeSessionTwo, Looper.myLooper()); assertEquals(1, ikeSocketTwo.mAliveIkeSessions.size()); assertNotEquals(ikeSocketOne, ikeSocketTwo); @@ -192,7 +192,10 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase { // Send IKE packet IkeUdpEncapSocket ikeSocket = IkeUdpEncapSocket.getIkeUdpEncapSocket( - mMockNetwork, mSpyIpSecManager, mMockIkeSessionStateMachine); + mMockNetwork, + mSpyIpSecManager, + mMockIkeSessionStateMachine, + Looper.myLooper()); ikeSocket.sendIkePacket(mDataOne, IPV4_LOOPBACK); byte[] receivedData = receive(mDummyRemoteServerFd); @@ -227,7 +230,8 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase { IkeUdpEncapSocket.getIkeUdpEncapSocket( mMockNetwork, mSpyIpSecManager, - mMockIkeSessionStateMachine)); + mMockIkeSessionStateMachine, + mIkeThread.getLooper())); createLatch.countDown(); Log.d("IkeUdpEncapSocketTest", "IkeUdpEncapSocket created."); } catch (ErrnoException @@ -253,20 +257,18 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase { sendToIkeUdpEncapSocket(mDummyRemoteServerFd, mDataOne, IPV4_LOOPBACK); receiveLatch.await(); - assertEquals(1, ikeSocket.numPacketsReceived()); assertArrayEquals(mDataOne, packetReceiver.mReceivedData); // Send second packet. sendToIkeUdpEncapSocket(mDummyRemoteServerFd, mDataTwo, IPV4_LOOPBACK); receiveLatch.await(); - assertEquals(2, ikeSocket.numPacketsReceived()); assertArrayEquals(mDataTwo, packetReceiver.mReceivedData); // Close IkeUdpEncapSocket. TestCountDownLatch closeLatch = new TestCountDownLatch(); - ikeSocket - .getHandler() + mIkeThread + .getThreadHandler() .post( () -> { ikeSocket.releaseReference(mMockIkeSessionStateMachine); 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 index 644ba066..b193099d 100644 --- 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 @@ -24,6 +24,7 @@ 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.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -38,8 +39,6 @@ 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.SaRecord.ChildSaRecord; import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecordConfig; import com.android.internal.net.ipsec.ike.SaRecord.IIpSecTransformHelper; @@ -148,9 +147,6 @@ public final class SaRecordTest { private IkeMacIntegrity mHmacSha1IntegrityMac; private IkeCipher mAesCbcCipher; - private LocalRequest mMockFutureRekeyIkeEvent; - private ChildLocalRequest mMockFutureRekeyChildEvent; - private SaLifetimeAlarmScheduler mMockLifetimeAlarmScheduler; private SaRecordHelper mSaRecordHelper = new SaRecordHelper(); @@ -168,8 +164,6 @@ public final class SaRecordTest { SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)); - mMockFutureRekeyIkeEvent = mock(LocalRequest.class); - mMockFutureRekeyChildEvent = mock(ChildLocalRequest.class); mMockLifetimeAlarmScheduler = mock(SaLifetimeAlarmScheduler.class); } @@ -192,8 +186,7 @@ public final class SaRecordTest { IKE_AUTH_ALGO_KEY_LEN, IKE_ENCR_ALGO_KEY_LEN, true /*isLocalInit*/, - mMockFutureRekeyIkeEvent, - mock(SaLifetimeAlarmScheduler.class)); + mMockLifetimeAlarmScheduler); int keyMaterialLen = IKE_SK_D_KEY_LEN @@ -225,10 +218,10 @@ public final class SaRecordTest { TestUtils.hexStringToByteArray(IKE_SK_PRF_INIT_HEX_STRING), ikeSaRecord.getSkPi()); assertArrayEquals( TestUtils.hexStringToByteArray(IKE_SK_PRF_RESP_HEX_STRING), ikeSaRecord.getSkPr()); + verify(mMockLifetimeAlarmScheduler).scheduleLifetimeExpiryAlarm(anyString()); ikeSaRecord.close(); - - verify(mMockFutureRekeyIkeEvent).cancel(); + verify(mMockLifetimeAlarmScheduler).cancelLifetimeExpiryAlarm(anyString()); } // Test generating keying material and building IpSecTransform for making Child SA. @@ -306,7 +299,6 @@ public final class SaRecordTest { TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING), false /*isTransport*/, true /*isLocalInit*/, - mMockFutureRekeyChildEvent, mMockLifetimeAlarmScheduler); ChildSaRecord childSaRecord = @@ -332,8 +324,10 @@ public final class SaRecordTest { TestUtils.hexStringToByteArray(FIRST_CHILD_ENCR_RESP_HEX_STRING), childSaRecord.getInboundDecryptionKey()); + verify(mMockLifetimeAlarmScheduler).scheduleLifetimeExpiryAlarm(anyString()); + childSaRecord.close(); - verify(mMockFutureRekeyChildEvent).cancel(); + verify(mMockLifetimeAlarmScheduler).cancelLifetimeExpiryAlarm(anyString()); SaRecord.setIpSecTransformHelper(new IpSecTransformHelper()); } diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java new file mode 100644 index 00000000..f859a822 --- /dev/null +++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.net.ipsec.ike.message; + +import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CERTIFICATE_ENCODING_X509_CERT_SIGNATURE; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.util.Pair; + +import com.android.internal.net.TestUtils; + +import org.junit.Test; + +import java.nio.ByteBuffer; + +public class IkeCertReqPayloadTest { + private static final int CERT_ENCODING_TYPE = CERTIFICATE_ENCODING_X509_CERT_SIGNATURE; + private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_AUTH; + private static final byte[] CERT_REQ_PAYLOAD = + TestUtils.hexStringToByteArray("27000019040d0a12bb1f98996563f15b10db95c67eea7990fa"); + private static final byte[] CA_SUBJECT_PUBLIC_KEY_INFO_HASH = + TestUtils.hexStringToByteArray("0d0a12bb1f98996563f15b10db95c67eea7990fa"); + + @Test + public void testDecode() throws Exception { + Pair<IkePayload, Integer> pair = + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_CERT_REQUEST, + false /*isResp*/, + ByteBuffer.wrap(CERT_REQ_PAYLOAD)); + + IkeCertReqPayload certPayload = (IkeCertReqPayload) pair.first; + assertEquals(CERT_ENCODING_TYPE, certPayload.certEncodingType); + assertArrayEquals( + CA_SUBJECT_PUBLIC_KEY_INFO_HASH, certPayload.caSubjectPublicKeyInforHashes); + + assertEquals(NEXT_PAYLOAD_TYPE, (int) pair.second); + } + + @Test + public void testEncode() throws Exception { + Pair<IkePayload, Integer> pair = + IkePayloadFactory.getIkePayload( + IkePayload.PAYLOAD_TYPE_CERT_REQUEST, + false /*isResp*/, + ByteBuffer.wrap(CERT_REQ_PAYLOAD)); + IkeCertReqPayload certPayload = (IkeCertReqPayload) pair.first; + + ByteBuffer byteBuffer = ByteBuffer.allocate(CERT_REQ_PAYLOAD.length); + certPayload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, byteBuffer); + assertArrayEquals(CERT_REQ_PAYLOAD, byteBuffer.array()); + + assertEquals(CERT_REQ_PAYLOAD.length, certPayload.getPayloadLength()); + } +} 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 index 884c76eb..0b3c3583 100644 --- 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 @@ -85,19 +85,6 @@ public final class IkeNotifyPayloadTest { } @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); |