Merge "[WPA3-Enterprise] Improve the security and robustness of profiles" into rvc-qpr-dev
diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java
index 70daa0d..c248d22 100644
--- a/service/java/com/android/server/wifi/WifiKeyStore.java
+++ b/service/java/com/android/server/wifi/WifiKeyStore.java
@@ -30,6 +30,7 @@
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
+import java.security.Principal;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -276,52 +277,110 @@
         // For WPA3-Enterprise 192-bit networks, set the SuiteBCipher field based on the
         // CA certificate type. Suite-B requires SHA384, reject other certs.
         if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) {
-            // Read the first CA certificate, and initialize
-            Certificate caCert = null;
-            try {
-                caCert = mKeyStore.getCertificate(config.enterpriseConfig.getCaCertificateAlias());
-            } catch (KeyStoreException e) {
-                Log.e(TAG, "Failed to get Suite-B certificate", e);
-            }
-            if (caCert == null || !(caCert instanceof X509Certificate)) {
-                Log.e(TAG, "Failed reading CA certificate for Suite-B");
+            // Read the CA certificates, and initialize
+            String[] caAliases = config.enterpriseConfig.getCaCertificateAliases();
+
+            if (caAliases == null || caAliases.length == 0) {
+                Log.e(TAG, "No CA aliases in profile");
                 return false;
             }
-            X509Certificate x509CaCert = (X509Certificate) caCert;
-            String sigAlgOid = x509CaCert.getSigAlgOID();
-            if (mVerboseLoggingEnabled) {
-                Log.d(TAG, "Signature algorithm: " + sigAlgOid);
-            }
-            config.allowedSuiteBCiphers.clear();
 
-            // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates
-            // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192
-            // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term
-            // Suite-B was already coined in the IEEE 802.11-2016 specification for
-            // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates
-            // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally
-            // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments,
-            // we are supporting both types here.
-            if (sigAlgOid.equals("1.2.840.113549.1.1.12")) {
-                // sha384WithRSAEncryption
-                config.allowedSuiteBCiphers.set(
-                        WifiConfiguration.SuiteBCipher.ECDHE_RSA);
-                if (mVerboseLoggingEnabled) {
-                    Log.d(TAG, "Selecting Suite-B RSA");
+            int caCertType = -1;
+            int prevCaCertType = -1;
+            for (String caAlias : caAliases) {
+                Certificate caCert = null;
+                try {
+                    caCert = mKeyStore.getCertificate(caAlias);
+                } catch (KeyStoreException e) {
+                    Log.e(TAG, "Failed to get Suite-B certificate", e);
                 }
-            } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) {
-                // ecdsa-with-SHA384
-                config.allowedSuiteBCiphers.set(
-                        WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
-                if (mVerboseLoggingEnabled) {
-                    Log.d(TAG, "Selecting Suite-B ECDSA");
+                if (caCert == null || !(caCert instanceof X509Certificate)) {
+                    Log.e(TAG, "Failed reading CA certificate for Suite-B");
+                    return false;
                 }
+
+                // Confirm that the CA certificate is compatible with Suite-B requirements
+                caCertType = getSuiteBCipherFromCert((X509Certificate) caCert);
+                if (caCertType < 0) {
+                    return false;
+                }
+                if (prevCaCertType != -1) {
+                    if (prevCaCertType != caCertType) {
+                        Log.e(TAG, "Incompatible CA certificates");
+                        return false;
+                    }
+                }
+                prevCaCertType = caCertType;
+            }
+
+            Certificate clientCert = null;
+            try {
+                clientCert = mKeyStore.getCertificate(config.enterpriseConfig
+                        .getClientCertificateAlias());
+            } catch (KeyStoreException e) {
+                Log.e(TAG, "Failed to get Suite-B client certificate", e);
+            }
+            if (clientCert == null || !(clientCert instanceof X509Certificate)) {
+                Log.e(TAG, "Failed reading client certificate for Suite-B");
+                return false;
+            }
+
+            int clientCertType = getSuiteBCipherFromCert((X509Certificate) clientCert);
+            if (clientCertType < 0) {
+                return false;
+            }
+
+            if (clientCertType == caCertType) {
+                config.allowedSuiteBCiphers.clear();
+                config.allowedSuiteBCiphers.set(clientCertType);
             } else {
-                Log.e(TAG, "Invalid CA certificate type for Suite-B: "
-                        + sigAlgOid);
+                Log.e(TAG, "Client certificate for Suite-B is incompatible with the CA "
+                        + "certificate");
                 return false;
             }
         }
         return true;
     }
+
+    /**
+     * Get the Suite-B cipher from the certificate
+     *
+     * @param x509Certificate Certificate to process
+     * @return WifiConfiguration.SuiteBCipher.ECDHE_RSA if the certificate OID matches the Suite-B
+     * requirements for RSA certificates, WifiConfiguration.SuiteBCipher.ECDHE_ECDSA if the
+     * certificate OID matches the Suite-B requirements for ECDSA certificates, or -1 otherwise.
+     */
+    private int getSuiteBCipherFromCert(X509Certificate x509Certificate) {
+        String sigAlgOid = x509Certificate.getSigAlgOID();
+        if (mVerboseLoggingEnabled) {
+            Principal p = x509Certificate.getSubjectX500Principal();
+            if (p != null && !TextUtils.isEmpty(p.getName())) {
+                Log.d(TAG, "Checking cert " + p.getName());
+            }
+        }
+
+        // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates
+        // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192
+        // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term
+        // Suite-B was already coined in the IEEE 802.11-2016 specification for
+        // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates
+        // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally
+        // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments,
+        // we are supporting both types here.
+        if (sigAlgOid.equals("1.2.840.113549.1.1.12")) {
+            // sha384WithRSAEncryption
+            if (mVerboseLoggingEnabled) {
+                Log.d(TAG, "Found Suite-B RSA certificate");
+            }
+            return WifiConfiguration.SuiteBCipher.ECDHE_RSA;
+        } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) {
+            // ecdsa-with-SHA384
+            if (mVerboseLoggingEnabled) {
+                Log.d(TAG, "Found Suite-B ECDSA certificate");
+            }
+            return WifiConfiguration.SuiteBCipher.ECDHE_ECDSA;
+        }
+        Log.e(TAG, "Invalid certificate type for Suite-B: " + sigAlgOid);
+        return -1;
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java
index 8eef7e7..f6cae66 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wifi;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalMatchers.aryEq;
@@ -223,6 +224,8 @@
      */
     @Test
     public void testConfigureSuiteBRsa3072() throws Exception {
+        when(mWifiEnterpriseConfig.getCaCertificateAliases())
+                .thenReturn(new String[]{USER_CA_CERT_ALIAS});
         when(mWifiEnterpriseConfig.getClientPrivateKey())
                 .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY);
         when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn(
@@ -249,6 +252,8 @@
      */
     @Test
     public void testConfigureSuiteBEcdsa() throws Exception {
+        when(mWifiEnterpriseConfig.getCaCertificateAliases())
+                .thenReturn(new String[]{USER_CA_CERT_ALIAS});
         when(mWifiEnterpriseConfig.getClientPrivateKey())
                 .thenReturn(FakeKeys.CLIENT_SUITE_B_ECC_KEY);
         when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn(
@@ -269,4 +274,119 @@
         assertTrue(
                 savedNetwork.allowedSuiteBCiphers.get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
     }
+
+    /**
+     * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when CA and client
+     * certificates are not of the same type.
+     */
+    @Test
+    public void testConfigurationFailureSuiteB() throws Exception {
+        // Create a configuration with RSA client cert and ECDSA CA cert
+        when(mWifiEnterpriseConfig.getClientPrivateKey())
+                .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY);
+        when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+        when(mWifiEnterpriseConfig.getClientCertificateChain())
+                .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+        when(mWifiEnterpriseConfig.getCaCertificates())
+                .thenReturn(new X509Certificate[]{FakeKeys.CA_SUITE_B_ECDSA_CERT});
+        when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn(
+                FakeKeys.CA_SUITE_B_ECDSA_CERT);
+        WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork(
+                WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+        savedNetwork.enterpriseConfig = mWifiEnterpriseConfig;
+        assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null));
+    }
+
+    /**
+     * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when CA is RSA but not
+     * with the required security
+     */
+    @Test
+    public void testConfigurationFailureSuiteBNon3072Rsa() throws Exception {
+        // Create a configuration with RSA client cert and weak RSA CA cert
+        when(mWifiEnterpriseConfig.getClientPrivateKey())
+                .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY);
+        when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_CERT0);
+        when(mWifiEnterpriseConfig.getClientCertificateChain())
+                .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+        when(mWifiEnterpriseConfig.getCaCertificates())
+                .thenReturn(new X509Certificate[]{FakeKeys.CA_CERT0});
+        when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn(
+                FakeKeys.CA_CERT0);
+        WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork(
+                WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+        savedNetwork.enterpriseConfig = mWifiEnterpriseConfig;
+        assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null));
+    }
+
+    /**
+     * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when one CA in the list
+     * is RSA but not with the required security
+     */
+    @Test
+    public void testConfigurationFailureSuiteBNon3072RsaInList() throws Exception {
+        // Create a configuration with RSA client cert and weak RSA CA cert
+        when(mWifiEnterpriseConfig.getClientPrivateKey())
+                .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY);
+        when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+        when(mWifiEnterpriseConfig.getClientCertificateChain())
+                .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+        when(mWifiEnterpriseConfig.getCaCertificates())
+                .thenReturn(
+                        new X509Certificate[]{FakeKeys.CA_SUITE_B_RSA3072_CERT, FakeKeys.CA_CERT0});
+        when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn(
+                FakeKeys.CA_SUITE_B_RSA3072_CERT);
+        when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[1]))).thenReturn(
+                FakeKeys.CA_CERT0);
+        when(mWifiEnterpriseConfig.getCaCertificateAliases())
+                .thenReturn(USER_CA_CERT_ALIASES);
+        WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork(
+                WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+        savedNetwork.enterpriseConfig = mWifiEnterpriseConfig;
+        assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null));
+    }
+
+    /**
+     * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when one CA in the list
+     * is RSA and the other is ECDSA
+     */
+    @Test
+    public void testConfigurationFailureSuiteBRsaAndEcdsaInList() throws Exception {
+        // Create a configuration with RSA client cert and weak RSA CA cert
+        when(mWifiEnterpriseConfig.getClientPrivateKey())
+                .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY);
+        when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+        when(mWifiEnterpriseConfig.getClientCertificateChain())
+                .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+        when(mWifiEnterpriseConfig.getCaCertificates())
+                .thenReturn(
+                        new X509Certificate[]{FakeKeys.CA_SUITE_B_RSA3072_CERT,
+                                FakeKeys.CA_SUITE_B_ECDSA_CERT});
+        when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn(
+                FakeKeys.CLIENT_SUITE_B_RSA3072_CERT);
+        when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn(
+                FakeKeys.CA_SUITE_B_RSA3072_CERT);
+        when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[1]))).thenReturn(
+                FakeKeys.CA_SUITE_B_ECDSA_CERT);
+        when(mWifiEnterpriseConfig.getCaCertificateAliases())
+                .thenReturn(USER_CA_CERT_ALIASES);
+        WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork(
+                WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+        savedNetwork.enterpriseConfig = mWifiEnterpriseConfig;
+        assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null));
+    }
 }