1 /*
2  * Copyright (C) 2020 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 android.appsecurity.cts.keyrotationtest.service;
18 
19 import android.app.Service;
20 import android.appsecurity.cts.keyrotationtest.utils.SignatureUtils;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.Signature;
26 import android.content.pm.SigningInfo;
27 import android.os.Bundle;
28 import android.os.IBinder;
29 
30 /**
31  * Provides a service that can be used within an Android application to verify the functionality of
32  * key rotation PackageManager APIs.
33  *
34  * <p>This service is specifically designed to test key rotation APIs, so some of the functionality
35  * is not generalized for multiple signers. For instance when testing {@link
36  * PackageManager#getPackageInfo(String, int)} with {@link PackageManager#GET_SIGNATURES} a response
37  * with multiple signatures is treated as an error. For the key rotation case this flag should only
38  * result in the original signer in the lineage being returned; since multiple signers are not
39  * supported with the V3 signature scheme this error is reported to the caller.
40  */
41 public class SignatureQueryService extends Service {
42     private SignatureQueryServiceImpl signatureQueryService;
43 
44     /** Service lifecycle callback method that is invoked when the service is first created. */
45     @Override
onCreate()46     public void onCreate() {
47         super.onCreate();
48         signatureQueryService = new SignatureQueryServiceImpl(getPackageManager(),
49                 getPackageName());
50     }
51 
52     /**
53      * Service lifecycle callback method that is invoked when a client attempts to bind to this
54      * service.
55      */
56     @Override
onBind(Intent intent)57     public IBinder onBind(Intent intent) {
58         return signatureQueryService;
59     }
60 
61     /**
62      * Implementation of the ISignatureQueryService to perform verification of the key rotation APIs
63      * within the PackageManager.
64      */
65     static class SignatureQueryServiceImpl extends ISignatureQueryService.Stub {
66         private final PackageManager packageManager;
67         private final String packageName;
68 
69         /**
70          * Protected constructor that accepts the {@code packageManager} and {@code packageName} to
71          * be used for the key rotation API tests.
72          */
SignatureQueryServiceImpl(PackageManager packageManager, String packageName)73         SignatureQueryServiceImpl(PackageManager packageManager, String packageName) {
74             this.packageManager = packageManager;
75             this.packageName = packageName;
76         }
77 
78         /**
79          * Performs the verification of the PackageManager key rotation APIs.
80          *
81          * <p>Verification tests are performed against the provided {@code
82          * expectedSignatureDigests}, a
83          * String array containing the hex representation of the expected signature(s) SHA-256
84          * digest(s) for the package. Digests should be in the order of the lineage with the initial
85          * signer at index 0. A non-null {@code companionPackageName} indicates that the specified
86          * package is expected to have the same signing identity as the app within which this
87          * service runs; the {@code PackageManager#checkSignature} APIs are used to verify this.
88          *
89          * <p>The following tests are performed with the provided digests and package name:
90          *
91          * <ul>
92          *   <li>Verification of the original signer in the lineage returned from {@link
93          *       PackageManager#getPackageInfo(String, int)} with the {@link
94          *       PackageManager#GET_SIGNATURES} flag.
95          *   <li>Verification of all of the signers in the lineage returned from {@link
96          *       PackageManager#getPackageInfo(String, int)} with the {@link
97          *       PackageManager#GET_SIGNING_CERTIFICATES} flag.
98          *   <li>Verification of all of the signers in the lineage from {@link
99          *       PackageManager#hasSigningCertificate(String, byte[], int)}; this method should
100          *       return
101          *       true for each of the provided digests when queried by package name.
102          *   <li>Verification of all of the signers in the lineage from {@link
103          *       PackageManager#hasSigningCertificate(int, byte[], int)}; this method should
104          *       return true
105          *       for each of the provided digests when queried by package uid.
106          *   <li>Verification of the same signing identity between this app and the specified
107          *   package
108          *       from {@link PackageManager#checkSignatures(String, String)} and {@link
109          *       PackageManager#checkSignatures(int, int)}.
110          * </ul>
111          *
112          * If any failures are encountered during the verification the service will immediately
113          * set the appropriate return code in the bundle and return.
114          *
115          * <p>A Bundle is returned containing the following elements under the specified keys:
116          *
117          * <ul>
118          *   <li>{@link ISignatureQueryService#KEY_VERIFY_SIGNATURES_RESULT} - int representing the
119          *       result of the test. For a list of return codes see {@link ISignatureQueryService}.
120          *   <li>{@link ISignatureQueryService#KEY_GET_SIGNATURES_RESULTS} - a String array of
121          *       the hex encoding of each of the signatures returned from the getPackageInfo
122          *       invocation with the {@code PackageManager#GET_SIGNATURES} flag. If no signatures
123          *       are returned from this query this value will be an empty array.
124          *   <li>{@link ISignatureQueryService#KEY_GET_SIGNING_CERTIFICATES_RESULTS} - a String
125          *       array of the hex encoding of each of the signatures returned from the
126          *       getPackageInfo invocation with the {@code PackageManager#GET_SIGNING_CERTIIFCATES}
127          *       flag. If no signature are returned from this query this value will be an empty
128          *       array.
129          * </ul>
130          */
131         @Override
verifySignatures(String[] expectedSignatureDigests, String companionPackageName)132         public Bundle verifySignatures(String[] expectedSignatureDigests,
133                 String companionPackageName) {
134             Bundle responseBundle = new Bundle();
135             if (expectedSignatureDigests == null || expectedSignatureDigests.length == 0) {
136                 responseBundle.putInt(KEY_VERIFY_SIGNATURES_RESULT,
137                         RESULT_NO_EXPECTED_SIGNATURES_PROVIDED);
138                 return responseBundle;
139             }
140 
141             // The SHA-256 MessageDigest is critical to all of the verification methods in this
142             // service, so ensure it is available before proceeding.
143             if (!SignatureUtils.computeSha256Digest(new byte[0]).isPresent()) {
144                 responseBundle.putInt(
145                         KEY_VERIFY_SIGNATURES_RESULT, RESULT_SHA256_MESSAGE_DIGEST_NOT_AVAILABLE);
146                 return responseBundle;
147             }
148 
149             int verificationResult = verifyGetSignatures(expectedSignatureDigests, responseBundle);
150             if (verificationResult != RESULT_SUCCESS) {
151                 responseBundle.putInt(KEY_VERIFY_SIGNATURES_RESULT, verificationResult);
152                 return responseBundle;
153             }
154 
155             verificationResult = verifyGetSigningCertificates(expectedSignatureDigests,
156                     responseBundle);
157             if (verificationResult != RESULT_SUCCESS) {
158                 responseBundle.putInt(KEY_VERIFY_SIGNATURES_RESULT, verificationResult);
159                 return responseBundle;
160             }
161 
162             verificationResult = verifyHasSigningCertificate(expectedSignatureDigests);
163             if (verificationResult != RESULT_SUCCESS) {
164                 responseBundle.putInt(KEY_VERIFY_SIGNATURES_RESULT, verificationResult);
165                 return responseBundle;
166             }
167 
168             verificationResult = verifyCheckSignatures(companionPackageName);
169             responseBundle.putInt(KEY_VERIFY_SIGNATURES_RESULT, verificationResult);
170             return responseBundle;
171         }
172 
173         /**
174          * Verifies the {@code PackageManager#getPackageInfo(String, int)} API returns the expected
175          * signature when invoked with the {@code PackageManager#GET_SIGNATURES} flag.
176          *
177          * <p>With this flag the API should return an array of {@link Signature} objects with a
178          * single element, the first signer in the lineage. The signature(s) are added to the
179          * provided {@code responseBundle}, then the signature is compared against the first element
180          * in the specified {@code expectedSignatureDigests}.
181          *
182          * <p>The following return codes can be returned:
183          *
184          * <ul>
185          *   <li>{@link ISignaturefervice#RESULT_SUCCESS} if a single signature is returned and
186          *   matches the expected digest of the first signer in the provided lineage.
187          *   <li>{@link ISignatureQueryService#RESULT_PACKAGE_NOT_FOUND} if the package name
188          *   cannot be found by the PackageManager.
189          *   <li>{@link ISignatureQueryService#RESULT_GET_SIGNATURES_NO_RESULTS} if no signatures
190          *   are returned.
191          *   <li>{@link ISignatureQueryService#RESULT_GET_SIGNATURES_MULTIPLE_SIGNATURES} if
192          *   multiple signatures are returned.
193          *   <li>{@link ISignatureQueryService#RESULT_GET_SIGNATURES_MISMATCH} if a single
194          *   signature is returned but does not match the first signer in the provided lineage.
195          * </ul>
196          */
verifyGetSignatures(String[] expectedSignatureDigests, Bundle responseBundle)197         private int verifyGetSignatures(String[] expectedSignatureDigests, Bundle responseBundle) {
198             PackageInfo packageInfo;
199             try {
200                 packageInfo = packageManager.getPackageInfo(packageName,
201                         PackageManager.GET_SIGNATURES);
202             } catch (PackageManager.NameNotFoundException e) {
203                 return RESULT_PACKAGE_NOT_FOUND;
204             }
205             Signature[] signatures = packageInfo.signatures;
206             copySignaturesToBundle(signatures, responseBundle, KEY_GET_SIGNATURES_RESULTS);
207             if (signatures == null || signatures.length == 0) {
208                 return RESULT_GET_SIGNATURES_NO_RESULTS;
209             }
210             // GET_SIGNATURES should only return the initial signing certificate in the linage.
211             if (signatures.length > 1) {
212                 return RESULT_GET_SIGNATURES_MULTIPLE_SIGNATURES;
213             }
214             if (!verifySignatureDigest(signatures[0], expectedSignatureDigests[0])) {
215                 return RESULT_GET_SIGNATURES_MISMATCH;
216             }
217             return RESULT_SUCCESS;
218         }
219 
220         /**
221          * Verifies the {@code PackageManager#getPackageInfo(String, int)} API returns the expected
222          * signatures when invoked with the {@code PackageManager#GET_SIGNING_CERTIFICATES} flag.
223          *
224          * <p>With this flag the API should return a {@link SigningInfo} object indicating whether
225          * the app is signed with multiple signers along with an array of {@code Signature} objects
226          * representing all of the signers in the lineage including the current signer. These
227          * signatures are added to the provided {@code responseBundle}, then each is verified
228          * against the specified {@code expectedSignatureDigests} to ensure that all of the expected
229          * signatures are returned by the API.
230          *
231          * <p>The following return codes can be returned by this method:
232          *
233          * <ul>
234          *   <li>{@link ISignatureQueryService#RESULT_SUCCESS} if the API returns that there is
235          *       only a single signer and all of the expected signatures are in the linage.
236          *   <li>{@link ISignatureQueryService#RESULT_PACKAGE_NOT_FOUND} if the package name
237          *       cannot be found by the PackageManager.
238          *   <li>{@link ISignatureQueryService#RESULT_GET_SIGNING_CERTIFICATES_NO_RESULTS} if no
239          *       signatures are returned.
240          *   <li>{@link ISignatureQueryService#RESULT_GET_SIGNING_CERTIFICATES_MULTIPLE_SIGNERS}
241          *       if the response from the API indicates the app has been signed by multiple signers.
242          *   <li>{@link
243          *   ISignatureQueryService#RESULT_GET_SIGNING_CERTIFICATES_UNEXPECTED_NUMBER_OF_SIGNATURES}
244          *       if the returned number of signatures does not match the number of expected
245          *       signatures.
246          *   <li>{@link ISignatureQueryService#RESULT_GET_SIGNING_CERTIFICATES_UNEXPECTED_SIGNATURE}
247          *       if the results contains one or more signatures that are not in the array of
248          *       expected signature digests.
249          * </ul>
250          */
verifyGetSigningCertificates( String[] expectedSignatureDigests, Bundle responseBundle)251         private int verifyGetSigningCertificates(
252                 String[] expectedSignatureDigests, Bundle responseBundle) {
253             PackageInfo packageInfo;
254             try {
255                 packageInfo =
256                         packageManager.getPackageInfo(packageName,
257                                 PackageManager.GET_SIGNING_CERTIFICATES);
258             } catch (PackageManager.NameNotFoundException e) {
259                 return RESULT_PACKAGE_NOT_FOUND;
260             }
261             SigningInfo signingInfo = packageInfo.signingInfo;
262             Signature[] signatures =
263                     signingInfo != null ? signingInfo.getSigningCertificateHistory() : null;
264             copySignaturesToBundle(signatures, responseBundle,
265                     KEY_GET_SIGNING_CERTIFICATES_RESULTS);
266             if (signingInfo == null) {
267                 return RESULT_GET_SIGNING_CERTIFICATES_NO_RESULTS;
268             }
269             if (signingInfo.hasMultipleSigners()) {
270                 return RESULT_GET_SIGNING_CERTIFICATES_MULTIPLE_SIGNERS;
271             }
272             if (signatures.length != expectedSignatureDigests.length) {
273                 return RESULT_GET_SIGNING_CERTIFICATES_UNEXPECTED_NUMBER_OF_SIGNATURES;
274             }
275             // The signing certificate history should be in the order of the lineage.
276             for (int i = 0; i < signatures.length; i++) {
277                 if (!verifySignatureDigest(signatures[i], expectedSignatureDigests[i])) {
278                     return RESULT_GET_SIGNING_CERTIFICATES_UNEXPECTED_SIGNATURE;
279                 }
280             }
281             return RESULT_SUCCESS;
282         }
283 
284         /**
285          * Verifies {@link PackageManager#hasSigningCertificate(String, byte[], int)} and {@link
286          * PackageManager#hasSigningCertificate(int, byte[], int)} APIs indicate that this package
287          * has the provided {@code expectedSignatureDigests} when querying by package name and uid.
288          *
289          * <p>The following return codes can be returned:
290          *
291          * <ul>
292          *   <li>{@link ISignatureQueryService#RESULT_SUCCESS} if the API indicates each of the
293          *       expected signing certificate digests is a signing certificate of the app.
294          *   <li>{@link ISignatureQueryService#RESULT_PACKAGE_NOT_FOUND} if the package name
295          *       cannot be found when querying for the app's uid.
296          *   <li>{@link ISignatureQueryService#RESULT_HAS_SIGNING_CERTIFICATE_BY_NAME_FAILED} if
297          *       the API returns one of the expected signing certificates is not found when querying
298          *       by package name.
299          *   <li>{@link ISignatureQueryService#RESULT_HAS_SIGNING_CERTIFICATE_BY_UID_FAILED} if
300          *       the API returns one of the expected signing certificates is not found when querying
301          *       by uid.
302          * </ul>
303          */
verifyHasSigningCertificate(String[] expectedSignatureDigests)304         private int verifyHasSigningCertificate(String[] expectedSignatureDigests) {
305             int uid;
306             try {
307                 ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
308                 uid = applicationInfo.uid;
309             } catch (PackageManager.NameNotFoundException e) {
310                 return RESULT_PACKAGE_NOT_FOUND;
311             }
312             for (String expectedSignatureDigest : expectedSignatureDigests) {
313                 byte[] signature = new Signature(expectedSignatureDigest).toByteArray();
314                 if (!packageManager.hasSigningCertificate(
315                         packageName, signature, PackageManager.CERT_INPUT_SHA256)) {
316                     return RESULT_HAS_SIGNING_CERTIFICATE_BY_NAME_FAILED;
317                 }
318                 if (!packageManager.hasSigningCertificate(
319                         uid, signature, PackageManager.CERT_INPUT_SHA256)) {
320                     return RESULT_HAS_SIGNING_CERTIFICATE_BY_UID_FAILED;
321                 }
322             }
323             return RESULT_SUCCESS;
324         }
325 
326         /**
327          * Verifies {@link PackageManager#checkSignatures(String, String)} and {@link
328          * PackageManager#checkSignatures(int, int)} APIs indicate that this package and the
329          * provided {@code companionPackageName} have the same signing identity when querying by
330          * package name and uid.
331          *
332          * <p>The following return codes can be returned:
333          *
334          * <ul>
335          *   <li>{@link ISignatureQueryService#RESULT_SUCCESS} if the API indicates both this
336          *       package and the companion package have the same signing identity.
337          *   <li>{@link ISignatureQueryService#RESULT_COMPANION_PACKAGE_NOT_FOUND} if the companion
338          *       package name cannot be found when querying for the companion app's uid.
339          *   <li>{@link ISignatureQueryService#RESULT_CHECK_SIGNATURES_BY_NAME_NO_MATCH} if the API
340          *       returns that this package and the companion package do not have the same signing
341          *       identity when querying by name.
342          *   <li>{@link ISignatureQueryService#RESULT_CHECK_SIGNATURES_BY_UID_NO_MATCH} if the API
343          *       returns that this package and the companion package do not have the same signing
344          *       identity when querying by uid.
345          * </ul>
346          *
347          * <p>If a null companion package is specified the signing identity check is skipped, and
348          * the method returns {@code ISignatureQueryService#RESULT_SUCCESS}.
349          */
verifyCheckSignatures(String companionPackageName)350         private int verifyCheckSignatures(String companionPackageName) {
351             if (companionPackageName == null) {
352                 return RESULT_SUCCESS;
353             }
354             int uid;
355             int companionUid;
356             try {
357                 ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
358                 uid = applicationInfo.uid;
359             } catch (PackageManager.NameNotFoundException e) {
360                 return RESULT_PACKAGE_NOT_FOUND;
361             }
362             try {
363                 ApplicationInfo applicationInfo =
364                         packageManager.getApplicationInfo(companionPackageName, 0);
365                 companionUid = applicationInfo.uid;
366             } catch (PackageManager.NameNotFoundException e) {
367                 return RESULT_COMPANION_PACKAGE_NOT_FOUND;
368             }
369             if (packageManager.checkSignatures(packageName, companionPackageName)
370                     != PackageManager.SIGNATURE_MATCH) {
371                 return RESULT_CHECK_SIGNATURES_BY_NAME_NO_MATCH;
372             }
373             if (packageManager.checkSignatures(uid, companionUid)
374                     != PackageManager.SIGNATURE_MATCH) {
375                 return RESULT_CHECK_SIGNATURES_BY_UID_NO_MATCH;
376             }
377             return RESULT_SUCCESS;
378         }
379 
380         /**
381          * Returns whether the provided {@code signature} matches the specified {@code
382          * expectedDigest}.
383          */
verifySignatureDigest(Signature signature, String expectedDigest)384         private static boolean verifySignatureDigest(Signature signature, String expectedDigest) {
385             return SignatureUtils.computeSha256Digest(signature.toByteArray())
386                     .get()
387                     .equals(expectedDigest);
388         }
389 
390         /**
391          * Copies the provided {@code signatures} to the specified {@code responseBundle} using the
392          * {@code bundleKey} as the key under which to store the signatures.
393          *
394          * <p>If the provided {@code Signature} array is null or empty then a String array of length
395          * zero is written to the bundle.
396          */
copySignaturesToBundle( Signature[] signatures, Bundle responseBundle, String bundleKey)397         private static void copySignaturesToBundle(
398                 Signature[] signatures, Bundle responseBundle, String bundleKey) {
399             String[] bundleSignatures;
400             if (signatures != null) {
401                 bundleSignatures = new String[signatures.length];
402                 for (int i = 0; i < signatures.length; i++) {
403                     bundleSignatures[i] = signatures[i].toCharsString();
404                 }
405             } else {
406                 bundleSignatures = new String[0];
407             }
408             responseBundle.putStringArray(bundleKey, bundleSignatures);
409         }
410     }
411 }
412 
413