Add new mandatory IPsec algorithms

This CL adds new mandatory IPsec algorithms and allows
OEM to enable them on old devices using resource overlay

Bug: 161716062
Test: FrameworksNetTests:IpSecAlgorithmTest
Change-Id: Ib827b05ea27dbe61b14ad236e858f825293ed994
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 8e9d08c..2e1c29a 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -16,34 +16,50 @@
 
 package android.net;
 
+import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
+import android.content.res.Resources;
+import android.os.Build;
 import android.os.Parcel;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.CollectionUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Map.Entry;
 import java.util.Random;
+import java.util.Set;
 
 /** Unit tests for {@link IpSecAlgorithm}. */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class IpSecAlgorithmTest {
-
     private static final byte[] KEY_MATERIAL;
 
+    private final Resources mMockResources = mock(Resources.class);
+
     static {
         KEY_MATERIAL = new byte[128];
         new Random().nextBytes(KEY_MATERIAL);
     };
 
+    private static byte[] generateKey(int keyLenInBits) {
+        return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8);
+    }
+
     @Test
     public void testNoTruncLen() throws Exception {
         Entry<String, Integer>[] authAndAeadList =
@@ -53,7 +69,7 @@
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
-                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224),
                 };
 
         // Expect auth and aead algorithms to throw errors if trunclen is omitted.
@@ -70,6 +86,52 @@
         new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
     }
 
+    private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen)
+            throws Exception {
+        new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen);
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen));
+            fail("Expected exception on unprovided auth trunclen");
+        } catch (IllegalArgumentException pass) {
+        }
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen);
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1);
+            fail("Invalid truncation length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception {
+        new IpSecAlgorithm(algoName, generateKey(keyLen));
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen + 8));
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    @Test
+    public void testValidationForAlgosAddedInS() throws Exception {
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.R) {
+            return;
+        }
+
+        for (int len : new int[] {160, 224, 288}) {
+            checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
+        }
+        checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96);
+        checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128);
+    }
+
     @Test
     public void testTruncLenValidation() throws Exception {
         for (int truncLen : new int[] {256, 512}) {
@@ -127,4 +189,37 @@
         assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin));
         p.recycle();
     }
+
+    private static Set<String> getMandatoryAlgos() {
+        return CollectionUtils.filter(
+                ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+                i -> Build.VERSION.FIRST_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+    }
+
+    private static Set<String> getOptionalAlgos() {
+        return CollectionUtils.filter(
+                ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+                i -> Build.VERSION.FIRST_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+    }
+
+    @Test
+    public void testGetSupportedAlgorithms() throws Exception {
+        assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos()));
+        assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll(
+                IpSecAlgorithm.getSupportedAlgorithms()));
+    }
+
+    @Test
+    public void testLoadAlgos() throws Exception {
+        final Set<String> optionalAlgoSet = getOptionalAlgos();
+        final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]);
+
+        doReturn(optionalAlgos).when(mMockResources)
+                .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms);
+
+        final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources));
+        final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet();
+
+        assertEquals(expectedAlgos, enabledAlgos);
+    }
 }