Merge "Create Traffic Selector Payload"
diff --git a/src/java/com/android/ike/ikev2/SaProposal.java b/src/java/com/android/ike/ikev2/SaProposal.java
index e478efd..9297179 100644
--- a/src/java/com/android/ike/ikev2/SaProposal.java
+++ b/src/java/com/android/ike/ikev2/SaProposal.java
@@ -19,6 +19,12 @@
import android.annotation.IntDef;
import android.util.ArraySet;
+import com.android.ike.ikev2.message.IkePayload;
+import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform;
+import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform;
+import com.android.ike.ikev2.message.IkeSaPayload.IntegrityTransform;
+import com.android.ike.ikev2.message.IkeSaPayload.PrfTransform;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
@@ -127,6 +133,230 @@
SUPPORTED_DH_GROUP.add(DH_GROUP_2048_BIT_MODP);
}
+ /** Package private */
+ @IkePayload.ProtocolId final int mProtocolId;
+ /** Package private */
+ final EncryptionTransform[] mEncryptionAlgorithms;
+ /** Package private */
+ final PrfTransform[] mPseudorandomFunctions;
+ /** Package private */
+ final IntegrityTransform[] mIntegrityAlgorithms;
+ /** Package private */
+ final DhGroupTransform[] mDhGroups;
+
+ private SaProposal(
+ @IkePayload.ProtocolId int protocol,
+ EncryptionTransform[] encryptionAlgos,
+ PrfTransform[] prfs,
+ IntegrityTransform[] integrityAlgos,
+ DhGroupTransform[] dhGroups) {
+ mProtocolId = protocol;
+ mEncryptionAlgorithms = encryptionAlgos;
+ mPseudorandomFunctions = prfs;
+ mIntegrityAlgorithms = integrityAlgos;
+ mDhGroups = dhGroups;
+ }
+
+ /**
+ * This class can be used to incrementally construct a SaProposal. SaProposal instances are
+ * immutable once built.
+ */
+ public static final class Builder {
+ private static final String ERROR_TAG = "Invalid SA Proposal: ";
+
+ /** Indicate if Builder is for building IKE SA proposal or Child SA proposal. */
+ private final boolean mIsIkeProposal;
+ /**
+ * Indicate if Builder is for building first Child SA proposal or addtional Child SA
+ * proposal. Only valid if mIsIkeProposal is false.
+ */
+ private final boolean mIsFirstChild;
+
+ // Use set to avoid adding repeated algorithms.
+ private final Set<EncryptionTransform> mProposedEncryptAlgos = new ArraySet<>();
+ private final Set<PrfTransform> mProposedPrfs = new ArraySet<>();
+ private final Set<IntegrityTransform> mProposedIntegrityAlgos = new ArraySet<>();
+ private final Set<DhGroupTransform> mProposedDhGroups = new ArraySet<>();
+
+ private boolean mHasAead = false;
+
+ private Builder(boolean isIke, boolean isFirstChild) {
+ mIsIkeProposal = isIke;
+ mIsFirstChild = isFirstChild;
+ }
+
+ private static boolean isAead(@EncryptionAlgorithm int algorithm) {
+ switch (algorithm) {
+ case ENCRYPTION_ALGORITHM_3DES:
+ // Fall through
+ case ENCRYPTION_ALGORITHM_AES_CBC:
+ return false;
+ case ENCRYPTION_ALGORITHM_AES_GCM_8:
+ // Fall through
+ case ENCRYPTION_ALGORITHM_AES_GCM_12:
+ // Fall through
+ case ENCRYPTION_ALGORITHM_AES_GCM_16:
+ return true;
+ default:
+ // Won't hit here.
+ throw new IllegalArgumentException("Unsupported Encryption Algorithm.");
+ }
+ }
+
+ private EncryptionTransform[] buildEncryptAlgosOrThrow() {
+ if (mProposedEncryptAlgos.isEmpty()) {
+ throw new IllegalArgumentException(
+ ERROR_TAG + "Encryption algorithm must be proposed.");
+ }
+
+ return (EncryptionTransform[]) mProposedEncryptAlgos.toArray();
+ }
+
+ private PrfTransform[] buildPrfsOrThrow() {
+ // TODO: Validate that PRF must be proposed for IKE SA and PRF must not be
+ // proposed for Child SA.
+ throw new UnsupportedOperationException("Cannot validate user proposed algorithm.");
+ }
+
+ private IntegrityTransform[] buildIntegAlgosOrThrow() {
+ // TODO: Validate proposed integrity algorithms according to existence of AEAD.
+ throw new UnsupportedOperationException("Cannot validate user proposed algorithm.");
+ }
+
+ private DhGroupTransform[] buildDhGroupsOrThrow() {
+ // TODO: Validate proposed DH groups according to the usage of SaProposal (for
+ // IKE SA, for first Child SA or for addtional Child SA)
+ throw new UnsupportedOperationException("Cannot validate user proposed algorithm.");
+ }
+
+ /** Returns a new Builder for a IKE SA Proposal. */
+ public static Builder newIkeSaProposalBuilder() {
+ return new Builder(true, false);
+ }
+
+ /**
+ * Returns a new Builder for a Child SA Proposal.
+ *
+ * @param isFirstChildSaProposal indicates if this SA proposal for first Child SA.
+ * @return Builder for a Child SA Proposal.
+ */
+ public static Builder newChildSaProposalBuilder(boolean isFirstChildSaProposal) {
+ return new Builder(false, isFirstChildSaProposal);
+ }
+
+ /**
+ * Adds an encryption algorithm to SA proposal being built.
+ *
+ * @param algorithm encryption algorithm to add to SaProposal.
+ * @return Builder of SaProposal.
+ */
+ public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) {
+ // Construct EncryptionTransform and validate proposed algorithm during
+ // construction.
+ EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm);
+
+ validateOnlyOneModeEncryptAlgoProposedOrThrow(algorithm);
+
+ mProposedEncryptAlgos.add(encryptionTransform);
+ return this;
+ }
+
+ /**
+ * Adds an encryption algorithm with specific key length to SA proposal being built.
+ *
+ * @param algorithm encryption algorithm to add to SaProposal.
+ * @param keyLength key length of algorithm.
+ * @return Builder of SaProposal.
+ * @throws IllegalArgumentException if AEAD and non-combined mode algorithms are mixed.
+ */
+ public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) {
+ // Construct EncryptionTransform and validate proposed algorithm during
+ // construction.
+ EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength);
+
+ validateOnlyOneModeEncryptAlgoProposedOrThrow(algorithm);
+
+ mProposedEncryptAlgos.add(encryptionTransform);
+ return this;
+ }
+
+ private void validateOnlyOneModeEncryptAlgoProposedOrThrow(
+ @EncryptionAlgorithm int algorithm) {
+ boolean isCurrentAead = isAead(algorithm);
+
+ if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) {
+ throw new IllegalArgumentException(
+ ERROR_TAG
+ + "Proposal cannot has both normal ciphers "
+ + "and combined-mode ciphers.");
+ }
+
+ if (isCurrentAead) mHasAead = true;
+ }
+
+ /**
+ * Adds a pseudorandom function to SA proposal being built.
+ *
+ * @param algorithm pseudorandom function to add to SaProposal.
+ * @return Builder of SaProposal.
+ */
+ public Builder addPseudorandomFunction(@PseudorandomFunction int algorithm) {
+ // Construct PrfTransform and validate proposed algorithm during
+ // construction.
+ mProposedPrfs.add(new PrfTransform(algorithm));
+ return this;
+ }
+
+ /**
+ * Adds an integrity algorithm to SA proposal being built.
+ *
+ * @param algorithm integrity algorithm to add to SaProposal.
+ * @return Builder of SaProposal.
+ */
+ public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
+ // Construct IntegrityTransform and validate proposed algorithm during
+ // construction.
+ mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm));
+ return this;
+ }
+
+ /**
+ * Adds a Diffie-Hellman Group to SA proposal being built.
+ *
+ * @param dhGroup to add to SaProposal.
+ * @return Builder of SaProposal.
+ */
+ public Builder addDhGroup(@DhGroup int dhGroup) {
+ // Construct DhGroupTransform and validate proposed dhGroup during
+ // construction.
+ mProposedDhGroups.add(new DhGroupTransform(dhGroup));
+ return this;
+ }
+
+ /**
+ * Validates, builds and returns the SaProposal
+ *
+ * @return SaProposal the validated SaProposal.
+ * @throws IllegalArgumentException if SaProposal is invalid.
+ * */
+ public SaProposal buildOrThrow() {
+ EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow();
+ PrfTransform[] prfTransforms = buildPrfsOrThrow();
+ IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow();
+ DhGroupTransform[] dhGroupTransforms = buildDhGroupsOrThrow();
+
+ // IKE library only supports negotiating ESP Child SA.
+ int protocol = mIsIkeProposal ? IkePayload.PROTOCOL_ID_IKE : IkePayload.PROTOCOL_ID_ESP;
+
+ return new SaProposal(
+ protocol,
+ encryptionTransforms,
+ prfTransforms,
+ integrityTransforms,
+ dhGroupTransforms);
+ }
+ }
+
/**
* Check if the provided algorithm is a supported encryption algorithm.
*
diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java
index 12fb5f6..5b5a3f0 100644
--- a/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java
@@ -138,14 +138,14 @@
// TODO: Add methods for generating and validating signature.
@Override
- protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
+ protected void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer) {
// TODO: Implement it.
throw new UnsupportedOperationException(
"It is not supported to encode a " + getTypeString());
}
@Override
- protected int getPayloadLength() {
+ protected int getAuthDataLength() {
// TODO: Implement it.
throw new UnsupportedOperationException(
"It is not supported to get payload length of " + getTypeString());
diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java
index beb2bdc..502af36 100644
--- a/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java
@@ -61,7 +61,7 @@
@AuthMethod public final int authMethod;
- protected IkeAuthPayload(boolean critical, int authMethod) throws IkeException {
+ protected IkeAuthPayload(boolean critical, int authMethod) {
super(PAYLOAD_TYPE_AUTH, critical);
this.authMethod = authMethod;
}
@@ -131,7 +131,23 @@
}
@Override
+ protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
+ encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
+ byteBuffer.put((byte) authMethod).put(new byte[AUTH_RESERVED_FIELD_LEN]);
+ encodeAuthDataToByteBuffer(byteBuffer);
+ }
+
+ @Override
+ protected int getPayloadLength() {
+ return GENERIC_HEADER_LENGTH + AUTH_HEADER_LEN + getAuthDataLength();
+ }
+
+ @Override
public String getTypeString() {
return "Authentication Payload";
}
+
+ protected abstract void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer);
+
+ protected abstract int getAuthDataLength();
}
diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java
index 63f45dd..213e24a 100644
--- a/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java
@@ -16,9 +16,10 @@
package com.android.ike.ikev2.message;
-import com.android.ike.ikev2.exceptions.IkeException;
-
import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+
+import javax.crypto.Mac;
/**
* IkeAuthPskPayload represents an Authentication Payload using Pre-Shared Key to do authentication.
@@ -27,25 +28,79 @@
* Protocol Version 2 (IKEv2).
*/
public final class IkeAuthPskPayload extends IkeAuthPayload {
+ // Hex of ASCII characters "Key Pad for IKEv2" for calculating PSK signature.
+ private static final byte[] IKE_KEY_PAD_STRING_ASCII_HEX_BYTES = {
+ (byte) 0x4b, (byte) 0x65, (byte) 0x79, (byte) 0x20,
+ (byte) 0x50, (byte) 0x61, (byte) 0x64, (byte) 0x20,
+ (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x20,
+ (byte) 0x49, (byte) 0x4b, (byte) 0x45, (byte) 0x76,
+ (byte) 0x32
+ };
+
public final byte[] signature;
- protected IkeAuthPskPayload(boolean critical, byte[] authData) throws IkeException {
+ /**
+ * Construct IkeAuthPskPayload for received IKE packet in the context of {@link
+ * IkePayloadFactory}.
+ */
+ protected IkeAuthPskPayload(boolean critical, byte[] authData) {
super(critical, IkeAuthPayload.AUTH_METHOD_PRE_SHARED_KEY);
signature = authData;
}
- @Override
- protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
- // TODO: Implement it.
- throw new UnsupportedOperationException(
- "It is not supported to encode a " + getTypeString());
+ /**
+ * Construct IkeAuthPskPayload for an outbound IKE packet.
+ *
+ * <p>Since IKE library is always a client, outbound IkeAuthPskPayload always signs IKE
+ * initiator's SignedOctets, which is concatenation of the IKE_INIT request message, the Nonce
+ * of IKE responder and the signed ID-Initiator payload body.
+ *
+ * @param psk locally stored pre-shared key
+ * @param ikeInitBytes IKE_INIT request for calculating IKE initiator's SignedOctets.
+ * @param nonce nonce of IKE responder for calculating IKE initiator's SignedOctets.
+ * @param idPayloadBodyBytes ID-Initiator payload body for calculating IKE initiator's
+ * SignedOctets.
+ * @param prfMac locally store PRF
+ * @param prfKeyBytes locally store PRF keys
+ */
+ public IkeAuthPskPayload(
+ byte[] psk,
+ byte[] ikeInitBytes,
+ byte[] nonce,
+ byte[] idPayloadBodyBytes,
+ Mac prfMac,
+ byte[] prfKeyBytes) {
+ super(false, IkeAuthPayload.AUTH_METHOD_PRE_SHARED_KEY);
+ signature =
+ calculatePskSignature(
+ psk, ikeInitBytes, nonce, idPayloadBodyBytes, prfMac, prfKeyBytes);
+ }
+
+ private static byte[] calculatePskSignature(
+ byte[] psk,
+ byte[] ikeInitBytes,
+ byte[] nonce,
+ byte[] idPayloadBodyBytes,
+ Mac prfMac,
+ byte[] prfKeyBytes) {
+ try {
+ byte[] signingKeyBytes = signWithPrf(prfMac, psk, IKE_KEY_PAD_STRING_ASCII_HEX_BYTES);
+ byte[] dataToSignBytes =
+ getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, prfMac, prfKeyBytes);
+ return signWithPrf(prfMac, signingKeyBytes, dataToSignBytes);
+ } catch (InvalidKeyException e) {
+ throw new IllegalArgumentException("Locally stored PRF key is invalid: ", e);
+ }
}
@Override
- protected int getPayloadLength() {
- // TODO: Implement it.
- throw new UnsupportedOperationException(
- "It is not supported to get payload length of " + getTypeString());
+ protected void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer) {
+ byteBuffer.put(signature);
+ }
+
+ @Override
+ protected int getAuthDataLength() {
+ return signature.length;
}
@Override
diff --git a/src/java/com/android/ike/ikev2/message/IkeSaPayload.java b/src/java/com/android/ike/ikev2/message/IkeSaPayload.java
index c31575d..e809c9c 100644
--- a/src/java/com/android/ike/ikev2/message/IkeSaPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeSaPayload.java
@@ -35,6 +35,7 @@
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -376,6 +377,19 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(type, id, keyLength);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof EncryptionTransform)) return false;
+
+ EncryptionTransform other = (EncryptionTransform) o;
+ return (type == other.type && id == other.id && keyLength == other.keyLength);
+ }
+
+ @Override
protected boolean isSupportedTransformId(int id) {
return SaProposal.isSupportedEncryptionAlgorithm(id);
}
@@ -476,6 +490,19 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(type, id);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof PrfTransform)) return false;
+
+ PrfTransform other = (PrfTransform) o;
+ return (type == other.type && id == other.id);
+ }
+
+ @Override
protected boolean isSupportedTransformId(int id) {
return SaProposal.isSupportedPseudorandomFunction(id);
}
@@ -524,6 +551,19 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(type, id);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IntegrityTransform)) return false;
+
+ IntegrityTransform other = (IntegrityTransform) o;
+ return (type == other.type && id == other.id);
+ }
+
+ @Override
protected boolean isSupportedTransformId(int id) {
return SaProposal.isSupportedIntegrityAlgorithm(id);
}
@@ -572,6 +612,19 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(type, id);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DhGroupTransform)) return false;
+
+ DhGroupTransform other = (DhGroupTransform) o;
+ return (type == other.type && id == other.id);
+ }
+
+ @Override
protected boolean isSupportedTransformId(int id) {
return SaProposal.isSupportedDhGroup(id);
}
@@ -626,6 +679,19 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(type, id);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof EsnTransform)) return false;
+
+ EsnTransform other = (EsnTransform) o;
+ return (type == other.type && id == other.id);
+ }
+
+ @Override
protected boolean isSupportedTransformId(int id) {
return (id == ESN_POLICY_NO_EXTENDED || id == ESN_POLICY_EXTENDED);
}
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java b/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java
new file mode 100644
index 0000000..522d44b
--- /dev/null
+++ b/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ike.ikev2;
+
+import static org.junit.Assert.fail;
+
+import com.android.ike.ikev2.SaProposal.Builder;
+
+import org.junit.Test;
+
+public final class SaProposalTest {
+ @Test
+ public void testBuildEncryptAlgosWithNoAlgorithm() throws Exception {
+ Builder builder = Builder.newIkeSaProposalBuilder();
+ try {
+ builder.buildOrThrow();
+ fail("Encryption algorithm is not provided.");
+ } catch (IllegalArgumentException expected) {
+
+ }
+ }
+
+ @Test
+ public void testBuildEncryptAlgosWithUnrecognizedAlgorithm() throws Exception {
+ Builder builder = Builder.newIkeSaProposalBuilder();
+ try {
+ builder.addEncryptionAlgorithm(-1);
+ fail("Encryption algorithm is not recognized.");
+ } catch (IllegalArgumentException expected) {
+
+ }
+ }
+
+ @Test
+ public void testBuildEncryptAlgosWithTwoModes() throws Exception {
+ Builder builder = Builder.newIkeSaProposalBuilder();
+ try {
+ builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES)
+ .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12);
+ fail("Expect failure when normal and combined-mode ciphers are proposed together.");
+ } catch (IllegalArgumentException expected) {
+
+ }
+ }
+}
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPayloadTest.java
index a67f04c..7c630b7 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPayloadTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPayloadTest.java
@@ -28,7 +28,7 @@
public final class IkeAuthPayloadTest {
private static final String PSK_AUTH_PAYLOAD_HEX_STRING =
"02000000df7c038aefaaa32d3f44b228b52a332744dfb2c1";
- private static final String PSK_AUTH_PAYLOAD_SIGNATRUE_HEX_STRING =
+ private static final String PSK_AUTH_PAYLOAD_SIGNATURE_HEX_STRING =
"df7c038aefaaa32d3f44b228b52a332744dfb2c1";
private static final String PSK_ID_PAYLOAD_HEX_STRING = "010000000a50500d";
private static final String PSK_SKP_HEX_STRING = "094787780EE466E2CB049FA327B43908BC57E485";
@@ -89,7 +89,7 @@
assertTrue(payload instanceof IkeAuthPskPayload);
byte[] expectedSignature =
- TestUtils.hexStringToByteArray(PSK_AUTH_PAYLOAD_SIGNATRUE_HEX_STRING);
+ TestUtils.hexStringToByteArray(PSK_AUTH_PAYLOAD_SIGNATURE_HEX_STRING);
assertArrayEquals(expectedSignature, ((IkeAuthPskPayload) payload).signature);
}
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPskPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPskPayloadTest.java
new file mode 100644
index 0000000..398e962
--- /dev/null
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthPskPayloadTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ike.ikev2.message;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import javax.crypto.Mac;
+
+
+public final class IkeAuthPskPayloadTest {
+ private static final String PSK_AUTH_PAYLOAD_HEX_STRING =
+ "2100001c02000000df7c038aefaaa32d3f44b228b52a332744dfb2c1";
+ private static final String PSK_AUTH_PAYLOAD_SIGNATURE_HEX_STRING =
+ "df7c038aefaaa32d3f44b228b52a332744dfb2c1";
+
+ private static final String PSK_IKE_INIT_REQUEST_HEX_STRING =
+ "5f54bf6d8b48e6e1000000000000000021202208"
+ + "0000000000000150220000300000002c01010004"
+ + "0300000c0100000c800e00800300000803000002"
+ + "0300000804000002000000080200000228000088"
+ + "00020000b4a2faf4bb54878ae21d638512ece55d"
+ + "9236fc5046ab6cef82220f421f3ce6361faf3656"
+ + "4ecb6d28798a94aad7b2b4b603ddeaaa5630adb9"
+ + "ece8ac37534036040610ebdd92f46bef84f0be7d"
+ + "b860351843858f8acf87056e272377f70c9f2d81"
+ + "e29c7b0ce4f291a3a72476bb0b278fd4b7b0a4c2"
+ + "6bbeb08214c707137607958729000024c39b7f36"
+ + "8f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c7"
+ + "2cb4240eb5c464122900001c00004004e54f73b7"
+ + "d83f6beb881eab2051d8663f421d10b02b00001c"
+ + "00004005d915368ca036004cb578ae3e3fb26850"
+ + "9aeab19000000020699369228741c6d4ca094c93"
+ + "e242c9de19e7b7c60000000500000500";
+ private static final String PSK_NONCE_RESP_HEX_STRING =
+ "9756112ca539f5c25abacc7ee92b73091942a9c06950f98848f1af1694c4ddff";
+ private static final String PSK_ID_INITIATOR_PAYLOAD_HEX_STRING = "010000000a50500d";
+
+ private static final String PSK_HEX_STRING = "6A756E69706572313233";
+ private static final String PSK_SKP_HEX_STRING = "094787780EE466E2CB049FA327B43908BC57E485";
+
+ private static final String PRF_HMAC_SHA1_ALGO_NAME = "HmacSHA1";
+
+ @Test
+ public void testBuildOutboundIkeAuthPskPayload() throws Exception {
+ byte[] psk = TestUtils.hexStringToByteArray(PSK_HEX_STRING);
+ byte[] ikeInitBytes = TestUtils.hexStringToByteArray(PSK_IKE_INIT_REQUEST_HEX_STRING);
+ byte[] nonce = TestUtils.hexStringToByteArray(PSK_NONCE_RESP_HEX_STRING);
+ byte[] idPayloadBodyBytes =
+ TestUtils.hexStringToByteArray(PSK_ID_INITIATOR_PAYLOAD_HEX_STRING);
+ byte[] prfKeyBytes = TestUtils.hexStringToByteArray(PSK_SKP_HEX_STRING);
+ Mac prfMac = Mac.getInstance(PRF_HMAC_SHA1_ALGO_NAME, IkeMessage.getSecurityProvider());
+
+ IkeAuthPskPayload payload =
+ new IkeAuthPskPayload(
+ psk, ikeInitBytes, nonce, idPayloadBodyBytes, prfMac, prfKeyBytes);
+
+ assertEquals(IkeAuthPayload.AUTH_METHOD_PRE_SHARED_KEY, payload.authMethod);
+ byte[] expectedSignature =
+ TestUtils.hexStringToByteArray(PSK_AUTH_PAYLOAD_SIGNATURE_HEX_STRING);
+ assertArrayEquals(expectedSignature, payload.signature);
+
+ // Verify payload length
+ int payloadLength = payload.getPayloadLength();
+ byte[] expectedPayload = TestUtils.hexStringToByteArray(PSK_AUTH_PAYLOAD_HEX_STRING);
+ assertEquals(expectedPayload.length, payloadLength);
+
+ // Verify encoding
+ ByteBuffer byteBuffer = ByteBuffer.allocate(payloadLength);
+ payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_SA, byteBuffer);
+ assertArrayEquals(expectedPayload, byteBuffer.array());
+ }
+}