aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorevitayan <evitayan@google.com>2019-02-12 18:30:14 -0800
committerevitayan <evitayan@google.com>2019-02-22 16:29:15 -0800
commit22ae538b8866ddfd22c981371a1c49dc406d98e2 (patch)
tree1da6ccadb108e429384c3f54cfc3d75da94675fa
parent4b94676d40b428c75748221e7f79442415bc6dc7 (diff)
downloadike-22ae538b8866ddfd22c981371a1c49dc406d98e2.tar.gz
Support encrypting IkeEncryptedPayloadBody
This commit: - Add constructor to support calculating checksum over and encrypting outbound IkeEncryptedPayloadBody - Add another constructor that take iv and padding as input for testing Bug: 122555731 Test: FrameworksIkeTests IkeEncryptedPayloadBodyTest Change-Id: I25c71a102ab957cd89d4b67490ea323d40de9a12
-rw-r--r--src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java109
-rw-r--r--tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java177
2 files changed, 281 insertions, 5 deletions
diff --git a/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java b/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java
index c4eb3045..52ca2a76 100644
--- a/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java
+++ b/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBody.java
@@ -17,9 +17,11 @@
package com.android.ike.ikev2.message;
import com.android.ike.ikev2.exceptions.IkeException;
+import com.android.internal.annotations.VisibleForTesting;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
@@ -83,30 +85,113 @@ final class IkeEncryptedPayloadBody {
inputBuffer.get(mIv).get(mEncryptedAndPaddedData).get(mIntegrityChecksum);
// Authenticate and decrypt.
- validateChecksumOrThrow(message, integrityMac, expectedChecksumLen, mIntegrityChecksum);
+ byte[] dataToAuthenticate =
+ Arrays.copyOfRange(message, 0, message.length - expectedChecksumLen);
+ validateChecksumOrThrow(dataToAuthenticate, integrityMac, mIntegrityChecksum);
mUnencryptedData = decrypt(mEncryptedAndPaddedData, decryptCipher, dKey, mIv);
}
+ /**
+ * Package private constructor for constructing an instance of IkeEncryptedPayloadBody for
+ * building an outbound packet.
+ */
+ IkeEncryptedPayloadBody(
+ byte[] ikeAndPayloadHeader,
+ byte[] unencryptedPayloads,
+ Mac integrityMac,
+ int expectedChecksumLen,
+ Cipher encryptCipher,
+ SecretKey eKey) {
+ this(
+ ikeAndPayloadHeader,
+ unencryptedPayloads,
+ integrityMac,
+ expectedChecksumLen,
+ encryptCipher,
+ eKey,
+ encryptCipher.getIV(),
+ calculatePadding(unencryptedPayloads.length, encryptCipher.getBlockSize()));
+ }
+
+ /** Package private constructor only for testing. */
+ @VisibleForTesting
+ IkeEncryptedPayloadBody(
+ byte[] ikeAndPayloadHeader,
+ byte[] unencryptedPayloads,
+ Mac integrityMac,
+ int expectedChecksumLen,
+ Cipher encryptCipher,
+ SecretKey eKey,
+ byte[] iv,
+ byte[] padding) {
+ mUnencryptedData = unencryptedPayloads;
+
+ // Encrypt data
+ mIv = iv;
+ mEncryptedAndPaddedData = encrypt(unencryptedPayloads, encryptCipher, eKey, iv, padding);
+
+ // Calculate checksum
+ ByteBuffer inputBuffer =
+ ByteBuffer.allocate(
+ ikeAndPayloadHeader.length + iv.length + mEncryptedAndPaddedData.length);
+ inputBuffer.put(ikeAndPayloadHeader).put(iv).put(mEncryptedAndPaddedData);
+ mIntegrityChecksum =
+ calculateChecksum(inputBuffer.array(), integrityMac, expectedChecksumLen);
+ }
+
// 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);
+ /** Package private for testing */
+ @VisibleForTesting
+ static byte[] calculateChecksum(
+ byte[] dataToAuthenticate, Mac integrityMac, int expectedChecksumLen) {
+ ByteBuffer inputBuffer = ByteBuffer.wrap(dataToAuthenticate);
integrityMac.update(inputBuffer);
byte[] calculatedChecksum =
Arrays.copyOfRange(integrityMac.doFinal(), 0, expectedChecksumLen);
+ return calculatedChecksum;
+ }
+
+ private static void validateChecksumOrThrow(
+ byte[] dataToAuthenticate, Mac integrityMac, byte[] integrityChecksum)
+ throws GeneralSecurityException {
+ // TODO: Make it package private and add test.
+ int checkSumLen = integrityChecksum.length;
+ byte[] calculatedChecksum =
+ calculateChecksum(dataToAuthenticate, integrityMac, checkSumLen);
if (!Arrays.equals(integrityChecksum, calculatedChecksum)) {
throw new GeneralSecurityException("Message authentication failed.");
}
}
+ /** Package private for testing */
+ @VisibleForTesting
+ static byte[] encrypt(
+ byte[] dataToEncrypt, Cipher encryptCipher, SecretKey eKey, byte[] iv, byte[] padding) {
+ int padLength = padding.length;
+ int paddedDataLength = dataToEncrypt.length + padLength + PAD_LEN_LEN;
+ ByteBuffer inputBuffer = ByteBuffer.allocate(paddedDataLength);
+ inputBuffer.put(dataToEncrypt).put(padding).put((byte) padLength);
+ inputBuffer.rewind();
+
+ try {
+ // Encrypt data.
+ ByteBuffer outputBuffer = ByteBuffer.allocate(paddedDataLength);
+ encryptCipher.init(Cipher.ENCRYPT_MODE, eKey, new IvParameterSpec(iv));
+ encryptCipher.doFinal(inputBuffer, outputBuffer);
+ return outputBuffer.array();
+ } catch (GeneralSecurityException e) {
+ throw new IllegalArgumentException("Fail to encrypt IKE message. ", e);
+ }
+ }
+
private static byte[] decrypt(
byte[] encryptedData, Cipher decryptCipher, SecretKey dKey, byte[] iv)
throws GeneralSecurityException {
+ // TODO: Make it package private and add test.
decryptCipher.init(Cipher.DECRYPT_MODE, dKey, new IvParameterSpec(iv));
ByteBuffer inputBuffer = ByteBuffer.wrap(encryptedData);
@@ -122,6 +207,20 @@ final class IkeEncryptedPayloadBody {
return decryptedData;
}
+ /** Package private for testing */
+ @VisibleForTesting
+ static byte[] calculatePadding(int dataToEncryptLength, int blockSize) {
+ // Sum of dataToEncryptLength, PAD_LEN_LEN and padLength should be aligned with block size.
+ int unpaddedLen = dataToEncryptLength + PAD_LEN_LEN;
+ int padLength = (unpaddedLen + blockSize - 1) / blockSize * blockSize - unpaddedLen;
+ byte[] padding = new byte[padLength];
+
+ // According to RFC 7296, "Padding MAY contain any value".
+ new SecureRandom().nextBytes(padding);
+
+ return padding;
+ }
+
/** Package private */
byte[] getUnencryptedData() {
return mUnencryptedData;
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java
new file mode 100644
index 00000000..48669e89
--- /dev/null
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeEncryptedPayloadBodyTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.Before;
+import org.junit.Test;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+public final class IkeEncryptedPayloadBodyTest {
+
+ private static final String IKE_AUTH_INIT_REQUEST_HEADER =
+ "5f54bf6d8b48e6e1909232b3d1edcb5c2e20230800000001000000ec";
+ private static final String IKE_AUTH_INIT_REQUEST_SK_HEADER = "230000d0";
+ private static final String IKE_AUTH_INIT_REQUEST_IV = "b9132b7bb9f658dfdc648e5017a6322a";
+ private static final String IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA =
+ "030c316ce55f365760d46426ce5cfc78bd1ed9abff63eb9594c1bd58"
+ + "46de333ecd3ea2b705d18293b130395300ba92a351041345"
+ + "0a10525cea51b2753b4e92b081fd78d995659a98f742278f"
+ + "f9b8fd3e21554865c15c79a5134d66b2744966089e416c60"
+ + "a274e44a9a3f084eb02f3bdce1e7de9de8d9a62773ab563b"
+ + "9a69ba1db03c752acb6136452b8a86c41addb4210d68c423"
+ + "efed80e26edca5fa3fe5d0a5ca9375ce332c474b93fb1fa3"
+ + "59eb4e81";
+ private static final String IKE_AUTH_INIT_REQUEST_CHECKSUM = "ae6e0f22abdad69ba8007d50";
+
+ private static final String IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA =
+ "2400000c010000000a50500d2700000c010000000a505050"
+ + "2100001c02000000df7c038aefaaa32d3f44b228b52a3327"
+ + "44dfb2c12c00002c00000028010304032ad4c0a20300000c"
+ + "0100000c800e008003000008030000020000000805000000"
+ + "2d00001801000000070000100000ffff00000000ffffffff"
+ + "2900001801000000070000100000ffff00000000ffffffff"
+ + "29000008000040000000000c0000400100000001";
+ private static final String IKE_AUTH_INIT_REQUEST_PADDING = "0000000000000000000000";
+ private static final int HMAC_SHA1_CHECKSUM_LEN = 12;
+
+ private static final String ENCR_KEY_FROM_INIT_TO_RESP = "5cbfd33f75796c0188c4a3a546aec4a1";
+ private static final String INTE_KEY_FROM_INIT_TO_RESP =
+ "554fbf5a05b7f511e05a30ce23d874db9ef55e51";
+
+ private static final String ENCR_ALGO_AES_CBC = "AES/CBC/NoPadding";
+ private static final String INTE_ALGO_HMAC_SHA1 = "HmacSHA1";
+
+ private Cipher mAesCbcCipher;
+ private SecretKey mAesCbcKey;
+ private Mac mHmacSha1IntegrityMac;
+
+ // TODO: Add tests for authenticating and decrypting received message.
+ @Before
+ public void setUp() throws Exception {
+ mAesCbcCipher = Cipher.getInstance(ENCR_ALGO_AES_CBC, IkeMessage.getSecurityProvider());
+ byte[] encryptKeyBytes = TestUtils.hexStringToByteArray(ENCR_KEY_FROM_INIT_TO_RESP);
+ mAesCbcKey = new SecretKeySpec(encryptKeyBytes, ENCR_ALGO_AES_CBC);
+
+ mHmacSha1IntegrityMac =
+ Mac.getInstance(INTE_ALGO_HMAC_SHA1, IkeMessage.getSecurityProvider());
+ byte[] integrityKeyBytes = TestUtils.hexStringToByteArray(INTE_KEY_FROM_INIT_TO_RESP);
+ SecretKeySpec integrityKey = new SecretKeySpec(integrityKeyBytes, INTE_ALGO_HMAC_SHA1);
+ mHmacSha1IntegrityMac.init(integrityKey);
+ }
+
+ @Test
+ public void testCalculateChecksum() throws Exception {
+ String hexStringToAuthenticate =
+ IKE_AUTH_INIT_REQUEST_HEADER
+ + IKE_AUTH_INIT_REQUEST_SK_HEADER
+ + IKE_AUTH_INIT_REQUEST_IV
+ + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA;
+ byte[] byteToAuthenticate = TestUtils.hexStringToByteArray(hexStringToAuthenticate);
+
+ byte[] expectedCheckSum = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_CHECKSUM);
+
+ byte[] calculatedChecksum =
+ IkeEncryptedPayloadBody.calculateChecksum(
+ byteToAuthenticate, mHmacSha1IntegrityMac, HMAC_SHA1_CHECKSUM_LEN);
+
+ assertArrayEquals(expectedCheckSum, calculatedChecksum);
+ }
+
+ @Test
+ public void testCalculatePaddingPlaintextShorterThanBlockSize() throws Exception {
+ int blockSize = 16;
+ int plainTextLength = 15;
+ int expectedPadLength = 0;
+
+ byte[] calculatedPadding =
+ IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize);
+ assertEquals(expectedPadLength, calculatedPadding.length);
+ }
+
+ @Test
+ public void testCalculatePaddingPlaintextInBlockSize() throws Exception {
+ int blockSize = 16;
+ int plainTextLength = 16;
+ int expectedPadLength = 15;
+
+ byte[] calculatedPadding =
+ IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize);
+ assertEquals(expectedPadLength, calculatedPadding.length);
+ }
+
+ @Test
+ public void testCalculatePaddingPlaintextLongerThanBlockSize() throws Exception {
+ int blockSize = 16;
+ int plainTextLength = 17;
+ int expectedPadLength = 14;
+
+ byte[] calculatedPadding =
+ IkeEncryptedPayloadBody.calculatePadding(plainTextLength, blockSize);
+ assertEquals(expectedPadLength, calculatedPadding.length);
+ }
+
+ @Test
+ public void testEncrypt() throws Exception {
+ byte[] dataToEncrypt =
+ TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA);
+ byte[] iv = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_IV);
+ byte[] padding = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_PADDING);
+
+ byte[] calculatedData =
+ IkeEncryptedPayloadBody.encrypt(
+ dataToEncrypt, mAesCbcCipher, mAesCbcKey, iv, padding);
+ byte[] expectedData =
+ TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA);
+
+ assertArrayEquals(expectedData, calculatedData);
+ }
+
+ @Test
+ public void testBuildAndEncodeOutboundIkeEncryptedPayloadBody() throws Exception {
+ byte[] ikeAndPayloadHeader =
+ TestUtils.hexStringToByteArray(
+ IKE_AUTH_INIT_REQUEST_HEADER + IKE_AUTH_INIT_REQUEST_SK_HEADER);
+ byte[] unencryptedPayloads =
+ TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_UNENCRYPTED_DATA);
+ byte[] iv = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_IV);
+ byte[] padding = TestUtils.hexStringToByteArray(IKE_AUTH_INIT_REQUEST_PADDING);
+
+ IkeEncryptedPayloadBody paylaodBody =
+ new IkeEncryptedPayloadBody(
+ ikeAndPayloadHeader,
+ unencryptedPayloads,
+ mHmacSha1IntegrityMac,
+ HMAC_SHA1_CHECKSUM_LEN,
+ mAesCbcCipher,
+ mAesCbcKey,
+ iv,
+ padding);
+
+ byte[] expectedEncodedData =
+ TestUtils.hexStringToByteArray(
+ IKE_AUTH_INIT_REQUEST_IV
+ + IKE_AUTH_INIT_REQUEST_ENCRYPT_PADDED_DATA
+ + IKE_AUTH_INIT_REQUEST_CHECKSUM);
+ assertArrayEquals(expectedEncodedData, paylaodBody.encode());
+ }
+}