diff options
5 files changed, 207 insertions, 34 deletions
diff --git a/src/java/com/android/ike/ikev2/message/IkePayload.java b/src/java/com/android/ike/ikev2/message/IkePayload.java index 332450f7..11ec94a9 100644 --- a/src/java/com/android/ike/ikev2/message/IkePayload.java +++ b/src/java/com/android/ike/ikev2/message/IkePayload.java @@ -37,6 +37,11 @@ public abstract class IkePayload { /** Length of a generic IKE payload header */ public static final int GENERIC_HEADER_LENGTH = 4; + /** + * Payload types as defined by IANA: + * + * @see <a href="https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml"> + */ @Retention(RetentionPolicy.SOURCE) @IntDef({ PAYLOAD_TYPE_NO_NEXT, @@ -49,6 +54,8 @@ public abstract class IkePayload { PAYLOAD_TYPE_NONCE, PAYLOAD_TYPE_NOTIFY, PAYLOAD_TYPE_VENDOR, + PAYLOAD_TYPE_TS_INITIATOR, + PAYLOAD_TYPE_TS_RESPONDER, PAYLOAD_TYPE_SK }) public @interface PayloadType {} @@ -73,6 +80,10 @@ public abstract class IkePayload { public static final int PAYLOAD_TYPE_NOTIFY = 41; /** Vendor Payload */ public static final int PAYLOAD_TYPE_VENDOR = 43; + /** Traffic Selector Payload of Child SA Initiator */ + public static final int PAYLOAD_TYPE_TS_INITIATOR = 44; + /** Traffic Selector Payload of Child SA Responder */ + public static final int PAYLOAD_TYPE_TS_RESPONDER = 45; /** Encrypted and Authenticated Payload */ public static final int PAYLOAD_TYPE_SK = 46; diff --git a/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java b/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java index a091618d..ef47b0ca 100644 --- a/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java +++ b/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java @@ -45,39 +45,49 @@ final class IkePayloadFactory { return (flagByte & PAYLOAD_HEADER_CRITICAL_BIT_SET) == PAYLOAD_HEADER_CRITICAL_BIT_SET; } - /** Default instance used for constructing IkePayload */ + /** Default IIkePayloadDecoder instance used for constructing IkePayload */ + static IIkePayloadDecoder sDecoderInstance = new IkePayloadDecoder(); + + /** + * IkePayloadDecoder implements IIkePayloadDecoder for constructing IkePayload from decoding + * received message. + * + * <p>Package private + */ @VisibleForTesting - static IkePayloadDecoder sDecoderInstance = - new IkePayloadDecoder() { - @Override - public IkePayload decodeIkePayload( - int payloadType, boolean isCritical, byte[] payloadBody) - throws IkeException { - switch (payloadType) { - // TODO: Add cases for creating supported payloads. - case IkePayload.PAYLOAD_TYPE_SA: - return new IkeSaPayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_KE: - return new IkeKePayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_ID_INITIATOR: - return new IkeIdPayload(isCritical, payloadBody, true); - case IkePayload.PAYLOAD_TYPE_ID_RESPONDER: - return new IkeIdPayload(isCritical, payloadBody, false); - case IkePayload.PAYLOAD_TYPE_CERT: - return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_AUTH: - return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_NONCE: - return new IkeNoncePayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_NOTIFY: - return new IkeNotifyPayload(isCritical, payloadBody); - case IkePayload.PAYLOAD_TYPE_VENDOR: - return new IkeVendorPayload(isCritical, payloadBody); - default: - return new IkeUnsupportedPayload(payloadType, isCritical); - } - } - }; + static class IkePayloadDecoder implements IIkePayloadDecoder { + @Override + public IkePayload decodeIkePayload(int payloadType, boolean isCritical, byte[] payloadBody) + throws IkeException { + switch (payloadType) { + // TODO: Add cases for creating supported payloads. + case IkePayload.PAYLOAD_TYPE_SA: + return new IkeSaPayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_KE: + return new IkeKePayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_ID_INITIATOR: + return new IkeIdPayload(isCritical, payloadBody, true); + case IkePayload.PAYLOAD_TYPE_ID_RESPONDER: + return new IkeIdPayload(isCritical, payloadBody, false); + case IkePayload.PAYLOAD_TYPE_CERT: + return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_AUTH: + return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_NONCE: + return new IkeNoncePayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_NOTIFY: + return new IkeNotifyPayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_VENDOR: + return new IkeVendorPayload(isCritical, payloadBody); + case IkePayload.PAYLOAD_TYPE_TS_INITIATOR: + return new IkeTsPayload(isCritical, payloadBody, true); + case IkePayload.PAYLOAD_TYPE_TS_RESPONDER: + return new IkeTsPayload(isCritical, payloadBody, false); + default: + return new IkeUnsupportedPayload(payloadType, isCritical); + } + } + } /** * Construct an instance of IkePayload according to its payload type. @@ -170,8 +180,14 @@ final class IkePayloadFactory { return new Pair(payload, nextPayloadType); } + /** + * IIkePayloadDecoder provides a package private interface for constructing IkePayload from + * decoding received message. + * + * <p>IIkePayloadDecoder exists so that the interface is injectable for testing. + */ @VisibleForTesting - interface IkePayloadDecoder { + interface IIkePayloadDecoder { IkePayload decodeIkePayload(int payloadType, boolean isCritical, byte[] payloadBody) throws IkeException; } diff --git a/src/java/com/android/ike/ikev2/message/IkeTsPayload.java b/src/java/com/android/ike/ikev2/message/IkeTsPayload.java new file mode 100644 index 00000000..339e92e6 --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeTsPayload.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ike.ikev2.message; + +import com.android.ike.ikev2.exceptions.IkeException; + +import java.nio.ByteBuffer; + +/** + * IkeTsPayload represents an Traffic Selector Initiator Payload or an Traffic Selector Responder + * Payload. + * + * <p>Traffic Selector Initiator Payload and Traffic Selector Responder Payload have same format but + * different payload types. They describe the address ranges and port ranges of Child SA initiator + * and Child SA responder. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.13">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2). + */ +public final class IkeTsPayload extends IkePayload { + // Length of reserved field in octets. + private static final int TS_HEADER_RESERVED_LEN = 3; + + /** Number of Traffic Selectors */ + public final int numTs; + + IkeTsPayload(boolean critical, byte[] payloadBody, boolean isInitiator) throws IkeException { + super((isInitiator ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER), critical); + + ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); + numTs = Byte.toUnsignedInt(inputBuffer.get()); + // Skip RESERVED byte + inputBuffer.get(new byte[TS_HEADER_RESERVED_LEN]); + + // TODO: Decode Traffic Selectors. + } + + /** + * Encode Traffic Selector Payload to ByteBuffer. + * + * @param nextPayload type of payload that follows this payload. + * @param byteBuffer destination ByteBuffer that stores encoded payload. + */ + @Override + protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { + throw new UnsupportedOperationException( + "It is not supported to encode a " + getTypeString()); + //TODO: Implement it. + } + + /** + * Get entire payload length. + * + * @return entire payload length. + */ + @Override + protected int getPayloadLength() { + throw new UnsupportedOperationException( + "It is not supported to get payload length of " + getTypeString()); + //TODO: Implement it. + } + + /** + * Return the payload type as a String. + * + * @return the payload type as a String. + */ + @Override + public String getTypeString() { + switch (payloadType) { + case PAYLOAD_TYPE_ID_INITIATOR: + return "Traffic Selector Initiator Payload"; + case PAYLOAD_TYPE_ID_RESPONDER: + return "Traffic Selector Responder Payload"; + default: + // Won't reach here. + throw new IllegalArgumentException( + "Invalid Payload Type for Traffic Selector Payload."); + } + } +} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java index 75f2ca20..8584f78a 100644 --- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeMessageTest.java @@ -24,6 +24,7 @@ import com.android.ike.ikev2.exceptions.IkeException; import com.android.ike.ikev2.exceptions.InvalidSyntaxException; import com.android.ike.ikev2.exceptions.UnsupportedCriticalPayloadException; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -93,7 +94,7 @@ public final class IkeMessageTest { @Before public void setUp() { IkePayloadFactory.sDecoderInstance = - new IkePayloadFactory.IkePayloadDecoder() { + new IkePayloadFactory.IIkePayloadDecoder() { @Override public IkePayload decodeIkePayload( @@ -108,6 +109,11 @@ public final class IkeMessageTest { }; } + @After + public void tearDown() { + IkePayloadFactory.sDecoderInstance = new IkePayloadFactory.IkePayloadDecoder(); + } + @Test public void testDecodeIkeMessage() throws Exception { byte[] inputPacket = TestUtils.hexStringToByteArray(IKE_SA_INIT_RAW_PACKET); diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeTsPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeTsPayloadTest.java new file mode 100644 index 00000000..f1c8b6e6 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeTsPayloadTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ike.ikev2.message; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import java.nio.ByteBuffer; + +public final class IkeTsPayloadTest { + private static final String TS_INITIATOR_PAYLOAD_HEX_STRING = + "2d00001801000000070000100000ffff00000000ffffffff"; + private static final int NUMBER_OF_TS = 1; + + @Test + public void testDecodeTsInitiatorPayload() throws Exception { + ByteBuffer inputBuffer = + ByteBuffer.wrap(TestUtils.hexStringToByteArray(TS_INITIATOR_PAYLOAD_HEX_STRING)); + + IkePayload payload = + IkePayloadFactory.getIkePayload(IkePayload.PAYLOAD_TYPE_TS_INITIATOR, inputBuffer) + .first; + assertTrue(payload instanceof IkeTsPayload); + + IkeTsPayload tsPayload = (IkeTsPayload) payload; + assertEquals(IkePayload.PAYLOAD_TYPE_TS_INITIATOR, tsPayload.payloadType); + assertEquals(NUMBER_OF_TS, tsPayload.numTs); + } +} |