Support algorithm configurability in PacketUtils EspHeader
This CL adds support to configure the IPsec algortihms to build
EspHeader.
This is a prepration CL to test kernel implementation of IPsec
algorithms.
Bug: 171083832
Test: atest CtsNetTestCases
Original-Change: https://android-review.googlesource.com/1503693
Merged-In: I53f59815d6cb879dae622fcbd17616564a97111a
Change-Id: I53f59815d6cb879dae622fcbd17616564a97111a
diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java
index 7e622f6..5da0d26 100644
--- a/tests/cts/net/src/android/net/cts/PacketUtils.java
+++ b/tests/cts/net/src/android/net/cts/PacketUtils.java
@@ -44,6 +44,7 @@
static final int TCP_HDRLEN = 20;
static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
static final int ESP_BLK_SIZE = 4; // ESP has to be 4-byte aligned
+ static final int ESP_TRAILER_LEN = 2;
// Not defined in OsConstants
static final int IPPROTO_IPV4 = 4;
@@ -65,6 +66,7 @@
static final int CHACHA20_POLY1305_ICV_LEN = 16;
// Authentication parameters
+ static final int HMAC_SHA256_ICV_LEN = 16;
static final int HMAC_SHA512_KEY_LEN = 64;
static final int HMAC_SHA512_ICV_LEN = 32;
static final int AES_XCBC_KEY_LEN = 16;
@@ -328,8 +330,9 @@
public final int nextHeader;
public final int spi;
public final int seqNum;
- public final byte[] key;
public final byte[] payload;
+ public final EspCipher cipher;
+ public final EspAuth auth;
/**
* Generic constructor for ESP headers.
@@ -340,11 +343,43 @@
* calculated using the pre-encryption IP header
*/
public EspHeader(int nextHeader, int spi, int seqNum, byte[] key, byte[] payload) {
+ this(nextHeader, spi, seqNum, payload, getDefaultCipher(key), getDefaultAuth(key));
+ }
+
+ /**
+ * Generic constructor for ESP headers that allows configuring encryption and authentication
+ * algortihms.
+ *
+ * <p>For Tunnel mode, payload will be a full IP header + attached payloads
+ *
+ * <p>For Transport mode, payload will be only the attached payloads, but with the checksum
+ * calculated using the pre-encryption IP header
+ */
+ public EspHeader(
+ int nextHeader,
+ int spi,
+ int seqNum,
+ byte[] payload,
+ EspCipher cipher,
+ EspAuth auth) {
this.nextHeader = nextHeader;
this.spi = spi;
this.seqNum = seqNum;
- this.key = key;
this.payload = payload;
+ this.cipher = cipher;
+ this.auth = auth;
+
+ if (cipher instanceof EspCipherNull && auth instanceof EspAuthNull) {
+ throw new IllegalArgumentException("No algorithm is provided");
+ }
+ }
+
+ private static EspCipher getDefaultCipher(byte[] key) {
+ return new EspCryptCipher(AES_CBC, AES_CBC_BLK_SIZE, key, AES_CBC_IV_LEN);
+ }
+
+ private static EspAuth getDefaultAuth(byte[] key) {
+ return new EspAuth(HMAC_SHA_256, key, HMAC_SHA256_ICV_LEN);
}
public int getProtocolId() {
@@ -352,9 +387,8 @@
}
public short length() {
- // ALWAYS uses AES-CBC, HMAC-SHA256 (128b trunc len)
- return (short)
- calculateEspPacketSize(payload.length, AES_CBC_IV_LEN, AES_CBC_BLK_SIZE, 128);
+ return calculateEspPacketSize(
+ payload.length, cipher.ivLen, cipher.blockSize, auth.icvLen * 8);
}
public byte[] getPacketBytes(IpHeader header) throws Exception {
@@ -368,58 +402,12 @@
ByteBuffer espPayloadBuffer = ByteBuffer.allocate(DATA_BUFFER_LEN);
espPayloadBuffer.putInt(spi);
espPayloadBuffer.putInt(seqNum);
- espPayloadBuffer.put(getCiphertext(key));
- espPayloadBuffer.put(getIcv(getByteArrayFromBuffer(espPayloadBuffer)), 0, 16);
+ espPayloadBuffer.put(cipher.getCipherText(nextHeader, payload, spi, seqNum));
+ espPayloadBuffer.put(auth.getIcv(getByteArrayFromBuffer(espPayloadBuffer)));
+
resultBuffer.put(getByteArrayFromBuffer(espPayloadBuffer));
}
-
- private byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
- Mac sha256HMAC = Mac.getInstance(HMAC_SHA_256);
- SecretKeySpec authKey = new SecretKeySpec(key, HMAC_SHA_256);
- sha256HMAC.init(authKey);
-
- return sha256HMAC.doFinal(authenticatedSection);
- }
-
- /**
- * Encrypts and builds ciphertext block. Includes the IV, Padding and Next-Header blocks
- *
- * <p>The ciphertext does NOT include the SPI/Sequence numbers, or the ICV.
- */
- private byte[] getCiphertext(byte[] key) throws GeneralSecurityException {
- int paddedLen = calculateEspEncryptedLength(payload.length, AES_CBC_BLK_SIZE);
- ByteBuffer paddedPayload = ByteBuffer.allocate(paddedLen);
- paddedPayload.put(payload);
-
- // Add padding - consecutive integers from 0x01
- int pad = 1;
- while (paddedPayload.position() < paddedPayload.limit()) {
- paddedPayload.put((byte) pad++);
- }
-
- paddedPayload.position(paddedPayload.limit() - 2);
- paddedPayload.put((byte) (paddedLen - 2 - payload.length)); // Pad length
- paddedPayload.put((byte) nextHeader);
-
- // Generate Initialization Vector
- byte[] iv = new byte[AES_CBC_IV_LEN];
- new SecureRandom().nextBytes(iv);
- IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
- SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
-
- // Encrypt payload
- Cipher cipher = Cipher.getInstance(AES_CBC);
- cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
- byte[] encrypted = cipher.doFinal(getByteArrayFromBuffer(paddedPayload));
-
- // Build ciphertext
- ByteBuffer cipherText = ByteBuffer.allocate(AES_CBC_IV_LEN + encrypted.length);
- cipherText.put(iv);
- cipherText.put(encrypted);
-
- return getByteArrayFromBuffer(cipherText);
- }
}
private static int addAndWrapForChecksum(int currentChecksum, int value) {
@@ -436,7 +424,7 @@
return (short) ((~val) & 0xffff);
}
- public static int calculateEspPacketSize(
+ public static short calculateEspPacketSize(
int payloadLen, int cryptIvLength, int cryptBlockSize, int authTruncLen) {
final int ESP_HDRLEN = 4 + 4; // SPI + Seq#
final int ICV_LEN = authTruncLen / 8; // Auth trailer; based on truncation length
@@ -444,7 +432,7 @@
// Align to block size of encryption algorithm
payloadLen = calculateEspEncryptedLength(payloadLen, cryptBlockSize);
- return payloadLen + ESP_HDRLEN + ICV_LEN;
+ return (short) (payloadLen + ESP_HDRLEN + ICV_LEN);
}
private static int calculateEspEncryptedLength(int payloadLen, int cryptBlockSize) {
@@ -475,6 +463,144 @@
}
}
+ public abstract static class EspCipher {
+ public final String algoName;
+ public final int blockSize;
+ public final byte[] key;
+ public final int ivLen;
+ protected byte[] iv;
+
+ public EspCipher(String algoName, int blockSize, byte[] key, int ivLen) {
+ this.algoName = algoName;
+ this.blockSize = blockSize;
+ this.key = key;
+ this.ivLen = ivLen;
+ this.iv = getIv(ivLen);
+ }
+
+ public static byte[] getPaddedPayload(int nextHeader, byte[] payload, int blockSize) {
+ final int paddedLen = calculateEspEncryptedLength(payload.length, blockSize);
+ final ByteBuffer paddedPayload = ByteBuffer.allocate(paddedLen);
+ paddedPayload.put(payload);
+
+ // Add padding - consecutive integers from 0x01
+ byte pad = 1;
+ while (paddedPayload.position() < paddedPayload.limit() - ESP_TRAILER_LEN) {
+ paddedPayload.put((byte) pad++);
+ }
+
+ // Add padding length and next header
+ paddedPayload.put((byte) (paddedLen - ESP_TRAILER_LEN - payload.length));
+ paddedPayload.put((byte) nextHeader);
+
+ return getByteArrayFromBuffer(paddedPayload);
+ }
+
+ private static byte[] getIv(int ivLen) {
+ final byte[] iv = new byte[ivLen];
+ new SecureRandom().nextBytes(iv);
+ return iv;
+ }
+
+ public abstract byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+ throws GeneralSecurityException;
+ }
+
+ public static class EspCipherNull extends EspCipher {
+ private static final String CRYPT_NULL = "CRYPT_NULL";
+ private static final int IV_LEN_UNUSED = 0;
+ private static final byte[] KEY_UNUSED = new byte[0];
+
+ private static final EspCipherNull INSTANCE = new EspCipherNull();
+
+ private EspCipherNull() {
+ super(CRYPT_NULL, ESP_BLK_SIZE, KEY_UNUSED, IV_LEN_UNUSED);
+ }
+
+ public static EspCipherNull getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+ throws GeneralSecurityException {
+ return getPaddedPayload(nextHeader, payload, blockSize);
+ }
+ }
+
+ public static class EspCryptCipher extends EspCipher {
+ public EspCryptCipher(String algoName, int blockSize, byte[] key, int ivLen) {
+ super(algoName, blockSize, key, ivLen);
+ }
+
+ @Override
+ public byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+ throws GeneralSecurityException {
+ final IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
+ final SecretKeySpec secretKeySpec = new SecretKeySpec(key, algoName);
+
+ // Encrypt payload
+ final Cipher cipher = Cipher.getInstance(algoName);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+ final byte[] encrypted =
+ cipher.doFinal(getPaddedPayload(nextHeader, payload, blockSize));
+
+ // Build ciphertext
+ final ByteBuffer cipherText = ByteBuffer.allocate(iv.length + encrypted.length);
+ cipherText.put(iv);
+ cipherText.put(encrypted);
+
+ return getByteArrayFromBuffer(cipherText);
+ }
+ }
+
+ // TODO: Implement EspAeadCipher in the following CL
+
+ public static class EspAuth {
+ public final String algoName;
+ public final byte[] key;
+ public final int icvLen;
+
+ public EspAuth(String algoName, byte[] key, int icvLen) {
+ this.algoName = algoName;
+ this.key = key;
+ this.icvLen = icvLen;
+ }
+
+ public byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
+ final Mac mac = Mac.getInstance(algoName);
+ final SecretKeySpec authKey = new SecretKeySpec(key, HMAC_SHA_256);
+ mac.init(authKey);
+
+ final ByteBuffer buffer = ByteBuffer.wrap(mac.doFinal(authenticatedSection));
+ final byte[] icv = new byte[icvLen];
+ buffer.get(icv);
+ return icv;
+ }
+ }
+
+ public static class EspAuthNull extends EspAuth {
+ private static final String AUTH_NULL = "AUTH_NULL";
+ private static final int ICV_LEN_UNUSED = 0;
+ private static final byte[] KEY_UNUSED = new byte[0];
+ private static final byte[] ICV_EMPTY = new byte[0];
+
+ private static final EspAuthNull INSTANCE = new EspAuthNull();
+
+ private EspAuthNull() {
+ super(AUTH_NULL, KEY_UNUSED, ICV_LEN_UNUSED);
+ }
+
+ public static EspAuthNull getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
+ return ICV_EMPTY;
+ }
+ }
+
/*
* Debug printing
*/