Make server cert validation check public

Make server cert validation check public to allow Apps check before
build suggestions

Cherry pick from ag/13221115

Bug: 175629207
Test: atest android.net.wifi
Change-Id: If523264475e0c210c6054e1b65a9526d9ba08cc2
diff --git a/framework/api/current.txt b/framework/api/current.txt
index f8b95dd..130a124 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -206,6 +206,7 @@
     ctor public WifiEnterpriseConfig();
     ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
     method public int describeContents();
+    method public boolean doesEapMethodUseServerCert();
     method public String getAltSubjectMatch();
     method public String getAnonymousIdentity();
     method @Nullable public java.security.cert.X509Certificate getCaCertificate();
@@ -222,6 +223,7 @@
     method public String getRealm();
     method @Deprecated public String getSubjectMatch();
     method public boolean isAuthenticationSimBased();
+    method public boolean isServerCertValidationEnabled();
     method public void setAltSubjectMatch(String);
     method public void setAnonymousIdentity(String);
     method public void setCaCertificate(@Nullable java.security.cert.X509Certificate);
diff --git a/framework/java/android/net/wifi/WifiEnterpriseConfig.java b/framework/java/android/net/wifi/WifiEnterpriseConfig.java
index e127ea9..2a874d4 100644
--- a/framework/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/framework/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -25,6 +25,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
@@ -1436,34 +1438,71 @@
     }
 
     /**
-     * Method determines whether the Enterprise configuration is insecure. An insecure
-     * configuration is one where EAP method requires a CA certification, i.e. PEAP, TLS, or
-     * TTLS, and any of the following conditions are met:
-     * - Both certificate and CA path are not configured.
-     * - Both alternative subject match and domain suffix match are not set.
-     *
-     * Note: this method does not exhaustively check security of the configuration - i.e. a return
-     * value of {@code false} is not a guarantee that the configuration is secure.
+     * Determines whether an Enterprise configuration's EAP method requires a Root CA certification
+     * to validate the authentication server i.e. PEAP, TLS, or TTLS.
+     * @return True if configuration requires a CA certification, false otherwise.
+     */
+    public boolean doesEapMethodUseServerCert() {
+        if (!SdkLevel.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
+        return isTlsBasedEapMethod();
+    }
+
+    /**
+     * A helper method to check if the EAP method of enterprise config is one of the target types.
+     * @return true if EAP method of enterprise config is one the target types, false otherwise.
      * @hide
      */
-    public boolean isInsecure() {
-        if (mEapMethod != Eap.PEAP && mEapMethod != Eap.TLS && mEapMethod != Eap.TTLS) {
-            return false;
+    public boolean isTlsBasedEapMethod() {
+        return mEapMethod == Eap.PEAP || mEapMethod == Eap.TLS || mEapMethod == Eap.TTLS;
+    }
+    /**
+     * Determines whether an Enterprise configuration enables server certificate validation.
+     * <p>
+     * The caller can determine, along with {@link #doesEapMethodUseServerCert()}, if an
+     * Enterprise configuration enables server certificate validation, which is a mandatory
+     * requirement for networks that use TLS based EAP methods. A configuration that does not
+     * enable server certificate validation will be ignored and will not be considered for
+     * network selection. A network suggestion with such a configuration will cause an
+     * IllegalArgumentException to be thrown when suggested.
+     * Server validation is achieved by the following:
+     * - Either certificate or CA path is configured.
+     * - Either alternative subject match or domain suffix match is set.
+     * @return True for server certificate validation is enabled, false otherwise.
+     * @throws IllegalStateException on configuration which doesn't use server certificate.
+     * @see #doesEapMethodUseServerCert()
+     */
+    public boolean isServerCertValidationEnabled() {
+        if (!SdkLevel.isAtLeastS()) {
+            throw new UnsupportedOperationException();
         }
+        if (!doesEapMethodUseServerCert()) {
+            throw new IllegalStateException("Configuration doesn't use server certificates for "
+                    + "authentication");
+        }
+        return isMandatoryParameterSetForServerCertValidation();
+    }
+
+    /**
+     * Helper method to check if mandatory parameter for server cert validation is set.
+     * @hide
+     */
+    public boolean isMandatoryParameterSetForServerCertValidation() {
         if (TextUtils.isEmpty(getAltSubjectMatch())
                 && TextUtils.isEmpty(getDomainSuffixMatch())) {
-            // Both subject and domain match are not set, it's insecure.
-            return true;
+            // Both subject and domain match are not set, validation is not enabled.
+            return false;
         }
         if (mIsAppInstalledCaCert) {
-            // CA certificate is installed by App, it's secure.
-            return false;
+            // CA certificate is installed by App, validation is enabled.
+            return true;
         }
         if (getCaCertificateAliases() != null) {
-            // CA certificate alias from keyStore is set, it's secure.
-            return false;
+            // CA certificate alias from keyStore is set, validation is enabled.
+            return true;
         }
-        return TextUtils.isEmpty(getCaPath());
+        return !TextUtils.isEmpty(getCaPath());
     }
 
     /**
diff --git a/framework/java/android/net/wifi/WifiNetworkSuggestion.java b/framework/java/android/net/wifi/WifiNetworkSuggestion.java
index 8e124a2..ab0a14f 100644
--- a/framework/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/framework/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -319,14 +319,16 @@
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
-         * @throws IllegalArgumentException if configuration CA certificate or
-         *                                  AltSubjectMatch/DomainSuffixMatch is not set.
+         * @throws IllegalArgumentException If configuration uses server certificate but validation
+         *                                  is not enabled. See {@link WifiEnterpriseConfig#isServerCertValidationEnabled()}
          */
         public @NonNull Builder setWpa2EnterpriseConfig(
                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
             checkNotNull(enterpriseConfig);
-            if (enterpriseConfig.isInsecure()) {
-                throw new IllegalArgumentException("Enterprise configuration is insecure");
+            if (enterpriseConfig.isTlsBasedEapMethod()
+                    && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
+                throw new IllegalArgumentException("Enterprise configuration mandates server "
+                        + "certificate but validation is not enabled.");
             }
             mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
             return this;
@@ -346,15 +348,17 @@
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
-         * @throws IllegalArgumentException if configuration CA certificate or
-         *                                  AltSubjectMatch/DomainSuffixMatch is not set.
+         * @throws IllegalArgumentException If configuration uses server certificate but validation
+         *                                  is not enabled. See {@link WifiEnterpriseConfig#isServerCertValidationEnabled()}
          */
         @Deprecated
         public @NonNull Builder setWpa3EnterpriseConfig(
                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
             checkNotNull(enterpriseConfig);
-            if (enterpriseConfig.isInsecure()) {
-                throw new IllegalArgumentException("Enterprise configuration is insecure");
+            if (enterpriseConfig.isTlsBasedEapMethod()
+                    && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
+                throw new IllegalArgumentException("Enterprise configuration mandates server "
+                        + "certificate but validation is not enabled.");
             }
             mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
             return this;
@@ -368,14 +372,16 @@
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
-         * @throws IllegalArgumentException if configuration CA certificate or
-         *                                  AltSubjectMatch/DomainSuffixMatch is not set.
+         * @throws IllegalArgumentException If configuration uses server certificate but validation
+         *                                  is not enabled. See {@link WifiEnterpriseConfig#isServerCertValidationEnabled()}
          */
         public @NonNull Builder setWpa3EnterpriseStandardModeConfig(
                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
             checkNotNull(enterpriseConfig);
-            if (enterpriseConfig.isInsecure()) {
-                throw new IllegalArgumentException("Enterprise configuration is insecure");
+            if (enterpriseConfig.isTlsBasedEapMethod()
+                    && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
+                throw new IllegalArgumentException("Enterprise configuration mandates server "
+                        + "certificate but validation is not enabled.");
             }
             mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
             mWpa3EnterpriseType = WPA3_ENTERPRISE_STANDARD;
diff --git a/framework/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/framework/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index 62485ec..3197edc 100644
--- a/framework/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/framework/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -48,6 +48,7 @@
     public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE;
     public static final String KEYSTORES_URI = "keystores://";
     private static final String TEST_DOMAIN_SUFFIX_MATCH = "domainSuffixMatch";
+    private static final String TEST_ALT_SUBJECT_MATCH = "DNS:server.test.com";
 
     private WifiEnterpriseConfig mEnterpriseConfig;
 
@@ -543,35 +544,78 @@
     }
 
     @Test
-    public void testIsEnterpriseConfigSecure() {
+    public void testIsEnterpriseConfigServerCertNotEnabled() {
         WifiEnterpriseConfig baseConfig = new WifiEnterpriseConfig();
         baseConfig.setEapMethod(Eap.PEAP);
         baseConfig.setPhase2Method(Phase2.MSCHAPV2);
-        assertTrue(baseConfig.isInsecure());
+        assertTrue(baseConfig.doesEapMethodUseServerCert());
+        assertFalse(baseConfig.isServerCertValidationEnabled());
 
         WifiEnterpriseConfig noMatchConfig = new WifiEnterpriseConfig(baseConfig);
         noMatchConfig.setCaCertificate(FakeKeys.CA_CERT0);
-        // Missing match is insecure.
-        assertTrue(noMatchConfig.isInsecure());
+        // Missing match disables validation.
+        assertTrue(baseConfig.doesEapMethodUseServerCert());
+        assertFalse(baseConfig.isServerCertValidationEnabled());
 
         WifiEnterpriseConfig noCaConfig = new WifiEnterpriseConfig(baseConfig);
         noCaConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
-        // Missing CA certificate is insecure.
-        assertTrue(noCaConfig.isInsecure());
+        // Missing CA certificate disables validation.
+        assertTrue(baseConfig.doesEapMethodUseServerCert());
+        assertFalse(baseConfig.isServerCertValidationEnabled());
 
-        WifiEnterpriseConfig secureConfig = new WifiEnterpriseConfig();
-        secureConfig.setEapMethod(Eap.PEAP);
-        secureConfig.setPhase2Method(Phase2.MSCHAPV2);
-        secureConfig.setCaCertificate(FakeKeys.CA_CERT0);
-        secureConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
-        assertFalse(secureConfig.isInsecure());
-
-        WifiEnterpriseConfig secureConfigWithCaAlias = new WifiEnterpriseConfig();
-        secureConfigWithCaAlias.setEapMethod(Eap.PEAP);
-        secureConfigWithCaAlias.setPhase2Method(Phase2.MSCHAPV2);
-        secureConfigWithCaAlias.setCaCertificateAliases(new String[]{"alias1", "alisa2"});
-        secureConfigWithCaAlias.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
-        assertFalse(secureConfigWithCaAlias.isInsecure());
+        WifiEnterpriseConfig noValidationConfig = new WifiEnterpriseConfig();
+        noValidationConfig.setEapMethod(Eap.AKA);
+        assertFalse(noValidationConfig.doesEapMethodUseServerCert());
     }
 
+    @Test
+    public void testIsEnterpriseConfigServerCertEnabledWithPeap() {
+        testIsEnterpriseConfigServerCertEnabled(Eap.PEAP);
+    }
+
+    @Test
+    public void testIsEnterpriseConfigServerCertEnabledWithTls() {
+        testIsEnterpriseConfigServerCertEnabled(Eap.TLS);
+    }
+
+    @Test
+    public void testIsEnterpriseConfigServerCertEnabledWithTTLS() {
+        testIsEnterpriseConfigServerCertEnabled(Eap.TTLS);
+    }
+
+    private void testIsEnterpriseConfigServerCertEnabled(int eapMethod) {
+        WifiEnterpriseConfig configWithCertAndDomainSuffixMatch = createEnterpriseConfig(eapMethod,
+                Phase2.NONE, FakeKeys.CA_CERT0, null, TEST_DOMAIN_SUFFIX_MATCH, null);
+        assertTrue(configWithCertAndDomainSuffixMatch.doesEapMethodUseServerCert());
+        assertTrue(configWithCertAndDomainSuffixMatch.isServerCertValidationEnabled());
+
+        WifiEnterpriseConfig configWithCertAndAltSubjectMatch = createEnterpriseConfig(eapMethod,
+                Phase2.NONE, FakeKeys.CA_CERT0, null, null, TEST_ALT_SUBJECT_MATCH);
+        assertTrue(configWithCertAndAltSubjectMatch.doesEapMethodUseServerCert());
+        assertTrue(configWithCertAndAltSubjectMatch.isServerCertValidationEnabled());
+
+        WifiEnterpriseConfig configWithAliasAndDomainSuffixMatch = createEnterpriseConfig(eapMethod,
+                Phase2.NONE, null, new String[]{"alias1", "alisa2"}, TEST_DOMAIN_SUFFIX_MATCH,
+                null);
+        assertTrue(configWithAliasAndDomainSuffixMatch.doesEapMethodUseServerCert());
+        assertTrue(configWithAliasAndDomainSuffixMatch.isServerCertValidationEnabled());
+
+        WifiEnterpriseConfig configWithAliasAndAltSubjectMatch = createEnterpriseConfig(eapMethod,
+                Phase2.NONE, null, new String[]{"alias1", "alisa2"}, null, TEST_ALT_SUBJECT_MATCH);
+        assertTrue(configWithAliasAndAltSubjectMatch.doesEapMethodUseServerCert());
+        assertTrue(configWithAliasAndAltSubjectMatch.isServerCertValidationEnabled());
+    }
+
+    private WifiEnterpriseConfig createEnterpriseConfig(int eapMethod, int phase2Method,
+            X509Certificate caCertificate, String[] aliases, String domainSuffixMatch,
+            String altSubjectMatch) {
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+        config.setEapMethod(eapMethod);
+        config.setPhase2Method(phase2Method);
+        config.setCaCertificate(caCertificate);
+        config.setCaCertificateAliases(aliases);
+        config.setDomainSuffixMatch(domainSuffixMatch);
+        config.setAltSubjectMatch(altSubjectMatch);
+        return config;
+    }
 }
diff --git a/service/java/com/android/server/wifi/NetworkSuggestionNominator.java b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java
index 2f6ee23..a40a0ae 100644
--- a/service/java/com/android/server/wifi/NetworkSuggestionNominator.java
+++ b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java
@@ -236,7 +236,9 @@
             boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed,
             boolean oemPrivateNetworkAllowed) {
         // Ignore insecure enterprise config.
-        if (config.isEnterprise() && config.enterpriseConfig.isInsecure()) {
+        if (config.isEnterprise() && config.enterpriseConfig.isTlsBasedEapMethod()
+                && !config.enterpriseConfig
+                .isMandatoryParameterSetForServerCertValidation()) {
             mLocalLog.log("Ignoring insecure enterprise network: " + config);
             return false;
         }
diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
index a27803b4..646b119 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
@@ -1045,12 +1045,14 @@
                 return false;
             }
             if (wns.passpointConfiguration == null) {
-                if (!WifiConfigurationUtil.validate(wns.wifiConfiguration,
+                WifiConfiguration config = wns.wifiConfiguration;
+                if (!WifiConfigurationUtil.validate(config,
                         WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
                     return false;
                 }
-                if (wns.wifiConfiguration.isEnterprise()
-                        && wns.wifiConfiguration.enterpriseConfig.isInsecure()) {
+                if (config.isEnterprise() && config.enterpriseConfig.isTlsBasedEapMethod()
+                        && !config.enterpriseConfig
+                        .isMandatoryParameterSetForServerCertValidation()) {
                     Log.e(TAG, "Insecure enterprise suggestion is invalid.");
                     return false;
                 }
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index fd855b2..75c087b 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -2662,7 +2662,8 @@
         }
 
         if (config.isEnterprise()) {
-            if (config.enterpriseConfig.isInsecure()) {
+            if (config.enterpriseConfig.isTlsBasedEapMethod()
+                    && !config.enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
                 Log.e(TAG, "Enterprise network configuration is missing either a Root CA "
                         + "or a domain name");
                 return -1;