1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.deviceandprofileowner;
18 
19 import android.app.KeyguardManager;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Build;
27 import android.security.KeyChainException;
28 import android.test.MoreAsserts;
29 
30 import java.util.concurrent.Semaphore;
31 import java.util.concurrent.TimeUnit;
32 
33 /**
34  * Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app
35  * (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various
36  * cert-related APIs. The expected certificate changes are validated both remotely and locally.
37  */
38 public class DelegatedCertInstallerTest extends BaseDeviceAdminTest {
39 
40     private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller";
41     private static final String NOT_EXIST_CERT_INSTALLER_PACKAGE
42             = "com.android.cts.certinstaller.not_exist";
43 
44     private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert";
45     private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert";
46     private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert";
47     private static final String ACTION_INSTALL_KEYPAIR =
48             "com.android.cts.certinstaller.install_keypair";
49     private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
50 
51     private static final String EXTRA_CERT_DATA = "extra_cert_data";
52     private static final String EXTRA_KEY_DATA = "extra_key_data";
53     private static final String EXTRA_KEY_ALIAS = "extra_key_alias";
54     private static final String EXTRA_RESULT_VALUE = "extra_result_value";
55     private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception";
56     // package name of receiver has to be specified explicitly as the receiver is registered in
57     // manifest
58     private static final ComponentName CERT_INSTALLER_COMPONENT = new ComponentName(
59             CERT_INSTALLER_PACKAGE, "com.android.cts.certinstaller.CertInstallerReceiver");
60 
61     /*
62      * The CA and keypair below are generated with:
63      *
64      * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
65      * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
66      * mkdir -p demoCA/newcerts
67      * touch demoCA/index.txt
68      * echo "01" > demoCA/serial
69      * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
70      */
71 
72      // Content from cacert.pem
73     private static final String TEST_CA =
74             "-----BEGIN CERTIFICATE-----\n" +
75             "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
76             "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
77             "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
78             "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
79             "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
80             "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
81             "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
82             "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
83             "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
84             "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
85             "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
86             "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
87             "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
88             "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
89             "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
90             "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
91             "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
92             "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
93             "wQ==\n" +
94             "-----END CERTIFICATE-----";
95     // Content from userkey.pem without the private key header and footer.
96     private static final String TEST_KEY =
97             "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCYprGsTU+5L3KM\n" +
98             "fhkm0gXM2xjGUH+543YLiMPGVr3eVS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqU\n" +
99             "mhegxG4s3IvGYVB0KZoRIjDKmnnvlx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T\n" +
100             "9sV6zW2Wzri+f5mvzKjhnArbLktHAgMBAAECgYBlfVVPhtZnmuXJzzQpAEZzTugb\n" +
101             "tN1OimZO0RIocTQoqj4KT+HkiJOLGFQPwbtFpMre+q4SRqNpM/oZnI1yRtKcCmIc\n" +
102             "mZgkwJ2k6pdSxqO0ofxFFTdT9czJ3rCnqBHy1g6BqUQFXT4olcygkxUpKYUwzlz1\n" +
103             "oAl487CoPxyr4sVEAQJBANwiUOHcdGd2RoRILDzw5WOXWBoWPOKzX/K9wt0yL+mO\n" +
104             "wlFNFSymqo9eLheHcEq/VD9qK9rT700dCewJfWj6+bECQQDNXmWNYIxGii5NJilT\n" +
105             "OBOHiMD/F0NE178j+/kmacbhDJwpkbLYXaP8rW4+Iswrm4ORJ59lvjNuXaZ28+sx\n" +
106             "fFp3AkA6Z7Bl/IO135+eATgbgx6ZadIqObQ1wbm3Qbmtzl7/7KyJvZXcnuup1icM\n" +
107             "fxa//jtwB89S4+Ad6ZJ0WaA4dj5BAkEAuG7V9KmIULE388EZy8rIfyepa22Q0/qN\n" +
108             "hdt8XasRGHsio5Jdc0JlSz7ViqflhCQde/aBh/XQaoVgQeO8jKyI8QJBAJHekZDj\n" +
109             "WA0w1RsBVVReN1dVXgjm1CykeAT8Qx8TUmBUfiDX6w6+eGQjKtS7f4KC2IdRTV6+\n" +
110             "bDzDoHBChHNC9ms=\n";
111 
112     // Content from usercert.pem without the header and footer.
113     private static final String TEST_CERT =
114             "MIIDEjCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET\n" +
115             "MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ\n" +
116             "dHkgTHRkMB4XDTE1MDUwMTE2NTQwNVoXDTI1MDQyODE2NTQwNVowWzELMAkGA1UE\n" +
117             "BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp\n" +
118             "ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY2xpZW50IGNlcnQwgZ8wDQYJKoZIhvcN\n" +
119             "AQEBBQADgY0AMIGJAoGBALCYprGsTU+5L3KMfhkm0gXM2xjGUH+543YLiMPGVr3e\n" +
120             "VS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqUmhegxG4s3IvGYVB0KZoRIjDKmnnv\n" +
121             "lx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T9sV6zW2Wzri+f5mvzKjhnArbLktH\n" +
122             "AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu\n" +
123             "ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQ8GL+jKSarvTn9fVNA2AzjY7qq\n" +
124             "gjAfBgNVHSMEGDAWgBRzBBA5sNWyT/fK8GrhN3tOqO5tgjANBgkqhkiG9w0BAQsF\n" +
125             "AAOCAQEAgwQEd2bktIDZZi/UOwU1jJUgGq7NiuBDPHcqgzjxhGFLQ8SQAAP3v3PR\n" +
126             "mLzcfxsxnzGynqN5iHQT4rYXxxaqrp1iIdj9xl9Wl5FxjZgXITxhlRscOd/UOBvG\n" +
127             "oMrazVczjjdoRIFFnjtU3Jf0Mich68HD1Z0S3o7X6sDYh6FTVR5KbLcxbk6RcoG4\n" +
128             "VCI5boR5LUXgb5Ed5UxczxvN12S71fyxHYVpuuI0z0HTIbAxKeRw43I6HWOmR1/0\n" +
129             "G6byGCNL/1Fz7Y+264fGqABSNTKdZwIU2K4ANEH7F+9scnhoO6OBp+gjBe5O+7jb\n" +
130             "wZmUCAoTka4hmoaOCj7cqt/IkmxozQ==\n";
131 
132     private DevicePolicyManager mDpm;
133     private volatile boolean mReceivedResult;
134     private volatile Exception mReceivedException;
135     private Semaphore mAvailableResultSemaphore;
136 
137     private final BroadcastReceiver receiver = new BroadcastReceiver() {
138         @Override
139         public void onReceive(Context context, Intent intent) {
140             if (ACTION_CERT_OPERATION_DONE.equals(intent.getAction())) {
141                 synchronized (DelegatedCertInstallerTest.this) {
142                     mReceivedResult = intent.getBooleanExtra(EXTRA_RESULT_VALUE, false);
143                     mReceivedException =
144                             (Exception) intent.getSerializableExtra(EXTRA_RESULT_EXCEPTION);
145                     mAvailableResultSemaphore.release();
146                 }
147             }
148         }
149     };
150 
151     @Override
setUp()152     public void setUp() throws Exception {
153         super.setUp();
154         mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
155         mAvailableResultSemaphore = new Semaphore(0);
156         mReceivedResult = false;
157         mReceivedException = null;
158         IntentFilter filter = new IntentFilter();
159         filter.addAction(ACTION_CERT_OPERATION_DONE);
160         mContext.registerReceiver(receiver, filter);
161     }
162 
163     @Override
tearDown()164     public void tearDown() throws Exception {
165         mContext.unregisterReceiver(receiver);
166         mDpm.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
167         // Installed private key pair will be removed once the lockscreen password is cleared,
168         // which is done in the hostside test.
169         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
170         super.tearDown();
171     }
172 
testCaCertsOperations()173     public void testCaCertsOperations() throws InterruptedException {
174         byte[] cert = TEST_CA.getBytes();
175 
176         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
177         assertEquals(CERT_INSTALLER_PACKAGE,
178                 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
179 
180         // Exercise installCaCert()
181         installCaCert(cert);
182         assertResult("installCaCert", true);
183         assertTrue("Certificate is not installed properly", mDpm.hasCaCertInstalled(
184                 ADMIN_RECEIVER_COMPONENT, cert));
185 
186         // Exercise getInstalledCaCerts()
187         verifyCaCert(cert);
188         assertResult("getInstalledCaCerts()", true);
189 
190         // Exercise uninstallCaCert()
191         removeCaCert(cert);
192         assertResult("uninstallCaCert()", true);
193         assertFalse("Certificate is not removed properly", mDpm.hasCaCertInstalled(
194                 ADMIN_RECEIVER_COMPONENT, cert));
195 
196         // Clear delegated cert installer.
197         // Tests after this are expected to fail.
198         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
199 
200         installCaCert(cert);
201         assertResult("installCaCert", false);
202     }
203 
testInstallKeyPair()204     public void testInstallKeyPair() throws InterruptedException, KeyChainException {
205         final String alias = "delegated-cert-installer-test-key";
206 
207         // Clear delegated cert installer.
208         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
209         // The app is not the cert installer , it shouldn't have have privilege to call
210         // installKeyPair().
211         installKeyPair(TEST_KEY, TEST_CERT, alias);
212         assertResult("installKeyPair", false);
213 
214         // Set the app to be cert installer.
215         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
216         assertEquals(CERT_INSTALLER_PACKAGE,
217                 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
218 
219         // Exercise installKeyPair()
220         checkKeyguardPrecondition();
221         installKeyPair(TEST_KEY, TEST_CERT, alias);
222         assertResult("installKeyPair", true);
223     }
224 
225     /**
226      * If DPC is targeting N+, @{link IllegalArgumentException } should be thrown if the package
227      * is missing.
228      */
testSetNotExistCertInstallerPackage()229     public void testSetNotExistCertInstallerPackage() throws Exception {
230         boolean shouldThrowException = getTargetApiLevel() >= Build.VERSION_CODES.N;
231         try {
232             mDpm.setCertInstallerPackage(
233                     ADMIN_RECEIVER_COMPONENT, NOT_EXIST_CERT_INSTALLER_PACKAGE);
234             if (shouldThrowException) {
235                 fail("Did not throw IllegalArgumentException");
236             }
237         } catch (IllegalArgumentException ex) {
238             if (!shouldThrowException) {
239                 fail("Should not throw exception");
240             }
241             MoreAsserts.assertContainsRegex("is not installed on the current user",
242                         ex.getMessage());
243         }
244     }
245 
246     /**
247      * installKeyPair() requires the system to have a lockscreen password, which should have been
248      * set by the host side test.
249      */
checkKeyguardPrecondition()250     private void checkKeyguardPrecondition() throws InterruptedException {
251         KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
252         if (!km.isKeyguardSecure()) {
253             Thread.sleep(5000);
254           }
255           assertTrue("A lockscreen password is required before keypair can be installed",
256                           km.isKeyguardSecure());
257     }
258 
installCaCert(byte[] cert)259     private void installCaCert(byte[] cert) {
260         Intent intent = new Intent();
261         intent.setAction(ACTION_INSTALL_CERT);
262         intent.setComponent(CERT_INSTALLER_COMPONENT);
263         intent.putExtra(EXTRA_CERT_DATA, cert);
264         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
265         mContext.sendBroadcast(intent);
266     }
267 
removeCaCert(byte[] cert)268     private void removeCaCert(byte[] cert) {
269         Intent intent = new Intent();
270         intent.setAction(ACTION_REMOVE_CERT);
271         intent.setComponent(CERT_INSTALLER_COMPONENT);
272         intent.putExtra(EXTRA_CERT_DATA, cert);
273         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
274         mContext.sendBroadcast(intent);
275     }
276 
verifyCaCert(byte[] cert)277     private void verifyCaCert(byte[] cert) {
278         Intent intent = new Intent();
279         intent.setAction(ACTION_VERIFY_CERT);
280         intent.setComponent(CERT_INSTALLER_COMPONENT);
281         intent.putExtra(EXTRA_CERT_DATA, cert);
282         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
283         mContext.sendBroadcast(intent);
284     }
285 
assertResult(String testName, Boolean expectSuccess)286     private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
287         assertTrue("Cert installer did not respond in time.",
288                 mAvailableResultSemaphore.tryAcquire(10, TimeUnit.SECONDS));
289         synchronized (this) {
290             if (expectSuccess) {
291                 assertTrue(testName + " failed unexpectedly.", mReceivedResult);
292                 assertNull(testName + " raised exception", mReceivedException);
293             } else {
294                 assertFalse(testName + " succeeded unexpectedly.", mReceivedResult);
295                 assertTrue(testName + " did not raise SecurityException",
296                         mReceivedException != null &&
297                         mReceivedException instanceof SecurityException);
298             }
299         }
300     }
301 
installKeyPair(String key, String cert, String alias)302     private void installKeyPair(String key, String cert, String alias) {
303         Intent intent = new Intent();
304         intent.setAction(ACTION_INSTALL_KEYPAIR);
305         intent.setComponent(CERT_INSTALLER_COMPONENT);
306         intent.putExtra(EXTRA_CERT_DATA, cert);
307         intent.putExtra(EXTRA_KEY_DATA, key);
308         intent.putExtra(EXTRA_KEY_ALIAS, alias);
309         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
310         mContext.sendBroadcast(intent);
311     }
312 }
313