aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYan Yan <evitayan@google.com>2019-02-11 18:09:03 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2019-02-11 18:09:03 +0000
commita82e2910b79091e539328ccb7f296e0b9981aaf1 (patch)
tree68ad1a0783647f53509293690f56d884600844ef
parentfb7c210d1e75c8029edf32aeccdb432b3222fb69 (diff)
parentca7997888c7142211bd2e41f77a1f25d4e8f4f0b (diff)
downloadike-a82e2910b79091e539328ccb7f296e0b9981aaf1.tar.gz
Merge "Create IkeEncryptedPayloadBody"
-rw-r--r--src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java141
-rw-r--r--src/java/com/android/ike/ikev2/message/IkeMessage.java8
-rw-r--r--src/java/com/android/ike/ikev2/message/IkePayloadFactory.java7
-rw-r--r--src/java/com/android/ike/ikev2/message/IkeSkPayload.java89
-rw-r--r--tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java30
5 files changed, 191 insertions, 84 deletions
diff --git a/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java b/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java
new file mode 100644
index 00000000..5bfed5d4
--- /dev/null
+++ b/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java
@@ -0,0 +1,141 @@
+/*
+ * 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;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * IkeEncryptedPayloadBody is a package private class that represents an IKE payload substructure
+ * that contains initialization vector, encrypted content, padding, pad length and integrity
+ * checksum.
+ *
+ * <p>Both an Encrypted Payload (IkeSkPayload) and an EncryptedFragmentPayload (IkeSkfPayload)
+ * consists of an IkeEncryptedPayloadBody instance.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#page-105">RFC 7296, Internet Key Exchange
+ * Protocol Version 2 (IKEv2).
+ * @see <a href="https://tools.ietf.org/html/rfc7383#page-6">RFC 7383, Internet Key Exchange
+ * Protocol Version 2 (IKEv2) Message Fragmentation
+ */
+final class IkeEncryptedPayloadBody {
+ // Length of pad length field.
+ private static final int PAD_LEN_LEN = 1;
+
+ private final byte[] mUnencryptedData;
+ private final byte[] mEncryptedAndPaddedData;
+ private final byte[] mIv;
+ private final byte[] mIntegrityChecksum;
+
+ /**
+ * Package private constructor for constructing an instance of IkeEncryptedPayloadBody from
+ * decrypting an incoming packet.
+ */
+ IkeEncryptedPayloadBody(
+ byte[] message,
+ Mac integrityMac,
+ int expectedChecksumLen,
+ Cipher decryptCipher,
+ SecretKey dKey)
+ throws IkeException, GeneralSecurityException {
+ ByteBuffer inputBuffer = ByteBuffer.wrap(message);
+
+ // Skip IKE header and SK payload header
+ byte[] tempArray = new byte[IkeHeader.IKE_HEADER_LENGTH + IkePayload.GENERIC_HEADER_LENGTH];
+ inputBuffer.get(tempArray);
+
+ // Extract bytes for authentication and decryption.
+ int expectedIvLen = decryptCipher.getBlockSize();
+ mIv = new byte[expectedIvLen];
+
+ int encryptedDataLen =
+ message.length
+ - (IkeHeader.IKE_HEADER_LENGTH
+ + IkePayload.GENERIC_HEADER_LENGTH
+ + expectedIvLen
+ + expectedChecksumLen);
+ // IkeMessage will catch exception if encryptedDataLen is negative.
+ mEncryptedAndPaddedData = new byte[encryptedDataLen];
+
+ mIntegrityChecksum = new byte[expectedChecksumLen];
+ inputBuffer.get(mIv).get(mEncryptedAndPaddedData).get(mIntegrityChecksum);
+
+ // Authenticate and decrypt.
+ validateChecksumOrThrow(message, integrityMac, expectedChecksumLen, mIntegrityChecksum);
+ mUnencryptedData = decrypt(mEncryptedAndPaddedData, decryptCipher, dKey, mIv);
+ }
+
+ // TODO: Add another constructor for AEAD protected payload.
+
+ // TODO: Add constructors that initiate IkeEncryptedPayloadBody for an outbound packet
+
+ private static void validateChecksumOrThrow(
+ byte[] message, Mac integrityMac, int expectedChecksumLen, byte[] integrityChecksum)
+ throws GeneralSecurityException {
+ ByteBuffer inputBuffer = ByteBuffer.wrap(message, 0, message.length - expectedChecksumLen);
+ integrityMac.update(inputBuffer);
+ byte[] calculatedChecksum =
+ Arrays.copyOfRange(integrityMac.doFinal(), 0, expectedChecksumLen);
+
+ if (!Arrays.equals(integrityChecksum, calculatedChecksum)) {
+ throw new GeneralSecurityException("Message authentication failed.");
+ }
+ }
+
+ private static byte[] decrypt(
+ byte[] encryptedData, Cipher decryptCipher, SecretKey dKey, byte[] iv)
+ throws GeneralSecurityException {
+ decryptCipher.init(Cipher.DECRYPT_MODE, dKey, new IvParameterSpec(iv));
+
+ ByteBuffer inputBuffer = ByteBuffer.wrap(encryptedData);
+ ByteBuffer outputBuffer = ByteBuffer.allocate(encryptedData.length);
+ decryptCipher.doFinal(inputBuffer, outputBuffer);
+
+ // Remove padding
+ outputBuffer.rewind();
+ int padLength = Byte.toUnsignedInt(outputBuffer.get(encryptedData.length - PAD_LEN_LEN));
+ byte[] decryptedData = new byte[encryptedData.length - padLength - PAD_LEN_LEN];
+
+ outputBuffer.get(decryptedData);
+ return decryptedData;
+ }
+
+ /** Package private */
+ byte[] getUnencryptedData() {
+ return mUnencryptedData;
+ }
+
+ /** Package private */
+ int getLength() {
+ return (mIv.length + mEncryptedAndPaddedData.length + mIntegrityChecksum.length);
+ }
+
+ /** Package private */
+ byte[] encode() {
+ ByteBuffer buffer = ByteBuffer.allocate(getLength());
+ buffer.put(mIv).put(mEncryptedAndPaddedData).put(mIntegrityChecksum);
+ return buffer.array();
+ }
+}
diff --git a/src/java/com/android/ike/ikev2/message/IkeMessage.java b/src/java/com/android/ike/ikev2/message/IkeMessage.java
index cb19317c..b2185bfe 100644
--- a/src/java/com/android/ike/ikev2/message/IkeMessage.java
+++ b/src/java/com/android/ike/ikev2/message/IkeMessage.java
@@ -347,6 +347,7 @@ public final class IkeMessage {
IkeHeader ikeHeader,
byte[] packet)
throws IkeException, GeneralSecurityException {
+ //TODO: Extract crypto params and call private decode method.
return null;
}
@@ -356,8 +357,7 @@ public final class IkeMessage {
Mac integrityMac,
int checksumLen,
Cipher decryptCipher,
- SecretKey dKey,
- int ivLen)
+ SecretKey dKey)
throws IkeException, GeneralSecurityException {
header.checkValidOrThrow(inputPacket.length);
@@ -370,12 +370,12 @@ public final class IkeMessage {
try {
Pair<IkeSkPayload, Integer> pair =
IkePayloadFactory.getIkeSkPayload(
- inputPacket, integrityMac, checksumLen, decryptCipher, dKey, ivLen);
+ inputPacket, integrityMac, checksumLen, decryptCipher, dKey);
IkeSkPayload skPayload = pair.first;
int firstPayloadType = pair.second;
List<IkePayload> supportedPayloadList =
- decodePayloadList(firstPayloadType, skPayload.unencryptedPayloads);
+ decodePayloadList(firstPayloadType, skPayload.getUnencryptedPayloads());
return new IkeMessage(header, supportedPayloadList);
} catch (NegativeArraySizeException | BufferUnderflowException e) {
diff --git a/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java b/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java
index 409d25bb..a091618d 100644
--- a/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java
+++ b/src/java/com/android/ike/ikev2/message/IkePayloadFactory.java
@@ -121,7 +121,6 @@ final class IkePayloadFactory {
* @param expectedChecksumLen the expected length of integrity checksum.
* @param decryptCipher the uninitialized Cipher for doing decryption.
* @param dKey the decryption key.
- * @param expectedIvLen the expected length of Initialization Vector.
* @return a pair including IkePayload and next payload type.
* @throws IkeException for decoding errors.
* @throws GeneralSecurityException if there is any error during integrity check or decryption.
@@ -131,8 +130,7 @@ final class IkePayloadFactory {
Mac integrityMac,
int expectedChecksumLen,
Cipher decryptCipher,
- SecretKey dKey,
- int expectedIvLen)
+ SecretKey dKey)
throws IkeException, GeneralSecurityException {
ByteBuffer input =
ByteBuffer.wrap(
@@ -168,8 +166,7 @@ final class IkePayloadFactory {
integrityMac,
expectedChecksumLen,
decryptCipher,
- dKey,
- expectedIvLen);
+ dKey);
return new Pair(payload, nextPayloadType);
}
diff --git a/src/java/com/android/ike/ikev2/message/IkeSkPayload.java b/src/java/com/android/ike/ikev2/message/IkeSkPayload.java
index b108580b..f2395157 100644
--- a/src/java/com/android/ike/ikev2/message/IkeSkPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeSkPayload.java
@@ -21,12 +21,10 @@ import com.android.ike.ikev2.message.IkePayload.PayloadType;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
-import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
/**
* IkeSkPayload represents a Encrypted Payload.
@@ -41,10 +39,10 @@ import javax.crypto.spec.IvParameterSpec;
*/
public final class IkeSkPayload extends IkePayload {
- public final byte[] unencryptedPayloads;
+ private final IkeEncryptedPayloadBody mIkeEncryptedPayloadBody;
/**
- * Construct an instance of IkeSkPayload in the context of {@link IkePayloadFactory}.
+ * Construct an instance of IkeSkPayload from decrypting an incoming packet.
*
* @param critical indicates if it is a critical payload.
* @param message the byte array contains the whole IKE message.
@@ -52,7 +50,6 @@ public final class IkeSkPayload extends IkePayload {
* @param expectedChecksumLen the expected length of integrity checksum.
* @param decryptCipher the uninitialized Cipher for doing decryption.
* @param dKey the decryption key.
- * @param expectedIvLen the expected length of Initialization Vector.
*/
IkeSkPayload(
boolean critical,
@@ -60,80 +57,36 @@ public final class IkeSkPayload extends IkePayload {
Mac integrityMac,
int expectedChecksumLen,
Cipher decryptCipher,
- SecretKey dKey,
- int expectedIvLen)
+ SecretKey dKey)
throws IkeException, GeneralSecurityException {
super(PAYLOAD_TYPE_SK, critical);
- ByteBuffer inputBuffer = ByteBuffer.wrap(message);
-
- // Skip IKE header and SK payload header
- byte[] tempArray = new byte[IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH];
- inputBuffer.get(tempArray);
-
- // Extract bytes for authentication and decryption.
- byte[] iv = new byte[expectedIvLen];
-
- int encryptedDataLen =
- message.length
- - (IkeHeader.IKE_HEADER_LENGTH
- + GENERIC_HEADER_LENGTH
- + expectedIvLen
- + expectedChecksumLen);
- // IkeMessage will catch exception if encryptedDataLen is negative.
- byte[] encryptedData = new byte[encryptedDataLen];
-
- byte[] integrityChecksum = new byte[expectedChecksumLen];
- inputBuffer.get(iv).get(encryptedData).get(integrityChecksum);
-
- // Authenticate and decrypt.
- validateChecksumOrThrow(message, integrityMac, expectedChecksumLen, integrityChecksum);
- unencryptedPayloads = decrypt(encryptedData, decryptCipher, dKey, iv);
+ mIkeEncryptedPayloadBody =
+ new IkeEncryptedPayloadBody(
+ message, integrityMac, expectedChecksumLen, decryptCipher, dKey);
}
- // TODO: Add another constructor for AEAD protected payload.
-
- private void validateChecksumOrThrow(
- byte[] message, Mac integrityMac, int expectedChecksumLen, byte[] integrityChecksum)
- throws GeneralSecurityException {
- ByteBuffer inputBuffer = ByteBuffer.wrap(message, 0, message.length - expectedChecksumLen);
- integrityMac.update(inputBuffer);
- byte[] calculatedChecksum =
- Arrays.copyOfRange(integrityMac.doFinal(), 0, expectedChecksumLen);
-
- if (!Arrays.equals(integrityChecksum, calculatedChecksum)) {
- throw new GeneralSecurityException("Message authentication failed. ");
- }
+ /**
+ * Return unencrypted payload list
+ *
+ * @return unencrypted payload list in a byte array.
+ */
+ public byte[] getUnencryptedPayloads() {
+ return mIkeEncryptedPayloadBody.getUnencryptedData();
}
- private byte[] decrypt(byte[] encryptedData, Cipher decryptCipher, SecretKey dKey, byte[] iv)
- throws GeneralSecurityException {
- IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
- decryptCipher.init(Cipher.DECRYPT_MODE, dKey, ivParameterSpec);
-
- ByteBuffer inputBuffer = ByteBuffer.wrap(encryptedData);
- ByteBuffer outputBuffer = ByteBuffer.allocate(encryptedData.length);
- decryptCipher.doFinal(inputBuffer, outputBuffer);
-
- // Remove padding
- outputBuffer.rewind();
- int padLength = Byte.toUnsignedInt(outputBuffer.get(encryptedData.length - 1));
- byte[] decryptedData = new byte[encryptedData.length - padLength - 1];
-
- outputBuffer.get(decryptedData);
- return decryptedData;
- }
+ // TODO: Add another constructor for AEAD protected payload.
/**
- * Throw an Exception when trying to encode this payload.
+ * Encode this payload to a ByteBuffer.
*
- * @throws UnsupportedOperationException for this payload.
+ * @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) {
- // TODO: Implement thie method
- throw new UnsupportedOperationException(
- "It is not supported to encode a " + getTypeString());
+ encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
+ byteBuffer.put(mIkeEncryptedPayloadBody.encode());
}
/**
@@ -143,9 +96,7 @@ public final class IkeSkPayload extends IkePayload {
*/
@Override
protected int getPayloadLength() {
- // TODO: Implement thie method
- throw new UnsupportedOperationException(
- "It is not supported to get length of a " + getTypeString());
+ return GENERIC_HEADER_LENGTH + mIkeEncryptedPayloadBody.getLength();
}
/**
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java
index 3f97162e..aebd1194 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSkPayloadTest.java
@@ -22,7 +22,9 @@ import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
+import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
+import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -59,7 +61,6 @@ public final class IkeSkPayloadTest {
private static final String ENCR_ALGO_AES_CBC = "AES/CBC/NoPadding";
private static final String INTE_ALGO_HMAC_SHA1 = "HmacSHA1";
- private static final int IV_LEN = 16;
private static final int CHECKSUM_LEN = 12;
private Cipher mAesCbcDecryptCipher;
@@ -90,12 +91,11 @@ public final class IkeSkPayloadTest {
mHmacSha1IntegrityMac,
CHECKSUM_LEN,
mAesCbcDecryptCipher,
- mAesCbcDecryptKey,
- IV_LEN)
+ mAesCbcDecryptKey)
.first;
byte[] expectedPlaintext =
TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_DECRYPTED_BODY_HEX_STRING);
- assertArrayEquals(expectedPlaintext, payload.unencryptedPayloads);
+ assertArrayEquals(expectedPlaintext, payload.getUnencryptedPayloads());
}
@Test
@@ -109,10 +109,28 @@ public final class IkeSkPayloadTest {
mHmacSha1IntegrityMac,
CHECKSUM_LEN,
mAesCbcDecryptCipher,
- mAesCbcDecryptKey,
- IV_LEN);
+ mAesCbcDecryptKey);
fail("Expected GeneralSecurityException: Invalid checksum.");
} catch (GeneralSecurityException expected) {
}
}
+
+ @Test
+ public void testEncode() throws Exception {
+ byte[] message = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_HEX_STRING);
+ byte[] payloadBytes =
+ Arrays.copyOfRange(message, IkeHeader.IKE_HEADER_LENGTH, message.length);
+ IkeSkPayload payload =
+ IkePayloadFactory.getIkeSkPayload(
+ message,
+ mHmacSha1IntegrityMac,
+ CHECKSUM_LEN,
+ mAesCbcDecryptCipher,
+ mAesCbcDecryptKey)
+ .first;
+ int payloadLength = payload.getPayloadLength();
+ ByteBuffer buffer = ByteBuffer.allocate(payloadLength);
+ payload.encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_ID_INITIATOR, buffer);
+ assertArrayEquals(payloadBytes, buffer.array());
+ }
}