[devicepolicy] More complete test of CaCert API

Main improvement: checking whether a CA certificate is not only listed
by the system but also whether it's *trusted* by standard security
APIs like TrustManager.

Bug:17469404
Change-Id: If5be7451f6823f22c57db1b2b7b373784bf77d99
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
index 9127dab..51c575c 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
@@ -15,69 +15,143 @@
  */
 package com.android.cts.deviceowner;
 
-import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
-import static com.android.cts.deviceowner.FakeKeys.FAKE_DSA_1;
-
 import java.io.ByteArrayInputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
-import java.security.cert.Certificate;
+import java.util.Arrays;
 import java.util.List;
 
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import static com.android.cts.deviceowner.FakeKeys.FAKE_DSA_1;
+import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
+
 public class CaCertManagementTest extends BaseDeviceOwnerTest {
+    /**
+     * Test: device admins should be able to list all installed certs.
+     *
+     * <p>The list of certificates must never be {@code null}.
+     */
     public void testCanRetrieveListOfInstalledCaCerts() {
         List<byte[]> caCerts = mDevicePolicyManager.getInstalledCaCerts(getWho());
         assertNotNull(caCerts);
     }
 
+    /**
+     * Test: a valid cert should be installable and also removable.
+     */
     public void testCanInstallAndUninstallACaCert()
-    throws CertificateException {
-        assertFalse(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
-        assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+            throws CertificateException, GeneralSecurityException {
+        assertUninstalled(FAKE_RSA_1.caCertificate);
+        assertUninstalled(FAKE_DSA_1.caCertificate);
+
         assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
-        assertTrue(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
-        assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+        assertInstalled(FAKE_RSA_1.caCertificate);
+        assertUninstalled(FAKE_DSA_1.caCertificate);
+
         mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_RSA_1.caCertificate);
-        assertFalse(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
-        assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+        assertUninstalled(FAKE_RSA_1.caCertificate);
+        assertUninstalled(FAKE_DSA_1.caCertificate);
     }
 
-    public void testUninstallationIsSelective() throws CertificateException {
+    /**
+     * Test: removing one certificate must not remove any others.
+     */
+    public void testUninstallationIsSelective()
+            throws CertificateException, GeneralSecurityException {
         assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
         assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_DSA_1.caCertificate));
+
         mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_DSA_1.caCertificate);
-        assertTrue(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
-        assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+        assertInstalled(FAKE_RSA_1.caCertificate);
+        assertUninstalled(FAKE_DSA_1.caCertificate);
+
         mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_RSA_1.caCertificate);
     }
 
-    public void testCanUninstallAllUserCaCerts() throws CertificateException {
+    /**
+     * Test: uninstallAllUserCaCerts should be equivalent to calling uninstallCaCert on every
+     * supplementary installed certificate.
+     */
+    public void testCanUninstallAllUserCaCerts()
+            throws CertificateException, GeneralSecurityException {
         assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
         assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_DSA_1.caCertificate));
+
         mDevicePolicyManager.uninstallAllUserCaCerts(getWho());
-        assertFalse(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
-        assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+        assertUninstalled(FAKE_RSA_1.caCertificate);
+        assertUninstalled(FAKE_DSA_1.caCertificate);
     }
 
-    private boolean hasCaCertInstalled(byte [] caCert) throws CertificateException {
-        boolean result = mDevicePolicyManager.hasCaCertInstalled(getWho(), caCert);
-        assertEquals(result, containsCertificate(
-            mDevicePolicyManager.getInstalledCaCerts(getWho()), caCert));
-        return result;
+    private void assertInstalled(byte[] caBytes)
+            throws CertificateException, GeneralSecurityException {
+        Certificate caCert = readCertificate(caBytes);
+        assertTrue(isCaCertInstalledAndTrusted(caCert));
     }
 
-    private static boolean containsCertificate(List<byte[]> certificates, byte [] toMatch)
-            throws CertificateException {
-        Certificate certificateToMatch = readCertificate(toMatch);
-        for (byte[] certBuffer : certificates) {
-            Certificate cert = readCertificate(certBuffer);
-            if (certificateToMatch.equals(cert)) {
-                return true;
+    private void assertUninstalled(byte[] caBytes)
+            throws CertificateException, GeneralSecurityException {
+        Certificate caCert = readCertificate(caBytes);
+        assertFalse(isCaCertInstalledAndTrusted(caCert));
+    }
+
+    /**
+     * Whether a given cert, or one a lot like it, has been installed system-wide and is available
+     * to all apps.
+     *
+     * <p>A CA certificate is "installed" if it matches all of the following conditions:
+     * <ul>
+     *   <li>{@link DevicePolicyManager#hasCaCertInstalled} returns {@code true}.</li>
+     *   <li>{@link DevicePolicyManager#getInstalledCaCerts} lists a matching certificate (not
+     *       necessarily exactly the same) in its response.</li>
+     *   <li>Any new instances of {@link TrustManager} should report the certificate among their
+     *       accepted issuer list -- older instances may keep the set of issuers they were created
+     *       with until explicitly refreshed.</li>
+     *
+     * @return {@code true} if installed by all metrics, {@code false} if not installed by any
+     *         metric. In any other case an {@link AssertionError} will be thrown.
+     */
+    private boolean isCaCertInstalledAndTrusted(Certificate caCert)
+            throws GeneralSecurityException, CertificateException {
+        boolean installed = mDevicePolicyManager.hasCaCertInstalled(getWho(), caCert.getEncoded());
+
+        boolean listed = false;
+        for (byte[] certBuffer : mDevicePolicyManager.getInstalledCaCerts(getWho())) {
+            if (caCert.equals(readCertificate(certBuffer))) {
+                listed = true;
             }
         }
-        return false;
+
+        boolean trusted = false;
+        final TrustManagerFactory tmf =
+                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init((KeyStore) null);
+        for (TrustManager trustManager : tmf.getTrustManagers()) {
+             if (trustManager instanceof X509TrustManager) {
+                final X509TrustManager tm = (X509TrustManager) trustManager;
+                if (Arrays.asList(tm.getAcceptedIssuers()).contains(caCert)) {
+                    trusted = true;
+                }
+            }
+        }
+
+        // All three responses should match - if an installed certificate isn't trusted or (worse)
+        // a trusted certificate isn't even installed we should 
+        assertEquals(installed, listed);
+        assertEquals(installed, trusted);
+        return installed;
     }
 
+    /**
+     * Convert an encoded certificate back into a {@link Certificate}.
+     *
+     * Instantiates a fresh CertificateFactory every time for repeatability.
+     */
     private static Certificate readCertificate(byte[] certBuffer) throws CertificateException {
         final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
         return certFactory.generateCertificate(new ByteArrayInputStream(certBuffer));