diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java index 7e622f658a..5da0d26fcc 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 @@ public class PacketUtils { 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 @@ public class PacketUtils { 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 class PacketUtils { 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 @@ public class PacketUtils { * 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. + * + *

For Tunnel mode, payload will be a full IP header + attached payloads + * + *

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 class PacketUtils { } 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 @@ public class PacketUtils { 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 - * - *

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 @@ public class PacketUtils { 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 @@ public class PacketUtils { // 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 class PacketUtils { } } + 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 */