Merge "Add support for AES-GCM-ESP as an IPSec algorithm" am: b6df7f0d35 am: d3a5792024 am: f92bfc3b77
am: 9c6f24d1ee

Change-Id: Ie9a43c7f0519a2d7ac3326944608190503804483
diff --git a/api/current.txt b/api/current.txt
index 87fa407..b86af14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25754,6 +25754,7 @@
     method public java.lang.String getName();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
     field public static final java.lang.String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final java.lang.String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final java.lang.String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -25801,6 +25802,7 @@
   public static class IpSecTransform.Builder {
     ctor public IpSecTransform.Builder(android.content.Context);
     method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index a868549..07bde73 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -27996,6 +27996,7 @@
     method public java.lang.String getName();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
     field public static final java.lang.String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final java.lang.String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final java.lang.String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -28043,6 +28044,7 @@
   public static class IpSecTransform.Builder {
     ctor public IpSecTransform.Builder(android.content.Context);
     method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index c93722f..ca72db6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -25954,6 +25954,7 @@
     method public java.lang.String getName();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
     field public static final java.lang.String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final java.lang.String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final java.lang.String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -26001,6 +26002,7 @@
   public static class IpSecTransform.Builder {
     ctor public IpSecTransform.Builder(android.content.Context);
     method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 79310e2..16b1452 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -31,7 +31,6 @@
  * RFC 4301.
  */
 public final class IpSecAlgorithm implements Parcelable {
-
     /**
      * AES-CBC Encryption/Ciphering Algorithm.
      *
@@ -68,6 +67,7 @@
      * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
      */
     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
+
     /**
      * SHA512 HMAC Authentication/Integrity Algorithm
      *
@@ -75,8 +75,24 @@
      */
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
+    /**
+     * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
+     *
+     * <p>Valid lengths for this key are {128, 192, 256}.
+     *
+     * <p>Valid ICV (truncation) lengths are {64, 96, 128}.
+     */
+    public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+
     /** @hide */
-    @StringDef({CRYPT_AES_CBC, AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, AUTH_HMAC_SHA512})
+    @StringDef({
+        CRYPT_AES_CBC,
+        AUTH_HMAC_MD5,
+        AUTH_HMAC_SHA1,
+        AUTH_HMAC_SHA256,
+        AUTH_HMAC_SHA512,
+        AUTH_CRYPT_AES_GCM
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AlgorithmName {}
 
@@ -102,7 +118,7 @@
      * @param algoName precise name of the algorithm to be used.
      * @param key non-null Key padded to a multiple of 8 bits.
      * @param truncLenBits the number of bits of output hash to use; only meaningful for
-     *     Authentication.
+     *     Authentication or Authenticated Encryption (equivalent to ICV length).
      */
     public IpSecAlgorithm(@AlgorithmName String algoName, byte[] key, int truncLenBits) {
         if (!isTruncationLengthValid(algoName, truncLenBits)) {
@@ -175,6 +191,8 @@
                 return (truncLenBits >= 192 && truncLenBits <= 384);
             case AUTH_HMAC_SHA512:
                 return (truncLenBits >= 256 && truncLenBits <= 512);
+            case AUTH_CRYPT_AES_GCM:
+                return (truncLenBits == 64 || truncLenBits == 96 || truncLenBits == 128);
             default:
                 return false;
         }
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 632b7fc..61b13a9 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -50,6 +50,9 @@
         // Authentication Algorithm
         private IpSecAlgorithm mAuthentication;
 
+        // Authenticated Encryption Algorithm
+        private IpSecAlgorithm mAuthenticatedEncryption;
+
         @Override
         public String toString() {
             return new StringBuilder()
@@ -59,6 +62,8 @@
                     .append(mEncryption)
                     .append(", mAuthentication=")
                     .append(mAuthentication)
+                    .append(", mAuthenticatedEncryption=")
+                    .append(mAuthenticatedEncryption)
                     .append("}")
                     .toString();
         }
@@ -118,6 +123,11 @@
         mFlow[direction].mAuthentication = authentication;
     }
 
+    /** Set the authenticated encryption algorithm for a given direction */
+    public void setAuthenticatedEncryption(int direction, IpSecAlgorithm authenticatedEncryption) {
+        mFlow[direction].mAuthenticatedEncryption = authenticatedEncryption;
+    }
+
     public void setNetwork(Network network) {
         mNetwork = network;
     }
@@ -163,6 +173,10 @@
         return mFlow[direction].mAuthentication;
     }
 
+    public IpSecAlgorithm getAuthenticatedEncryption(int direction) {
+        return mFlow[direction].mAuthenticatedEncryption;
+    }
+
     public Network getNetwork() {
         return mNetwork;
     }
@@ -199,9 +213,11 @@
         out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
         out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
         out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
+        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption, flags);
         out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
         out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
         out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
+        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption, flags);
         out.writeInt(mEncapType);
         out.writeInt(mEncapSocketResourceId);
         out.writeInt(mEncapRemotePort);
@@ -221,11 +237,15 @@
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+        mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption =
+                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
         mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+        mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption =
+                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         mEncapType = in.readInt();
         mEncapSocketResourceId = in.readInt();
         mEncapRemotePort = in.readInt();
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index e15a2c6..48b5bd5 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -281,6 +281,8 @@
          * <p>If encryption is set for a given direction without also providing an SPI for that
          * direction, creation of an IpSecTransform will fail upon calling a build() method.
          *
+         * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
+         *
          * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
          * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
          */
@@ -296,6 +298,8 @@
          * <p>If authentication is set for a given direction without also providing an SPI for that
          * direction, creation of an IpSecTransform will fail upon calling a build() method.
          *
+         * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
+         *
          * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
          * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
          */
@@ -306,6 +310,29 @@
         }
 
         /**
+         * Add an authenticated encryption algorithm to the transform for the given direction.
+         *
+         * <p>If an authenticated encryption algorithm is set for a given direction without also
+         * providing an SPI for that direction, creation of an IpSecTransform will fail upon calling
+         * a build() method.
+         *
+         * <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
+         * Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
+         * referred to in RFC 4301)
+         *
+         * <p>Authenticated encryption is mutually exclusive with encryption and authentication.
+         *
+         * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+         * @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
+         *     be applied.
+         */
+        public IpSecTransform.Builder setAuthenticatedEncryption(
+                @TransformDirection int direction, IpSecAlgorithm algo) {
+            mConfig.setAuthenticatedEncryption(direction, algo);
+            return this;
+        }
+
+        /**
          * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
          * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
          * given destination address.
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 2e1f142..cf1d33c 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -882,8 +882,14 @@
         for (int direction : DIRECTIONS) {
             IpSecAlgorithm crypt = config.getEncryption(direction);
             IpSecAlgorithm auth = config.getAuthentication(direction);
-            if (crypt == null && auth == null) {
-                throw new IllegalArgumentException("Encryption and Authentication are both null");
+            IpSecAlgorithm authenticatedEncryption = config.getAuthenticatedEncryption(direction);
+            if (authenticatedEncryption == null && crypt == null && auth == null) {
+                throw new IllegalArgumentException(
+                        "No Encryption or Authentication algorithms specified");
+            } else if (authenticatedEncryption != null && (auth != null || crypt != null)) {
+                throw new IllegalArgumentException(
+                        "Authenticated Encryption is mutually"
+                                + " exclusive with other Authentication or Encryption algorithms");
             }
 
             if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
@@ -922,6 +928,7 @@
         for (int direction : DIRECTIONS) {
             IpSecAlgorithm auth = c.getAuthentication(direction);
             IpSecAlgorithm crypt = c.getEncryption(direction);
+            IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
 
             spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
             int spi = spis[direction].getSpi();
@@ -942,6 +949,9 @@
                                 (crypt != null) ? crypt.getName() : "",
                                 (crypt != null) ? crypt.getKey() : null,
                                 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+                                (authCrypt != null) ? authCrypt.getName() : "",
+                                (authCrypt != null) ? authCrypt.getKey() : null,
+                                (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
                                 encapType,
                                 encapLocalPort,
                                 encapRemotePort);
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9057a10..b4b8094 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -17,10 +17,12 @@
 package com.android.server;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,7 +39,6 @@
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.support.test.filters.SmallTest;
-import android.system.OsConstants;
 
 import java.net.Socket;
 import java.util.Arrays;
@@ -53,8 +54,8 @@
 @RunWith(Parameterized.class)
 public class IpSecServiceParameterizedTest {
 
-    private static final int DROID_SPI = 0xD1201D;
-    private static final int DROID_SPI2 = DROID_SPI + 1;
+    private static final int TEST_SPI_OUT = 0xD1201D;
+    private static final int TEST_SPI_IN = TEST_SPI_OUT + 1;
 
     private final String mRemoteAddr;
 
@@ -81,6 +82,16 @@
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
 
+    private static final IpSecAlgorithm AUTH_ALGO =
+            new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
+    private static final IpSecAlgorithm CRYPT_ALGO =
+            new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+    private static final IpSecAlgorithm AEAD_ALGO =
+            new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, CRYPT_KEY, CRYPT_KEY.length * 4);
+
+    private static final int[] DIRECTIONS =
+            new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
+
     public IpSecServiceParameterizedTest(String remoteAddr) {
         mRemoteAddr = remoteAddr;
     }
@@ -103,14 +114,14 @@
                         eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         eq(mRemoteAddr),
-                        eq(DROID_SPI)))
-                .thenReturn(DROID_SPI);
+                        eq(TEST_SPI_OUT)))
+                .thenReturn(TEST_SPI_OUT);
 
         IpSecSpiResponse spiResp =
                 mIpSecService.reserveSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
+                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
         assertEquals(IpSecManager.Status.OK, spiResp.status);
-        assertEquals(DROID_SPI, spiResp.spi);
+        assertEquals(TEST_SPI_OUT, spiResp.spi);
     }
 
     @Test
@@ -120,56 +131,60 @@
                         eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         eq(mRemoteAddr),
-                        eq(DROID_SPI)))
-                .thenReturn(DROID_SPI);
+                        eq(TEST_SPI_OUT)))
+                .thenReturn(TEST_SPI_OUT);
 
         IpSecSpiResponse spiResp =
                 mIpSecService.reserveSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
+                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
 
         mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
+                        eq(spiResp.resourceId),
+                        anyInt(),
+                        anyString(),
+                        anyString(),
+                        eq(TEST_SPI_OUT));
     }
 
-    IpSecConfig buildIpSecConfig() throws Exception {
-        IpSecManager ipSecManager = new IpSecManager(mIpSecService);
-
-        // Mocking the netd to allocate SPI
+    private int getNewSpiResourceId(int direction, String remoteAddress, int returnSpi)
+            throws Exception {
         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
-                .thenReturn(DROID_SPI)
-                .thenReturn(DROID_SPI2);
+                .thenReturn(returnSpi);
 
-        IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
-        IpSecAlgorithm authAlgo =
-                new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
+        IpSecSpiResponse spi =
+                mIpSecService.reserveSecurityParameterIndex(
+                        direction,
+                        NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
+                        IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
+                        new Binder());
+        return spi.resourceId;
+    }
 
-        /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
-        IpSecManager.SecurityParameterIndex outSpi =
-                ipSecManager.reserveSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT,
-                        NetworkUtils.numericToInetAddress(mRemoteAddr));
-        IpSecManager.SecurityParameterIndex inSpi =
-                ipSecManager.reserveSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_IN,
-                        NetworkUtils.numericToInetAddress(mRemoteAddr));
-
-        IpSecConfig config = new IpSecConfig();
-        config.setSpiResourceId(IpSecTransform.DIRECTION_IN, inSpi.getResourceId());
-        config.setSpiResourceId(IpSecTransform.DIRECTION_OUT, outSpi.getResourceId());
-        config.setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo);
-        config.setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo);
-        config.setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo);
-        config.setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo);
+    private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception {
+        config.setSpiResourceId(
+                IpSecTransform.DIRECTION_OUT,
+                getNewSpiResourceId(IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT));
+        config.setSpiResourceId(
+                IpSecTransform.DIRECTION_IN,
+                getNewSpiResourceId(IpSecTransform.DIRECTION_IN, mRemoteAddr, TEST_SPI_IN));
         config.setRemoteAddress(mRemoteAddr);
-        return config;
+    }
+
+    private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception {
+        for (int direction : DIRECTIONS) {
+            config.setEncryption(direction, CRYPT_ALGO);
+            config.setAuthentication(direction, AUTH_ALGO);
+        }
     }
 
     @Test
     public void testCreateTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = buildIpSecConfig();
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
@@ -183,13 +198,72 @@
                         anyString(),
                         anyString(),
                         anyLong(),
-                        eq(DROID_SPI),
+                        eq(TEST_SPI_OUT),
                         eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
                         eq(AUTH_KEY),
                         anyInt(),
                         eq(IpSecAlgorithm.CRYPT_AES_CBC),
                         eq(CRYPT_KEY),
                         anyInt(),
+                        eq(""),
+                        isNull(),
+                        eq(0),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+        verify(mMockNetd)
+                .ipSecAddSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_IN),
+                        anyString(),
+                        anyString(),
+                        anyLong(),
+                        eq(TEST_SPI_IN),
+                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
+                        eq(AUTH_KEY),
+                        anyInt(),
+                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
+                        eq(CRYPT_KEY),
+                        anyInt(),
+                        eq(""),
+                        isNull(),
+                        eq(0),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+    }
+
+    @Test
+    public void testCreateTransportModeTransformAead() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+
+        ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_OUT, AEAD_ALGO);
+        ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+        verify(mMockNetd)
+                .ipSecAddSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        anyString(),
+                        anyLong(),
+                        eq(TEST_SPI_OUT),
+                        eq(""),
+                        isNull(),
+                        eq(0),
+                        eq(""),
+                        isNull(),
+                        eq(0),
+                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
+                        eq(CRYPT_KEY),
+                        anyInt(),
                         anyInt(),
                         anyInt(),
                         anyInt());
@@ -201,11 +275,14 @@
                         anyString(),
                         anyString(),
                         anyLong(),
-                        eq(DROID_SPI2),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
+                        eq(TEST_SPI_IN),
+                        eq(""),
+                        isNull(),
+                        eq(0),
+                        eq(""),
+                        isNull(),
+                        eq(0),
+                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
                         eq(CRYPT_KEY),
                         anyInt(),
                         anyInt(),
@@ -214,8 +291,68 @@
     }
 
     @Test
+    public void testCreateInvalidConfigAeadWithAuth() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+
+        for (int direction : DIRECTIONS) {
+            ipSecConfig.setAuthentication(direction, AUTH_ALGO);
+            ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
+        }
+
+        try {
+            mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+            fail(
+                    "IpSecService should have thrown an error on authentication being"
+                            + " enabled with authenticated encryption");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testCreateInvalidConfigAeadWithCrypt() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+
+        for (int direction : DIRECTIONS) {
+            ipSecConfig.setEncryption(direction, CRYPT_ALGO);
+            ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
+        }
+
+        try {
+            mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+            fail(
+                    "IpSecService should have thrown an error on encryption being"
+                            + " enabled with authenticated encryption");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testCreateInvalidConfigAeadWithAuthAndCrypt() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+
+        for (int direction : DIRECTIONS) {
+            ipSecConfig.setAuthentication(direction, AUTH_ALGO);
+            ipSecConfig.setEncryption(direction, CRYPT_ALGO);
+            ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
+        }
+
+        try {
+            mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+            fail(
+                    "IpSecService should have thrown an error on authentication and encryption being"
+                            + " enabled with authenticated encryption");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testDeleteTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = buildIpSecConfig();
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
@@ -227,19 +364,21 @@
                         eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         anyString(),
-                        eq(DROID_SPI));
+                        eq(TEST_SPI_OUT));
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
                         eq(createTransformResp.resourceId),
                         eq(IpSecTransform.DIRECTION_IN),
                         anyString(),
                         anyString(),
-                        eq(DROID_SPI2));
+                        eq(TEST_SPI_IN));
     }
 
     @Test
     public void testApplyTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = buildIpSecConfig();
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
@@ -255,7 +394,7 @@
                         eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         anyString(),
-                        eq(DROID_SPI));
+                        eq(TEST_SPI_OUT));
         verify(mMockNetd)
                 .ipSecApplyTransportModeTransform(
                         eq(pfd.getFileDescriptor()),
@@ -263,7 +402,7 @@
                         eq(IpSecTransform.DIRECTION_IN),
                         anyString(),
                         anyString(),
-                        eq(DROID_SPI2));
+                        eq(TEST_SPI_IN));
     }
 
     @Test