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