Merge "Encode unencrypted IKE message to byte array"
diff --git a/src/java/com/android/ike/ikev2/SaProposal.java b/src/java/com/android/ike/ikev2/SaProposal.java
new file mode 100644
index 0000000..37a8100
--- /dev/null
+++ b/src/java/com/android/ike/ikev2/SaProposal.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ike.ikev2;
+
+import android.annotation.IntDef;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * SaProposal represents a user configured set contains cryptograhic algorithms and key generating
+ * materials for negotiating an IKE or Child SA.
+ *
+ * <p>User must provide at least a valid SaProposal when they are creating a new IKE SA or Child SA.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
+ * Protocol Version 2 (IKEv2).
+ */
+public final class SaProposal {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ENCRYPTION_ALGORITHM_3DES,
+ ENCRYPTION_ALGORITHM_AES_CBC,
+ ENCRYPTION_ALGORITHM_AES_GCM_8,
+ ENCRYPTION_ALGORITHM_AES_GCM_12,
+ ENCRYPTION_ALGORITHM_AES_GCM_16
+ })
+ public @interface EncryptionAlgorithm {}
+
+ public static final int ENCRYPTION_ALGORITHM_3DES = 3;
+ public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
+ public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18;
+ public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19;
+ public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20;
+
+ private static final Set<Integer> SUPPORTED_ENCRYPTION_ALGORITHM;
+
+ static {
+ SUPPORTED_ENCRYPTION_ALGORITHM = new ArraySet<>();
+ SUPPORTED_ENCRYPTION_ALGORITHM.add(SaProposal.ENCRYPTION_ALGORITHM_3DES);
+ SUPPORTED_ENCRYPTION_ALGORITHM.add(SaProposal.ENCRYPTION_ALGORITHM_AES_CBC);
+ SUPPORTED_ENCRYPTION_ALGORITHM.add(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8);
+ SUPPORTED_ENCRYPTION_ALGORITHM.add(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12);
+ SUPPORTED_ENCRYPTION_ALGORITHM.add(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16);
+ }
+
+ public static final int KEY_LEN_AES_128 = 128;
+ public static final int KEY_LEN_AES_192 = 192;
+ public static final int KEY_LEN_AES_256 = 256;
+
+ /**
+ * Check if the provided algorithm is a supported encryption algorithm.
+ *
+ * @param algorithm IKE standard encryption algorithm id
+ * @return if the provided algorithm is a supported encryption algorithm.
+ */
+ public static boolean isSupportedEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) {
+ return SUPPORTED_ENCRYPTION_ALGORITHM.contains(algorithm);
+ }
+
+ // TODO: Implement constructing SaProposal with a Builder that supports adding
+ // encryption/integrity algorithms, prf, and DH Group.
+}
diff --git a/src/java/com/android/ike/ikev2/message/IkeSaPayload.java b/src/java/com/android/ike/ikev2/message/IkeSaPayload.java
index e3ded36..79a874b 100644
--- a/src/java/com/android/ike/ikev2/message/IkeSaPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeSaPayload.java
@@ -16,9 +16,13 @@
package com.android.ike.ikev2.message;
+import static com.android.ike.ikev2.SaProposal.EncryptionAlgorithm;
+
import android.annotation.IntDef;
+import android.util.ArraySet;
import android.util.Pair;
+import com.android.ike.ikev2.SaProposal;
import com.android.ike.ikev2.exceptions.IkeException;
import com.android.ike.ikev2.exceptions.InvalidSyntaxException;
import com.android.internal.annotations.VisibleForTesting;
@@ -28,6 +32,7 @@
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
/**
* IkeSaPayload represents a Security Association payload. It contains one or more {@link Proposal}.
@@ -38,11 +43,11 @@
public final class IkeSaPayload extends IkePayload {
public final List<Proposal> proposalList;
/**
- * Construct an instance of IkeSaPayload in the context of IkePayloadFactory
+ * Construct an instance of IkeSaPayload in the context of IkePayloadFactory.
*
* @param critical indicates if this payload is critical. Ignored in supported payload as
* instructed by the RFC 7296.
- * @param payloadBody the encoded payload body in byte array
+ * @param payloadBody the encoded payload body in byte array.
*/
IkeSaPayload(boolean critical, byte[] payloadBody) throws IkeException {
super(IkePayload.PAYLOAD_TYPE_SA, critical);
@@ -63,12 +68,14 @@
// TODO: Add another constructor for building outbound message.
/**
- * Proposal represents a set contains cryptograhic algorithms and key generating materials. It
+ * Proposal represents a set contains cryptographic algorithms and key generating materials. It
* contains multiple {@link Transform}.
*
* @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.1">RFC 7296, Internet Key
* Exchange Protocol Version 2 (IKEv2).
- * <p>Ignore Proposal with unsupported Protocol ID when processing IkeSaPayload
+ * <p>Proposals with an unrecognized Protocol ID, containing an unrecognized Transform Type
+ * or lacking a necessary Transform Type shall be ignored when processing a received SA
+ * Payload.
*/
public static final class Proposal {
private static final byte LAST_PROPOSAL = 0;
@@ -83,7 +90,9 @@
Transform[] transformArray = new Transform[count];
for (int i = 0; i < count; i++) {
Transform transform = Transform.readFrom(inputBuffer);
- transformArray[i] = transform;
+ if (transform.isSupported) {
+ transformArray[i] = transform;
+ }
}
return transformArray;
}
@@ -95,7 +104,9 @@
public final byte spiSize;
public final long spi;
+
public final Transform[] transformArray;
+ // TODO: Validate this proposal
@VisibleForTesting
Proposal(byte number, int protocolId, byte spiSize, long spi, Transform[] transformArray) {
@@ -155,13 +166,15 @@
}
/**
- * Transform represents a cryptograhic algorithm. It may contain one or more {@link Attribute}.
+ * Transform is an abstract base class that represents the common information for all Transform
+ * types. It may contain one or more {@link Attribute}.
*
* @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key
* Exchange Protocol Version 2 (IKEv2).
- * <p>Ignore Transform with unsupported type when processing IkeSaPayload
+ * <p>Transforms with unrecognized Transform ID or containing unrecognized Attribute Type
+ * shall be ignored when processing received SA payload.
*/
- public static final class Transform {
+ public abstract static class Transform {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
@@ -204,12 +217,25 @@
// Only supported type falls into {@link TransformType}
public final int type;
public final int id;
- public final List<Attribute> attributeList;
+ public final boolean isSupported;
- Transform(int type, int id, List<Attribute> attributeList) {
+ /** Construct an instance of Transform in the context of {@link SaProposal} */
+ protected Transform(int type, int id) {
this.type = type;
this.id = id;
- this.attributeList = attributeList;
+ if (!isSupportedTransformId(id)) {
+ throw new IllegalArgumentException(
+ "Unsupported " + getTransformTypeString() + " Algorithm ID: " + id);
+ }
+ this.isSupported = true;
+ }
+
+ /** Construct an instance of Transform in the context of {@link Transform} */
+ protected Transform(int type, int id, List<Attribute> attributeList) {
+ this.type = type;
+ this.id = id;
+ this.isSupported =
+ isSupportedTransformId(id) && !hasUnrecognizedAttribute(attributeList);
}
@VisibleForTesting
@@ -234,14 +260,210 @@
// Decode attributes
List<Attribute> attributeList = sAttributeDecoder.decodeAttributes(length, inputBuffer);
- return new Transform(type, id, attributeList);
+ validateAttributeUniqueness(attributeList);
+
+ switch (type) {
+ case TRANSFORM_TYPE_ENCR:
+ return new EncryptionTransform(id, attributeList);
+ // TODO: Add Integrity algorithm, PRF, DhGroup and ESN
+ default:
+ return new UnrecognizedTransform(type, id, attributeList);
+ }
}
- // TODO: Add another contructor for encoding.
+ // Throw InvalidSyntaxException if there are multiple Attributes of the same type
+ private static void validateAttributeUniqueness(List<Attribute> attributeList)
+ throws IkeException {
+ Set<Integer> foundTypes = new ArraySet<>();
+ for (Attribute attr : attributeList) {
+ if (!foundTypes.add(attr.type)) {
+ throw new InvalidSyntaxException(
+ "There are multiple Attributes of the same type. ");
+ }
+ }
+ }
+
+ // Check if this Transform ID is supported.
+ protected abstract boolean isSupportedTransformId(int id);
+
+ // Check if there is Attribute with unrecognized type.
+ protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) {
+ for (Attribute attr : attributeList) {
+ if (attr instanceof UnrecognizedAttribute) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get Tranform Type as a String.
+ *
+ * @return Tranform Type as a String.
+ */
+ public abstract String getTransformTypeString();
+
+ // TODO: Add abstract getTransformIdString() to return specific algorithm/dhGroup name
+ }
+
+ // TODO: Implement PrfTransform, IntegrityTransform, DhGroupTransform and EsnTransForm
+
+ /**
+ * EncryptionTransform represents an encryption algorithm. It may contain an Atrribute
+ * specifying the key length.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key
+ * Exchange Protocol Version 2 (IKEv2).
+ */
+ public static final class EncryptionTransform extends Transform {
+ private static final int KEY_LEN_UNASSIGNED = 0;
+
+ public final int keyLength;
+
+ @Override
+ protected boolean isSupportedTransformId(int id) {
+ return SaProposal.isSupportedEncryptionAlgorithm(id);
+ }
+
+ /**
+ * Contruct an instance of EncryptionTransform in the context of {@link SaProposal} with
+ * fixed key length.
+ *
+ * @param id the IKE standard Transform ID.
+ */
+ public EncryptionTransform(@EncryptionAlgorithm int id) {
+ this(id, KEY_LEN_UNASSIGNED);
+ }
+
+ /**
+ * Contruct an instance of EncryptionTransform in the context of {@link SaProposal} with
+ * variable key length.
+ *
+ * @param id the IKE standard Transform ID.
+ * @param keyLength the specified key length of this encryption algorithm.
+ */
+ public EncryptionTransform(@EncryptionAlgorithm int id, int keyLength) {
+ super(Transform.TRANSFORM_TYPE_ENCR, id);
+
+ this.keyLength = keyLength;
+ try {
+ validateKeyLength();
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e.message);
+ }
+ }
+
+ /**
+ * Contruct an instance of EncryptionTransform in the context of abstract class {@link
+ * Transform}.
+ *
+ * @param id the IKE standard Transform ID.
+ * @param attributeList the decoded list of Attribute.
+ * @throws InvalidSyntaxException for syntax error.
+ */
+ protected EncryptionTransform(int id, List<Attribute> attributeList)
+ throws InvalidSyntaxException {
+ super(Transform.TRANSFORM_TYPE_ENCR, id, attributeList);
+ if (!isSupported) {
+ keyLength = KEY_LEN_UNASSIGNED;
+ } else {
+ if (attributeList.size() == 0) {
+ keyLength = KEY_LEN_UNASSIGNED;
+ } else {
+ KeyLengthAttribute attr = getKeyLengthAttribute(attributeList);
+ keyLength = attr.keyLength;
+ }
+ validateKeyLength();
+ }
+ }
+
+ private KeyLengthAttribute getKeyLengthAttribute(List<Attribute> attributeList) {
+ for (Attribute attr : attributeList) {
+ if (attr.type == Attribute.ATTRIBUTE_TYPE_KEY_LENGTH) {
+ return (KeyLengthAttribute) attr;
+ }
+ }
+ throw new IllegalArgumentException("Cannot find Attribute with Key Length type");
+ }
+
+ private void validateKeyLength() throws InvalidSyntaxException {
+ switch (id) {
+ case SaProposal.ENCRYPTION_ALGORITHM_3DES:
+ if (keyLength != KEY_LEN_UNASSIGNED) {
+ throw new InvalidSyntaxException(
+ "Must not set Key Length value for this "
+ + getTransformTypeString()
+ + " Algorithm ID: "
+ + id);
+ }
+ return;
+ case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC:
+ /* fall through */
+ case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8:
+ /* fall through */
+ case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12:
+ /* fall through */
+ case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16:
+ if (keyLength == KEY_LEN_UNASSIGNED) {
+ throw new InvalidSyntaxException(
+ "Must set Key Length value for this "
+ + getTransformTypeString()
+ + " Algorithm ID: "
+ + id);
+ }
+ if (keyLength != SaProposal.KEY_LEN_AES_128
+ && keyLength != SaProposal.KEY_LEN_AES_192
+ && keyLength != SaProposal.KEY_LEN_AES_256) {
+ throw new InvalidSyntaxException(
+ "Invalid key length for this "
+ + getTransformTypeString()
+ + " Algorithm ID: "
+ + id);
+ }
+ return;
+ default:
+ // Won't hit here.
+ throw new IllegalArgumentException(
+ "Unrecognized Encryption Algorithm ID: " + id);
+ }
+ }
+
+ @Override
+ public String getTransformTypeString() {
+ return "Encryption Algorithm";
+ }
}
/**
- * Attribute is for completing the specification of some {@link Transform}.
+ * UnrecognizedTransform represents a Transform with unrecognized Transform Type.
+ *
+ * <p>Proposals containing an UnrecognizedTransform should be ignored.
+ */
+ protected static final class UnrecognizedTransform extends Transform {
+
+ @Override
+ protected boolean isSupportedTransformId(int id) {
+ return false;
+ }
+
+ protected UnrecognizedTransform(int type, int id, List<Attribute> attributeList) {
+ super(type, id, attributeList);
+ }
+
+ /**
+ * Return Tranform Type of Unrecognized Transform as a String.
+ *
+ * @return Tranform Type of Unrecognized Transform as a String.
+ */
+ @Override
+ public String getTransformTypeString() {
+ return "Unrecognized Transform Type";
+ }
+ }
+
+ /**
+ * Attribute is an abtract base class for completing the specification of some {@link
+ * Transform}.
*
* <p>Attribute is either in Type/Value format or Type/Length/Value format. For TV format,
* Attribute length is always 4 bytes containing value for 2 bytes. While for TLV format,
@@ -252,7 +474,7 @@
* @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.5">RFC 7296, Internet Key
* Exchange Protocol Version 2 (IKEv2).
*/
- public static final class Attribute {
+ public abstract static class Attribute {
@Retention(RetentionPolicy.SOURCE)
@IntDef({ATTRIBUTE_TYPE_KEY_LENGTH})
public @interface AttributeType {}
@@ -265,17 +487,17 @@
// Only Key Length type belongs to AttributeType
public final int type;
- public final byte[] value;
- Attribute(int type, byte[] value) {
+ /** Construct an instance of an Attribute when decoding message. */
+ protected Attribute(int type) {
this.type = type;
- this.value = value;
}
@VisibleForTesting
static Pair<Attribute, Integer> readFrom(ByteBuffer inputBuffer) throws IkeException {
short formatAndType = inputBuffer.getShort();
int type = formatAndType & 0x7fff;
+
int length = 0;
byte[] value = new byte[0];
if ((formatAndType & 0x8000) == 0x8000) {
@@ -290,12 +512,41 @@
length = Short.toUnsignedInt(inputBuffer.getShort());
value = new byte[length - LENGTH_FOR_TV];
}
+
inputBuffer.get(value);
- return new Pair(new Attribute(type, value), length);
+
+ switch (type) {
+ case ATTRIBUTE_TYPE_KEY_LENGTH:
+ return new Pair(new KeyLengthAttribute(value), length);
+ default:
+ return new Pair(new UnrecognizedAttribute(type, value), length);
+ }
+ }
+ }
+
+ /** KeyLengthAttribute represents a Key Length type Attribute */
+ public static final class KeyLengthAttribute extends Attribute {
+ public final int keyLength;
+
+ protected KeyLengthAttribute(byte[] value) {
+ this(Short.toUnsignedInt(ByteBuffer.wrap(value).getShort()));
}
- // TODO: Add another contructor for encoding.
+ protected KeyLengthAttribute(int keyLength) {
+ super(ATTRIBUTE_TYPE_KEY_LENGTH);
+ this.keyLength = keyLength;
+ }
+ }
+ /**
+ * UnrecognizedAttribute represents a Attribute with unrecoginzed Attribute Type.
+ *
+ * <p>Transforms containing UnrecognizedAttribute should be ignored.
+ */
+ protected static final class UnrecognizedAttribute extends Attribute {
+ protected UnrecognizedAttribute(int type, byte[] value) {
+ super(type);
+ }
}
/**
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeKePayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeKePayloadTest.java
index 20deb8a..7d38d5c 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeKePayloadTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeKePayloadTest.java
@@ -50,7 +50,6 @@
@IkePayload.PayloadType
private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_NONCE;
- @IkeKePayload.DhGroup
private static final int EXPECTED_DH_GROUP = IkePayload.DH_GROUP_1024_BIT_MODP;
private static final int EXPECTED_KE_DATA_LEN = 128;
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java
index 2ee9d3b..e2909fc 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java
@@ -17,6 +17,9 @@
package com.android.ike.ikev2.message;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -24,8 +27,20 @@
import android.util.Pair;
+import com.android.ike.ikev2.SaProposal;
import com.android.ike.ikev2.exceptions.IkeException;
+import com.android.ike.ikev2.exceptions.InvalidSyntaxException;
+import com.android.ike.ikev2.message.IkeSaPayload.Attribute;
+import com.android.ike.ikev2.message.IkeSaPayload.AttributeDecoder;
+import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform;
+import com.android.ike.ikev2.message.IkeSaPayload.KeyLengthAttribute;
+import com.android.ike.ikev2.message.IkeSaPayload.Proposal;
+import com.android.ike.ikev2.message.IkeSaPayload.Transform;
+import com.android.ike.ikev2.message.IkeSaPayload.TransformDecoder;
+import com.android.ike.ikev2.message.IkeSaPayload.UnrecognizedAttribute;
+import com.android.ike.ikev2.message.IkeSaPayload.UnrecognizedTransform;
+import org.junit.Before;
import org.junit.Test;
import java.nio.ByteBuffer;
@@ -36,6 +51,7 @@
private static final String PROPOSAL_RAW_PACKET =
"0000002c010100040300000c0100000c800e0080030000080300000203000008040"
+ "000020000000802000002";
+
private static final String TWO_PROPOSAL_RAW_PACKET =
"020000dc010100190300000c0100000c800e00800300000c0100000c800e00c0030"
+ "0000c0100000c800e01000300000801000003030000080300000c0300"
@@ -54,7 +70,10 @@
+ "0300000804000015030000080400001c030000080400001d030000080"
+ "400001e030000080400001f030000080400000f030000080400001003"
+ "00000804000012000000080400000e";
- private static final String TRANSFORM_RAW_PACKET = "0300000c0100000c800e0080";
+ private static final String ENCR_TRANSFORM_RAW_PACKET = "0300000c0100000c800e0080";
+ private static final int TRANSFORM_TYPE_POSITION = 4;
+ private static final int TRANSFORM_ID_POSITION = 7;
+
private static final String ATTRIBUTE_RAW_PACKET = "800e0080";
private static final int PROPOSAL_NUMBER = 1;
@@ -68,53 +87,161 @@
// Constants for multiple proposals test
private static final byte[] PROPOSAL_NUMBER_LIST = {1, 2};
- private static final byte TRANSFORM_TYPE = 1;
- private static final byte TRANSFORM_ID = 12;
+ private static final int KEY_LEN = 128;
- private static final byte ATTRIBUTE_TYPE = 14;
- private static final byte[] ATTRIBUTE_VALUE = {(byte) 0x00, (byte) 0x80};
+ private AttributeDecoder mMockedAttributeDecoder;
+ private KeyLengthAttribute mAttributeKeyLength128;
+ private List<Attribute> mAttributeListWithKeyLength128;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockedAttributeDecoder = mock(AttributeDecoder.class);
+ mAttributeKeyLength128 = new KeyLengthAttribute(SaProposal.KEY_LEN_AES_128);
+ mAttributeListWithKeyLength128 = new LinkedList<>();
+ mAttributeListWithKeyLength128.add(mAttributeKeyLength128);
+ }
@Test
public void testDecodeAttribute() throws Exception {
byte[] inputPacket = TestUtils.hexStringToByteArray(ATTRIBUTE_RAW_PACKET);
ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
- Pair<IkeSaPayload.Attribute, Integer> pair = IkeSaPayload.Attribute.readFrom(inputBuffer);
- IkeSaPayload.Attribute attribute = pair.first;
+ Pair<Attribute, Integer> pair = Attribute.readFrom(inputBuffer);
+ Attribute attribute = pair.first;
- assertEquals(ATTRIBUTE_TYPE, attribute.type);
- assertEquals(ATTRIBUTE_VALUE.length, attribute.value.length);
- for (int i = 0; i < ATTRIBUTE_VALUE.length; i++) {
- assertEquals(ATTRIBUTE_VALUE[i], attribute.value[i]);
+ assertEquals(Attribute.ATTRIBUTE_TYPE_KEY_LENGTH, attribute.type);
+ assertEquals(KEY_LEN, ((KeyLengthAttribute) attribute).keyLength);
+ }
+
+ @Test
+ public void testDecodeEncryptionTransform() throws Exception {
+ byte[] inputPacket = TestUtils.hexStringToByteArray(ENCR_TRANSFORM_RAW_PACKET);
+ ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
+
+ when(mMockedAttributeDecoder.decodeAttributes(anyInt(), any()))
+ .thenReturn(mAttributeListWithKeyLength128);
+ Transform.sAttributeDecoder = mMockedAttributeDecoder;
+
+ Transform transform = Transform.readFrom(inputBuffer);
+
+ assertEquals(Transform.TRANSFORM_TYPE_ENCR, transform.type);
+ assertEquals(SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, transform.id);
+ assertTrue(transform.isSupported);
+ }
+
+ @Test
+ public void testDecodeEncryptionTransformWithInvalidKeyLength() throws Exception {
+ byte[] inputPacket = TestUtils.hexStringToByteArray(ENCR_TRANSFORM_RAW_PACKET);
+ ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
+
+ List<Attribute> attributeList = new LinkedList<>();
+ Attribute keyLengAttr = new KeyLengthAttribute(SaProposal.KEY_LEN_AES_128 + 1);
+ attributeList.add(keyLengAttr);
+
+ when(mMockedAttributeDecoder.decodeAttributes(anyInt(), any())).thenReturn(attributeList);
+ Transform.sAttributeDecoder = mMockedAttributeDecoder;
+
+ try {
+ Transform.readFrom(inputBuffer);
+ fail("Expected InvalidSyntaxException for invalid key length.");
+ } catch (InvalidSyntaxException expected) {
}
}
@Test
- public void testDecodeTransform() throws Exception {
- byte[] inputPacket = TestUtils.hexStringToByteArray(TRANSFORM_RAW_PACKET);
+ public void testConstructEncryptionTransformWithUnSupportedId() throws Exception {
+ try {
+ new EncryptionTransform(SaProposal.ENCRYPTION_ALGORITHM_3DES + 1);
+ fail("Expected IllegalArgumentException for unsupported Transform ID");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testConstructEncryptionTransformWithInvalidKeyLength() throws Exception {
+ try {
+ new EncryptionTransform(SaProposal.ENCRYPTION_ALGORITHM_3DES, 129);
+ fail("Expected IllegalArgumentException for invalid key length.");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testDecodeUnrecognizedTransform() throws Exception {
+ byte[] inputPacket = TestUtils.hexStringToByteArray(ENCR_TRANSFORM_RAW_PACKET);
+ inputPacket[TRANSFORM_TYPE_POSITION] = 6;
ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
- IkeSaPayload.AttributeDecoder mockedDecoder = mock(IkeSaPayload.AttributeDecoder.class);
- List<IkeSaPayload.Attribute> attributeList = new LinkedList<>();
- when(mockedDecoder.decodeAttributes(anyInt(), any())).thenReturn(attributeList);
- IkeSaPayload.Transform.sAttributeDecoder = mockedDecoder;
- IkeSaPayload.Transform transform = IkeSaPayload.Transform.readFrom(inputBuffer);
+ when(mMockedAttributeDecoder.decodeAttributes(anyInt(), any()))
+ .thenReturn(mAttributeListWithKeyLength128);
+ Transform.sAttributeDecoder = mMockedAttributeDecoder;
- assertEquals(TRANSFORM_TYPE, transform.type);
- assertEquals(TRANSFORM_ID, transform.id);
- assertEquals(0, transform.attributeList.size());
+ Transform transform = Transform.readFrom(inputBuffer);
+
+ assertEquals(UnrecognizedTransform.class, transform.getClass());
+ }
+
+ @Test
+ public void testDecodeTransformWithRepeatedAttribute() throws Exception {
+ byte[] inputPacket = TestUtils.hexStringToByteArray(ENCR_TRANSFORM_RAW_PACKET);
+ ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
+
+ List<Attribute> attributeList = new LinkedList<>();
+ attributeList.add(mAttributeKeyLength128);
+ attributeList.add(mAttributeKeyLength128);
+
+ when(mMockedAttributeDecoder.decodeAttributes(anyInt(), any())).thenReturn(attributeList);
+ Transform.sAttributeDecoder = mMockedAttributeDecoder;
+
+ try {
+ Transform.readFrom(inputBuffer);
+ fail("Expected InvalidSyntaxException for repeated Attribute Type Key Length.");
+ } catch (InvalidSyntaxException expected) {
+ }
+ }
+
+ @Test
+ public void testDecodeTransformWithUnrecognizedTransformId() throws Exception {
+ byte[] inputPacket = TestUtils.hexStringToByteArray(ENCR_TRANSFORM_RAW_PACKET);
+ inputPacket[TRANSFORM_ID_POSITION] = 1;
+ ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
+
+ when(mMockedAttributeDecoder.decodeAttributes(anyInt(), any()))
+ .thenReturn(mAttributeListWithKeyLength128);
+ Transform.sAttributeDecoder = mMockedAttributeDecoder;
+
+ Transform transform = Transform.readFrom(inputBuffer);
+
+ assertFalse(transform.isSupported);
+ }
+
+ @Test
+ public void testDecodeTransformWithUnrecogniedAttributeType() throws Exception {
+ byte[] inputPacket = TestUtils.hexStringToByteArray(ENCR_TRANSFORM_RAW_PACKET);
+ ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
+
+ List<Attribute> attributeList = new LinkedList<>();
+ attributeList.add(mAttributeKeyLength128);
+ Attribute attributeUnrecognized = new UnrecognizedAttribute(1, new byte[0]);
+ attributeList.add(attributeUnrecognized);
+
+ when(mMockedAttributeDecoder.decodeAttributes(anyInt(), any())).thenReturn(attributeList);
+ Transform.sAttributeDecoder = mMockedAttributeDecoder;
+
+ Transform transform = Transform.readFrom(inputBuffer);
+
+ assertFalse(transform.isSupported);
}
@Test
public void testDecodeSingleProposal() throws Exception {
byte[] inputPacket = TestUtils.hexStringToByteArray(PROPOSAL_RAW_PACKET);
ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket);
- IkeSaPayload.TransformDecoder mockedDecoder = mock(IkeSaPayload.TransformDecoder.class);
- when(mockedDecoder.decodeTransforms(anyInt(), any()))
- .thenReturn(new IkeSaPayload.Transform[0]);
- IkeSaPayload.Proposal.sTransformDecoder = mockedDecoder;
+ TransformDecoder mockedDecoder = mock(TransformDecoder.class);
+ when(mockedDecoder.decodeTransforms(anyInt(), any())).thenReturn(new Transform[0]);
+ Proposal.sTransformDecoder = mockedDecoder;
- IkeSaPayload.Proposal proposal = IkeSaPayload.Proposal.readFrom(inputBuffer);
+ Proposal proposal = Proposal.readFrom(inputBuffer);
assertEquals(PROPOSAL_NUMBER, proposal.number);
assertEquals(PROPOSAL_PROTOCOL_ID, proposal.protocolId);
@@ -126,11 +253,11 @@
@Test
public void testDecodeMultipleProposal() throws Exception {
byte[] inputPacket = TestUtils.hexStringToByteArray(TWO_PROPOSAL_RAW_PACKET);
- IkeSaPayload.Proposal.sTransformDecoder =
- new IkeSaPayload.TransformDecoder() {
+ Proposal.sTransformDecoder =
+ new TransformDecoder() {
@Override
- public IkeSaPayload.Transform[] decodeTransforms(
- int count, ByteBuffer inputBuffer) throws IkeException {
+ public Transform[] decodeTransforms(int count, ByteBuffer inputBuffer)
+ throws IkeException {
for (int i = 0; i < count; i++) {
// Read length field and move position
inputBuffer.getShort();
@@ -138,7 +265,7 @@
byte[] temp = new byte[length - 4];
inputBuffer.get(temp);
}
- return new IkeSaPayload.Transform[0];
+ return new Transform[0];
}
};
@@ -146,7 +273,7 @@
assertEquals(PROPOSAL_NUMBER_LIST.length, payload.proposalList.size());
for (int i = 0; i < payload.proposalList.size(); i++) {
- IkeSaPayload.Proposal proposal = payload.proposalList.get(i);
+ Proposal proposal = payload.proposalList.get(i);
assertEquals(PROPOSAL_NUMBER_LIST[i], proposal.number);
assertEquals(IkePayload.PROTOCOL_ID_IKE, proposal.protocolId);
assertEquals(0, proposal.spiSize);