blob: 73dc80c62ddfb11d0fff8f672c6a824d92cf0965 [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.net.cts;
import static android.net.IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305;
import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
import static android.net.cts.PacketUtils.AES_CTR;
import static android.net.cts.PacketUtils.AES_CTR_BLK_SIZE;
import static android.net.cts.PacketUtils.AES_CTR_IV_LEN;
import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_20;
import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_28;
import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_36;
import static android.net.cts.PacketUtils.AES_CTR_SALT_LEN;
import static android.net.cts.PacketUtils.CHACHA20_POLY1305;
import static android.net.cts.PacketUtils.CHACHA20_POLY1305_BLK_SIZE;
import static android.net.cts.PacketUtils.CHACHA20_POLY1305_ICV_LEN;
import static android.net.cts.PacketUtils.CHACHA20_POLY1305_IV_LEN;
import static android.net.cts.PacketUtils.CHACHA20_POLY1305_KEY_LEN;
import static android.net.cts.PacketUtils.CHACHA20_POLY1305_SALT_LEN;
import static android.net.cts.PacketUtils.ESP_HDRLEN;
import static android.net.cts.PacketUtils.IP6_HDRLEN;
import static android.net.cts.PacketUtils.getIpHeader;
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assume.assumeTrue;
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.Network;
import android.net.TestNetworkInterface;
import android.net.cts.PacketUtils.BytePayload;
import android.net.cts.PacketUtils.EspAeadCipher;
import android.net.cts.PacketUtils.EspAuth;
import android.net.cts.PacketUtils.EspAuthNull;
import android.net.cts.PacketUtils.EspCipher;
import android.net.cts.PacketUtils.EspCryptCipher;
import android.net.cts.PacketUtils.EspHeader;
import android.net.cts.PacketUtils.IpHeader;
import android.net.cts.PacketUtils.UdpHeader;
import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class IpSecAlgorithmImplTest extends IpSecBaseTest {
private static final InetAddress LOCAL_ADDRESS =
InetAddress.parseNumericAddress("2001:db8:1::1");
private static final InetAddress REMOTE_ADDRESS =
InetAddress.parseNumericAddress("2001:db8:1::2");
private static final int REMOTE_PORT = 12345;
private static final IpSecManager IPSEC_MANAGER =
InstrumentationRegistry.getContext().getSystemService(IpSecManager.class);
private static class CheckCryptoImplTest implements TestNetworkRunnable.Test {
private final IpSecAlgorithm mIpsecEncryptAlgo;
private final IpSecAlgorithm mIpsecAuthAlgo;
private final IpSecAlgorithm mIpsecAeadAlgo;
private final EspCipher mEspCipher;
private final EspAuth mEspAuth;
public CheckCryptoImplTest(
IpSecAlgorithm ipsecEncryptAlgo,
IpSecAlgorithm ipsecAuthAlgo,
IpSecAlgorithm ipsecAeadAlgo,
EspCipher espCipher,
EspAuth espAuth) {
mIpsecEncryptAlgo = ipsecEncryptAlgo;
mIpsecAuthAlgo = ipsecAuthAlgo;
mIpsecAeadAlgo = ipsecAeadAlgo;
mEspCipher = espCipher;
mEspAuth = espAuth;
}
private static byte[] buildTransportModeEspPayload(
int srcPort, int dstPort, int spi, EspCipher espCipher, EspAuth espAuth)
throws Exception {
final UdpHeader udpPayload =
new UdpHeader(srcPort, dstPort, new BytePayload(TEST_DATA));
final IpHeader preEspIpHeader =
getIpHeader(
udpPayload.getProtocolId(), LOCAL_ADDRESS, REMOTE_ADDRESS, udpPayload);
final PacketUtils.EspHeader espPayload =
new EspHeader(
udpPayload.getProtocolId(),
spi,
1 /* sequence number */,
udpPayload.getPacketBytes(preEspIpHeader),
espCipher,
espAuth);
return espPayload.getPacketBytes(preEspIpHeader);
}
@Override
public void runTest(TestNetworkInterface testIface, TestNetworkCallback tunNetworkCallback)
throws Exception {
final TunUtils tunUtils = new TunUtils(testIface.getFileDescriptor());
tunNetworkCallback.waitForAvailable();
final Network testNetwork = tunNetworkCallback.currentNetwork;
final IpSecTransform.Builder transformBuilder =
new IpSecTransform.Builder(InstrumentationRegistry.getContext());
if (mIpsecAeadAlgo != null) {
transformBuilder.setAuthenticatedEncryption(mIpsecAeadAlgo);
} else {
if (mIpsecEncryptAlgo != null) {
transformBuilder.setEncryption(mIpsecEncryptAlgo);
}
if (mIpsecAuthAlgo != null) {
transformBuilder.setAuthentication(mIpsecAuthAlgo);
}
}
try (final IpSecManager.SecurityParameterIndex outSpi =
IPSEC_MANAGER.allocateSecurityParameterIndex(REMOTE_ADDRESS);
final IpSecManager.SecurityParameterIndex inSpi =
IPSEC_MANAGER.allocateSecurityParameterIndex(LOCAL_ADDRESS);
final IpSecTransform outTransform =
transformBuilder.buildTransportModeTransform(LOCAL_ADDRESS, outSpi);
final IpSecTransform inTransform =
transformBuilder.buildTransportModeTransform(REMOTE_ADDRESS, inSpi);
// Bind localSocket to a random available port.
final DatagramSocket localSocket = new DatagramSocket(0)) {
IPSEC_MANAGER.applyTransportModeTransform(
localSocket, IpSecManager.DIRECTION_IN, inTransform);
IPSEC_MANAGER.applyTransportModeTransform(
localSocket, IpSecManager.DIRECTION_OUT, outTransform);
// Send ESP packet
final DatagramPacket outPacket =
new DatagramPacket(
TEST_DATA, 0, TEST_DATA.length, REMOTE_ADDRESS, REMOTE_PORT);
testNetwork.bindSocket(localSocket);
localSocket.send(outPacket);
final byte[] outEspPacket =
tunUtils.awaitEspPacket(outSpi.getSpi(), false /* useEncap */);
// Remove transform for good hygiene
IPSEC_MANAGER.removeTransportModeTransforms(localSocket);
// Get the kernel-generated ESP payload
final byte[] outEspPayload = new byte[outEspPacket.length - IP6_HDRLEN];
System.arraycopy(outEspPacket, IP6_HDRLEN, outEspPayload, 0, outEspPayload.length);
// Get the IV of the kernel-generated ESP payload
final byte[] iv =
Arrays.copyOfRange(
outEspPayload, ESP_HDRLEN, ESP_HDRLEN + mEspCipher.ivLen);
// Build ESP payload using the kernel-generated IV and the user space crypto
// implementations
mEspCipher.updateIv(iv);
final byte[] expectedEspPayload =
buildTransportModeEspPayload(
localSocket.getLocalPort(),
REMOTE_PORT,
outSpi.getSpi(),
mEspCipher,
mEspAuth);
// Compare user-space-generated and kernel-generated ESP payload
assertArrayEquals(expectedEspPayload, outEspPayload);
}
}
@Override
public void cleanupTest() {
// Do nothing
}
@Override
public InetAddress[] getTestNetworkAddresses() {
return new InetAddress[] {LOCAL_ADDRESS};
}
}
private void checkAesCtr(int keyLen) throws Exception {
final byte[] cryptKey = getKeyBytes(keyLen);
final IpSecAlgorithm ipsecEncryptAlgo =
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CTR, cryptKey);
final EspCipher espCipher =
new EspCryptCipher(
AES_CTR, AES_CTR_BLK_SIZE, cryptKey, AES_CTR_IV_LEN, AES_CTR_SALT_LEN);
runWithShellPermissionIdentity(new TestNetworkRunnable(new CheckCryptoImplTest(
ipsecEncryptAlgo, null /* ipsecAuthAlgo */, null /* ipsecAeadAlgo */,
espCipher, EspAuthNull.getInstance())));
}
@Test
public void testAesCtr160() throws Exception {
assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
checkAesCtr(AES_CTR_KEY_LEN_20);
}
@Test
public void testAesCtr224() throws Exception {
assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
checkAesCtr(AES_CTR_KEY_LEN_28);
}
@Test
public void testAesCtr288() throws Exception {
assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
checkAesCtr(AES_CTR_KEY_LEN_36);
}
@Test
public void testChaCha20Poly1305() throws Exception {
assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
final byte[] cryptKey = getKeyBytes(CHACHA20_POLY1305_KEY_LEN);
final IpSecAlgorithm ipsecAeadAlgo =
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
cryptKey,
CHACHA20_POLY1305_ICV_LEN * 8);
final EspAeadCipher espAead =
new EspAeadCipher(
CHACHA20_POLY1305,
CHACHA20_POLY1305_BLK_SIZE,
cryptKey,
CHACHA20_POLY1305_IV_LEN,
CHACHA20_POLY1305_ICV_LEN,
CHACHA20_POLY1305_SALT_LEN);
runWithShellPermissionIdentity(
new TestNetworkRunnable(
new CheckCryptoImplTest(
null /* ipsecEncryptAlgo */,
null /* ipsecAuthAlgo */,
ipsecAeadAlgo,
espAead,
EspAuthNull.getInstance())));
}
}