1 /* 2 * Copyright (C) 2021 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.content.pm; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.text.TextUtils; 25 import android.util.ArraySet; 26 import android.util.PackageUtils; 27 import android.util.Slog; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.internal.util.DataClass; 31 32 import libcore.util.HexEncoding; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.security.PublicKey; 37 import java.security.cert.CertificateException; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.Set; 43 44 /** 45 * A container for signing-related data of an application package. 46 * 47 * @hide 48 */ 49 @DataClass(genConstructor = false, genConstDefs = false, genParcelable = true, genAidl = false) 50 public final class SigningDetails implements Parcelable { 51 52 private static final String TAG = "SigningDetails"; 53 54 @Retention(RetentionPolicy.SOURCE) 55 @IntDef({SignatureSchemeVersion.UNKNOWN, 56 SignatureSchemeVersion.JAR, 57 SignatureSchemeVersion.SIGNING_BLOCK_V2, 58 SignatureSchemeVersion.SIGNING_BLOCK_V3, 59 SignatureSchemeVersion.SIGNING_BLOCK_V4}) 60 public @interface SignatureSchemeVersion { 61 int UNKNOWN = 0; 62 int JAR = 1; 63 int SIGNING_BLOCK_V2 = 2; 64 int SIGNING_BLOCK_V3 = 3; 65 int SIGNING_BLOCK_V4 = 4; 66 } 67 68 /** The signing certificates associated with this application package. */ 69 private final @Nullable Signature[] mSignatures; 70 71 /** The signature scheme version for this application package. */ 72 private final @SignatureSchemeVersion int mSignatureSchemeVersion; 73 74 /** The public keys set for the certificates. */ 75 private final @Nullable ArraySet<PublicKey> mPublicKeys; 76 77 /** 78 * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that 79 * contains two pieces of information: 80 * 1) the past signing certificates 81 * 2) the flags that APK wants to assign to each of the past signing certificates. 82 * 83 * This collection of {@code Signature} objects, each of which is formed from a former 84 * signing certificate of this APK before it was changed by signing certificate rotation, 85 * represents the first piece of information. It is the APK saying to the rest of the 86 * world: "hey if you trust the old cert, you can trust me!" This is useful, if for 87 * instance, the platform would like to determine whether or not to allow this APK to do 88 * something it would've allowed it to do under the old cert (like upgrade). 89 */ 90 private final @Nullable Signature[] mPastSigningCertificates; 91 92 /** special value used to see if cert is in package - not exposed to callers */ 93 private static final int PAST_CERT_EXISTS = 0; 94 95 @IntDef(flag = true, 96 value = {CertCapabilities.INSTALLED_DATA, 97 CertCapabilities.SHARED_USER_ID, 98 CertCapabilities.PERMISSION, 99 CertCapabilities.ROLLBACK}) 100 public @interface CertCapabilities { 101 102 /** accept data from already installed pkg with this cert */ 103 int INSTALLED_DATA = 1; 104 105 /** accept sharedUserId with pkg with this cert */ 106 int SHARED_USER_ID = 2; 107 108 /** grant SIGNATURE permissions to pkgs with this cert */ 109 int PERMISSION = 4; 110 111 /** allow pkg to update to one signed by this certificate */ 112 int ROLLBACK = 8; 113 114 /** allow pkg to continue to have auth access gated by this cert */ 115 int AUTH = 16; 116 } 117 118 @IntDef(value = {CapabilityMergeRule.MERGE_SELF_CAPABILITY, 119 CapabilityMergeRule.MERGE_OTHER_CAPABILITY, 120 CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY}) 121 public @interface CapabilityMergeRule { 122 /** 123 * When capabilities are different for a common signer in the lineage, use the capabilities 124 * from this instance. 125 */ 126 int MERGE_SELF_CAPABILITY = 0; 127 128 /** 129 * When capabilities are different for a common signer in the lineage, use the capabilites 130 * from the other instance. 131 */ 132 int MERGE_OTHER_CAPABILITY = 1; 133 134 /** 135 * When capabilities are different for a common signer in the lineage, use the most 136 * restrictive between the two signers. 137 */ 138 int MERGE_RESTRICTED_CAPABILITY = 2; 139 } 140 141 /** A representation of unknown signing details. Use instead of null. */ 142 public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null, 143 SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null); 144 145 @VisibleForTesting SigningDetails(@ullable Signature[] signatures, @SignatureSchemeVersion int signatureSchemeVersion, @Nullable ArraySet<PublicKey> keys, @Nullable Signature[] pastSigningCertificates)146 public SigningDetails(@Nullable Signature[] signatures, 147 @SignatureSchemeVersion int signatureSchemeVersion, 148 @Nullable ArraySet<PublicKey> keys, @Nullable Signature[] pastSigningCertificates) { 149 mSignatures = signatures; 150 mSignatureSchemeVersion = signatureSchemeVersion; 151 mPublicKeys = keys; 152 mPastSigningCertificates = pastSigningCertificates; 153 } 154 SigningDetails(@ullable Signature[] signatures, @SignatureSchemeVersion int signatureSchemeVersion, @Nullable Signature[] pastSigningCertificates)155 public SigningDetails(@Nullable Signature[] signatures, 156 @SignatureSchemeVersion int signatureSchemeVersion, 157 @Nullable Signature[] pastSigningCertificates) 158 throws CertificateException { 159 this(signatures, signatureSchemeVersion, toSigningKeys(signatures), 160 pastSigningCertificates); 161 } 162 SigningDetails(@ullable Signature[] signatures, @SignatureSchemeVersion int signatureSchemeVersion)163 public SigningDetails(@Nullable Signature[] signatures, 164 @SignatureSchemeVersion int signatureSchemeVersion) 165 throws CertificateException { 166 this(signatures, signatureSchemeVersion, /* pastSigningCertificates */ null); 167 } 168 SigningDetails(@ullable SigningDetails orig)169 public SigningDetails(@Nullable SigningDetails orig) { 170 if (orig != null) { 171 if (orig.mSignatures != null) { 172 mSignatures = orig.mSignatures.clone(); 173 } else { 174 mSignatures = null; 175 } 176 mSignatureSchemeVersion = orig.mSignatureSchemeVersion; 177 mPublicKeys = new ArraySet<>(orig.mPublicKeys); 178 if (orig.mPastSigningCertificates != null) { 179 mPastSigningCertificates = orig.mPastSigningCertificates.clone(); 180 } else { 181 mPastSigningCertificates = null; 182 } 183 } else { 184 mSignatures = null; 185 mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN; 186 mPublicKeys = null; 187 mPastSigningCertificates = null; 188 } 189 } 190 191 /** 192 * Merges the signing lineage of this instance with the lineage in the provided {@code 193 * otherSigningDetails} using {@link CapabilityMergeRule#MERGE_OTHER_CAPABILITY} as the merge 194 * rule. 195 * 196 * @param otherSigningDetails the {@code SigningDetails} with which to merge 197 * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer 198 * of the other. If neither instance has a lineage, or if neither has the same or an 199 * ancestor signer then this instance is returned. 200 * @see #mergeLineageWith(SigningDetails, int) 201 */ mergeLineageWith(@onNull SigningDetails otherSigningDetails)202 public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) { 203 return mergeLineageWith(otherSigningDetails, CapabilityMergeRule.MERGE_OTHER_CAPABILITY); 204 } 205 206 /** 207 * Merges the signing lineage of this instance with the lineage in the provided {@code 208 * otherSigningDetails} when one has the same or an ancestor signer of the other using the 209 * provided {@code mergeRule} to handle differences in capabilities for shared signers. 210 * 211 * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance 212 * containing the longest common lineage with differences in capabilities for shared signers 213 * resolved using the provided {@code mergeRule}. If the two lineages contain the same signers 214 * with the same capabilities then the instance on which this was invoked is returned without 215 * any changes. Similarly if neither instance has a lineage, or if neither has the same or an 216 * ancestor signer then this instance is returned. 217 * 218 * Following are some example results of this method for lineages with signers A, B, C, D: 219 * <ul> 220 * <li>lineage B merged with lineage A -> B returns lineage A -> B. 221 * <li>lineage A -> B merged with lineage B -> C returns lineage A -> B -> C 222 * <li>lineage A -> B with the {@code PERMISSION} capability revoked for A merged with 223 * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns the 224 * following based on the {@code mergeRule}: 225 * <ul> 226 * <li>{@code MERGE_SELF_CAPABILITY} - lineage A -> B with {@code PERMISSION} revoked 227 * for A. 228 * <li>{@code MERGE_OTHER_CAPABILITY} - lineage A -> B with {@code SHARED_USER_ID} 229 * revoked for A. 230 * <li>{@code MERGE_RESTRICTED_CAPABILITY} - lineage A -> B with {@code PERMISSION} and 231 * {@code SHARED_USER_ID} revoked for A. 232 * </ul> 233 * <li>lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage 234 * A -> B -> C since the current signer of both instances is not the same or in the 235 * lineage of the other. 236 * </ul> 237 * 238 * @param otherSigningDetails the {@code SigningDetails} with which to merge 239 * @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in 240 * capabilities for shared signers 241 * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer 242 * of the other. If neither instance has a lineage, or if neither has the same or an 243 * ancestor signer then this instance is returned. 244 */ mergeLineageWith(@onNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule)245 public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails, 246 @CapabilityMergeRule int mergeRule) { 247 if (!hasPastSigningCertificates()) { 248 return otherSigningDetails.hasPastSigningCertificates() 249 && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this; 250 } 251 if (!otherSigningDetails.hasPastSigningCertificates()) { 252 return this; 253 } 254 // Use the utility method to determine which SigningDetails instance is the descendant 255 // and to confirm that the signing lineage does not diverge. 256 SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails); 257 if (descendantSigningDetails == null) { 258 return this; 259 } 260 SigningDetails mergedDetails = this; 261 if (descendantSigningDetails == this) { 262 // If this instance is the descendant then the merge will also be invoked against this 263 // instance and the provided mergeRule can be used as is. 264 mergedDetails = mergeLineageWithAncestorOrSelf(otherSigningDetails, mergeRule); 265 } else { 266 // If the provided instance is the descendant then the merge will be invoked against the 267 // other instance and a self or other merge rule will need to be flipped. 268 switch (mergeRule) { 269 case CapabilityMergeRule.MERGE_SELF_CAPABILITY: 270 mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this, 271 CapabilityMergeRule.MERGE_OTHER_CAPABILITY); 272 break; 273 case CapabilityMergeRule.MERGE_OTHER_CAPABILITY: 274 mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this, 275 CapabilityMergeRule.MERGE_SELF_CAPABILITY); 276 break; 277 case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY: 278 mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this, 279 mergeRule); 280 break; 281 } 282 } 283 return mergedDetails; 284 } 285 286 /** 287 * Merges the signing lineage of this instance with the lineage of the ancestor (or same) 288 * signer in the provided {@code otherSigningDetails}. 289 * 290 * @param otherSigningDetails the {@code SigningDetails} with which to merge 291 * @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in 292 * capabilities for shared signers 293 * @return Merged {@code SigningDetails} instance. 294 */ mergeLineageWithAncestorOrSelf( @onNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule)295 private @NonNull SigningDetails mergeLineageWithAncestorOrSelf( 296 @NonNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule) { 297 // This method should only be called with instances that contain lineages. 298 int index = mPastSigningCertificates.length - 1; 299 int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1; 300 if (index < 0 || otherIndex < 0) { 301 return this; 302 } 303 304 List<Signature> mergedSignatures = new ArrayList<>(); 305 boolean capabilitiesModified = false; 306 // If this is a descendant lineage then add all of the descendant signer(s) to the 307 // merged lineage until the ancestor signer is reached. 308 while (index >= 0 && !mPastSigningCertificates[index].equals( 309 otherSigningDetails.mPastSigningCertificates[otherIndex])) { 310 mergedSignatures.add(new Signature(mPastSigningCertificates[index--])); 311 } 312 // If the signing lineage was exhausted then the provided ancestor is not actually an 313 // ancestor of this lineage. 314 if (index < 0) { 315 return this; 316 } 317 318 do { 319 // Add the common signer to the merged lineage and resolve any differences in 320 // capabilites with the merge rule. 321 Signature signature = mPastSigningCertificates[index--]; 322 Signature ancestorSignature = 323 otherSigningDetails.mPastSigningCertificates[otherIndex--]; 324 Signature mergedSignature = new Signature(signature); 325 if (signature.getFlags() != ancestorSignature.getFlags()) { 326 capabilitiesModified = true; 327 switch (mergeRule) { 328 case CapabilityMergeRule.MERGE_SELF_CAPABILITY: 329 mergedSignature.setFlags(signature.getFlags()); 330 break; 331 case CapabilityMergeRule.MERGE_OTHER_CAPABILITY: 332 mergedSignature.setFlags(ancestorSignature.getFlags()); 333 break; 334 case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY: 335 mergedSignature.setFlags( 336 signature.getFlags() & ancestorSignature.getFlags()); 337 break; 338 } 339 } 340 mergedSignatures.add(mergedSignature); 341 } while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals( 342 otherSigningDetails.mPastSigningCertificates[otherIndex])); 343 344 // If both lineages still have elements then their lineages have diverged; since this is 345 // not supported return the invoking instance. 346 if (index >= 0 && otherIndex >= 0) { 347 return this; 348 } 349 350 // Add any remaining elements from either lineage that is not yet exhausted to the 351 // the merged lineage. 352 while (otherIndex >= 0) { 353 mergedSignatures.add(new Signature( 354 otherSigningDetails.mPastSigningCertificates[otherIndex--])); 355 } 356 while (index >= 0) { 357 mergedSignatures.add(new Signature(mPastSigningCertificates[index--])); 358 } 359 360 // if this lineage already contains all the elements in the ancestor and none of the 361 // capabilities were changed then just return this instance. 362 if (mergedSignatures.size() == mPastSigningCertificates.length 363 && !capabilitiesModified) { 364 return this; 365 } 366 // Since the signatures were added to the merged lineage from newest to oldest reverse 367 // the list to ensure the oldest signer is at index 0. 368 Collections.reverse(mergedSignatures); 369 try { 370 return new SigningDetails(new Signature[]{new Signature(mSignatures[0])}, 371 mSignatureSchemeVersion, mergedSignatures.toArray(new Signature[0])); 372 } catch (CertificateException e) { 373 Slog.e(TAG, "Caught an exception creating the merged lineage: ", e); 374 return this; 375 } 376 } 377 378 /** 379 * Returns whether this and the provided {@code otherSigningDetails} share a common 380 * ancestor. 381 * 382 * <p>The two SigningDetails have a common ancestor if any of the following conditions are 383 * met: 384 * - If neither has a lineage and their current signer(s) are equal. 385 * - If only one has a lineage and the signer of the other is the same or in the lineage. 386 * - If both have a lineage and their current signers are the same or one is in the lineage 387 * of the other, and their lineages do not diverge to different signers. 388 */ hasCommonAncestor(@onNull SigningDetails otherSigningDetails)389 public boolean hasCommonAncestor(@NonNull SigningDetails otherSigningDetails) { 390 if (!hasPastSigningCertificates()) { 391 // If this instance does not have a lineage then it must either be in the ancestry 392 // of or the same signer of the otherSigningDetails. 393 return otherSigningDetails.hasAncestorOrSelf(this); 394 } 395 if (!otherSigningDetails.hasPastSigningCertificates()) { 396 return hasAncestorOrSelf(otherSigningDetails); 397 } 398 // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing 399 // details; a null return from that method indicates there is no common lineage between 400 // the two or that they diverge at a point in the lineage. 401 return getDescendantOrSelf(otherSigningDetails) != null; 402 } 403 404 /** 405 * Returns whether this instance is currently signed, or has ever been signed, with a 406 * signing certificate from the provided {@link Set} of {@code certDigests}. 407 * 408 * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding 409 * of each trusted certificate with the digest characters in upper case. If this instance 410 * has multiple signers then all signers must be in the provided {@code Set}. If this 411 * instance has a signing lineage then this method will return true if any of the previous 412 * signers in the lineage match one of the entries in the {@code Set}. 413 */ hasAncestorOrSelfWithDigest(@ullable Set<String> certDigests)414 public boolean hasAncestorOrSelfWithDigest(@Nullable Set<String> certDigests) { 415 if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) { 416 return false; 417 } 418 // If an app is signed by multiple signers then all of the signers must be in the Set. 419 if (mSignatures.length > 1) { 420 // If the Set has less elements than the number of signatures then immediately 421 // return false as there's no way to satisfy the requirement of all signatures being 422 // in the Set. 423 if (certDigests.size() < mSignatures.length) { 424 return false; 425 } 426 for (Signature signature : mSignatures) { 427 String signatureDigest = PackageUtils.computeSha256Digest( 428 signature.toByteArray()); 429 if (!certDigests.contains(signatureDigest)) { 430 return false; 431 } 432 } 433 return true; 434 } 435 436 String signatureDigest = PackageUtils.computeSha256Digest(mSignatures[0].toByteArray()); 437 if (certDigests.contains(signatureDigest)) { 438 return true; 439 } 440 if (hasPastSigningCertificates()) { 441 // The last element in the pastSigningCertificates array is the current signer; 442 // since that was verified above just check all the signers in the lineage. 443 for (int i = 0; i < mPastSigningCertificates.length - 1; i++) { 444 signatureDigest = PackageUtils.computeSha256Digest( 445 mPastSigningCertificates[i].toByteArray()); 446 if (certDigests.contains(signatureDigest)) { 447 return true; 448 } 449 } 450 } 451 return false; 452 } 453 454 /** 455 * Returns the SigningDetails with a descendant (or same) signer after verifying the 456 * descendant has the same, a superset, or a subset of the lineage of the ancestor. 457 * 458 * <p>If this instance and the provided {@code otherSigningDetails} do not share an 459 * ancestry, or if their lineages diverge then null is returned to indicate there is no 460 * valid descendant SigningDetails. 461 */ getDescendantOrSelf( @onNull SigningDetails otherSigningDetails)462 private @Nullable SigningDetails getDescendantOrSelf( 463 @NonNull SigningDetails otherSigningDetails) { 464 final SigningDetails descendantSigningDetails; 465 final SigningDetails ancestorSigningDetails; 466 if (hasAncestorOrSelf(otherSigningDetails)) { 467 // If the otherSigningDetails has the same signer or a signer in the lineage of this 468 // instance then treat this instance as the descendant. 469 descendantSigningDetails = this; 470 ancestorSigningDetails = otherSigningDetails; 471 } else if (otherSigningDetails.hasAncestor(this)) { 472 // The above check confirmed that the two instances do not have the same signer and 473 // the signer of otherSigningDetails is not in this instance's lineage; if this 474 // signer is in the otherSigningDetails lineage then treat this as the ancestor. 475 descendantSigningDetails = otherSigningDetails; 476 ancestorSigningDetails = this; 477 } else { 478 // The signers are not the same and neither has the current signer of the other in 479 // its lineage; return null to indicate there is no descendant signer. 480 return null; 481 } 482 // Once the descent (or same) signer is identified iterate through the ancestry until 483 // the current signer of the ancestor is found. 484 int descendantIndex = descendantSigningDetails.mPastSigningCertificates.length - 1; 485 int ancestorIndex = ancestorSigningDetails.mPastSigningCertificates.length - 1; 486 while (descendantIndex >= 0 487 && !descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals( 488 ancestorSigningDetails.mPastSigningCertificates[ancestorIndex])) { 489 descendantIndex--; 490 } 491 // Since the ancestry was verified above the descendant lineage should never be 492 // exhausted, but if for some reason the ancestor signer is not found then return null. 493 if (descendantIndex < 0) { 494 return null; 495 } 496 // Once the common ancestor (or same) signer is found iterate over the lineage of both 497 // to ensure that they are either the same or one is a subset of the other. 498 do { 499 descendantIndex--; 500 ancestorIndex--; 501 } while (descendantIndex >= 0 && ancestorIndex >= 0 502 && descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals( 503 ancestorSigningDetails.mPastSigningCertificates[ancestorIndex])); 504 505 // If both lineages still have elements then they diverge and cannot be considered a 506 // valid common lineage. 507 if (descendantIndex >= 0 && ancestorIndex >= 0) { 508 return null; 509 } 510 // Since one or both of the lineages was exhausted they are either the same or one is a 511 // subset of the other; return the valid descendant. 512 return descendantSigningDetails; 513 } 514 515 /** Returns true if the signing details have one or more signatures. */ hasSignatures()516 public boolean hasSignatures() { 517 return mSignatures != null && mSignatures.length > 0; 518 } 519 520 /** Returns true if the signing details have past signing certificates. */ hasPastSigningCertificates()521 public boolean hasPastSigningCertificates() { 522 return mPastSigningCertificates != null && mPastSigningCertificates.length > 0; 523 } 524 525 /** 526 * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one. 527 * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates, 528 * then that means it has authorized a signing certificate rotation, which eventually leads 529 * to our certificate, and thus can be trusted. If this method evaluates to true, this 530 * SigningDetails object should be trusted if the previous one is. 531 */ hasAncestorOrSelf(@onNull SigningDetails oldDetails)532 public boolean hasAncestorOrSelf(@NonNull SigningDetails oldDetails) { 533 if (this == UNKNOWN || oldDetails == UNKNOWN) { 534 return false; 535 } 536 if (oldDetails.mSignatures.length > 1) { 537 // multiple-signer packages cannot rotate signing certs, so we just compare current 538 // signers for an exact match 539 return signaturesMatchExactly(oldDetails); 540 } else { 541 // we may have signing certificate rotation history, check to see if the oldDetails 542 // was one of our old signing certificates 543 return hasCertificate(oldDetails.mSignatures[0]); 544 } 545 } 546 547 /** 548 * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails} 549 * is a descendant of {@code oldDetails}, not if they're the same. This is used to 550 * determine if this object is newer than the provided one. 551 */ hasAncestor(@onNull SigningDetails oldDetails)552 public boolean hasAncestor(@NonNull SigningDetails oldDetails) { 553 if (this == UNKNOWN || oldDetails == UNKNOWN) { 554 return false; 555 } 556 if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) { 557 // the last entry in pastSigningCertificates is the current signer, ignore it 558 for (int i = 0; i < mPastSigningCertificates.length - 1; i++) { 559 if (mPastSigningCertificates[i].equals(oldDetails.mSignatures[0])) { 560 return true; 561 } 562 } 563 } 564 return false; 565 } 566 567 /** 568 * Returns whether this {@code SigningDetails} has a signer in common with the provided 569 * {@code otherDetails} with the specified {@code flags} capabilities provided by this 570 * signer. 571 * 572 * <p>Note this method allows for the signing lineage to diverge, so this should only be 573 * used for instances where the only requirement is a common signer in the lineage with 574 * the specified capabilities. If the current signer of this instance is an ancestor of 575 * {@code otherDetails} then {@code true} is immediately returned since the current signer 576 * has all capabilities granted. 577 */ hasCommonSignerWithCapability(@onNull SigningDetails otherDetails, @CertCapabilities int flags)578 public boolean hasCommonSignerWithCapability(@NonNull SigningDetails otherDetails, 579 @CertCapabilities int flags) { 580 if (this == UNKNOWN || otherDetails == UNKNOWN) { 581 return false; 582 } 583 // If either is signed with more than one signer then both must be signed by the same 584 // signers to consider the capabilities granted. 585 if (mSignatures.length > 1 || otherDetails.mSignatures.length > 1) { 586 return signaturesMatchExactly(otherDetails); 587 } 588 // The Signature class does not use the granted capabilities in the hashCode 589 // computation, so a Set can be used to check for a common signer. 590 Set<Signature> otherSignatures = new ArraySet<>(); 591 if (otherDetails.hasPastSigningCertificates()) { 592 otherSignatures.addAll(Arrays.asList(otherDetails.mPastSigningCertificates)); 593 } else { 594 otherSignatures.addAll(Arrays.asList(otherDetails.mSignatures)); 595 } 596 // If the current signer of this instance is an ancestor of the other than return true 597 // since all capabilities are granted to the current signer. 598 if (otherSignatures.contains(mSignatures[0])) { 599 return true; 600 } 601 if (hasPastSigningCertificates()) { 602 // Since the current signer was checked above and the last signature in the 603 // pastSigningCertificates is the current signer skip checking the last element. 604 for (int i = 0; i < mPastSigningCertificates.length - 1; i++) { 605 if (otherSignatures.contains(mPastSigningCertificates[i])) { 606 // If the caller specified multiple capabilities ensure all are set. 607 if ((mPastSigningCertificates[i].getFlags() & flags) == flags) { 608 return true; 609 } 610 } 611 } 612 } 613 return false; 614 } 615 616 /** 617 * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or 618 * not this one grants it the provided capability, represented by the {@code flags} 619 * parameter. In the event of signing certificate rotation, a package may still interact 620 * with entities signed by its old signing certificate and not want to break previously 621 * functioning behavior. The {@code flags} value determines which capabilities the app 622 * signed by the newer signing certificate would like to continue to give to its previous 623 * signing certificate(s). 624 */ checkCapability(@onNull SigningDetails oldDetails, @CertCapabilities int flags)625 public boolean checkCapability(@NonNull SigningDetails oldDetails, 626 @CertCapabilities int flags) { 627 if (this == UNKNOWN || oldDetails == UNKNOWN) { 628 return false; 629 } 630 if (oldDetails.mSignatures.length > 1) { 631 // multiple-signer packages cannot rotate signing certs, so we must have an exact 632 // match, which also means all capabilities are granted 633 return signaturesMatchExactly(oldDetails); 634 } else { 635 // we may have signing certificate rotation history, check to see if the oldDetails 636 // was one of our old signing certificates, and if we grant it the capability it's 637 // requesting 638 return hasCertificate(oldDetails.mSignatures[0], flags); 639 } 640 } 641 642 /** 643 * A special case of {@code checkCapability} which re-encodes both sets of signing 644 * certificates to counteract a previous re-encoding. 645 */ checkCapabilityRecover(@onNull SigningDetails oldDetails, @CertCapabilities int flags)646 public boolean checkCapabilityRecover(@NonNull SigningDetails oldDetails, 647 @CertCapabilities int flags) throws CertificateException { 648 if (oldDetails == UNKNOWN || this == UNKNOWN) { 649 return false; 650 } 651 if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) { 652 // signing certificates may have rotated, check entire history for effective match 653 for (int i = 0; i < mPastSigningCertificates.length; i++) { 654 if (Signature.areEffectiveMatch( 655 oldDetails.mSignatures[0], 656 mPastSigningCertificates[i]) 657 && mPastSigningCertificates[i].getFlags() == flags) { 658 return true; 659 } 660 } 661 } else { 662 return Signature.areEffectiveMatch(oldDetails, this); 663 } 664 return false; 665 } 666 667 /** 668 * Determine if {@code signature} is in this SigningDetails' signing certificate history, 669 * including the current signer. Automatically returns false if this object has multiple 670 * signing certificates, since rotation is only supported for single-signers; this is 671 * enforced by {@code hasCertificateInternal}. 672 */ hasCertificate(@onNull Signature signature)673 public boolean hasCertificate(@NonNull Signature signature) { 674 return hasCertificateInternal(signature, PAST_CERT_EXISTS); 675 } 676 677 /** 678 * Determine if {@code signature} is in this SigningDetails' signing certificate history, 679 * including the current signer, and whether or not it has the given permission. 680 * Certificates which match our current signer automatically get all capabilities. 681 * Automatically returns false if this object has multiple signing certificates, since 682 * rotation is only supported for single-signers. 683 */ hasCertificate(@onNull Signature signature, @CertCapabilities int flags)684 public boolean hasCertificate(@NonNull Signature signature, @CertCapabilities int flags) { 685 return hasCertificateInternal(signature, flags); 686 } 687 688 /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */ hasCertificate(byte[] certificate)689 public boolean hasCertificate(byte[] certificate) { 690 Signature signature = new Signature(certificate); 691 return hasCertificate(signature); 692 } 693 hasCertificateInternal(@onNull Signature signature, int flags)694 private boolean hasCertificateInternal(@NonNull Signature signature, int flags) { 695 if (this == UNKNOWN) { 696 return false; 697 } 698 699 // only single-signed apps can have pastSigningCertificates 700 if (hasPastSigningCertificates()) { 701 // check all past certs, except for the current one, which automatically gets all 702 // capabilities, since it is the same as the current signature 703 for (int i = 0; i < mPastSigningCertificates.length - 1; i++) { 704 if (mPastSigningCertificates[i].equals(signature)) { 705 if (flags == PAST_CERT_EXISTS 706 || (flags & mPastSigningCertificates[i].getFlags()) == flags) { 707 return true; 708 } 709 } 710 } 711 } 712 713 // not in previous certs signing history, just check the current signer and make sure 714 // we are singly-signed 715 return mSignatures.length == 1 && mSignatures[0].equals(signature); 716 } 717 718 /** 719 * Determines if the provided {@code sha256String} is an ancestor of this one, and whether 720 * or not this one grants it the provided capability, represented by the {@code flags} 721 * parameter. In the event of signing certificate rotation, a package may still interact 722 * with entities signed by its old signing certificate and not want to break previously 723 * functioning behavior. The {@code flags} value determines which capabilities the app 724 * signed by the newer signing certificate would like to continue to give to its previous 725 * signing certificate(s). 726 * 727 * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an 728 * app with multiple signers, this represents the hex-encoded sha256 729 * digest of the combined hex-encoded sha256 digests of each individual 730 * signing certificate according to {@link 731 * PackageUtils#computeSignaturesSha256Digest(Signature[])} 732 */ checkCapability(@ullable String sha256String, @CertCapabilities int flags)733 public boolean checkCapability(@Nullable String sha256String, @CertCapabilities int flags) { 734 if (this == UNKNOWN || TextUtils.isEmpty(sha256String)) { 735 return false; 736 } 737 738 // first see if the hash represents a single-signer in our signing history 739 final byte[] sha256Bytes = HexEncoding.decode(sha256String, false /* allowSingleChar */); 740 if (hasSha256Certificate(sha256Bytes, flags)) { 741 return true; 742 } 743 744 // Not in signing history, either represents multiple signatures or not a match. 745 // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match. 746 // We already check the single-signer case above as part of hasSha256Certificate, so no 747 // need to verify we have multiple signers, just run the old check 748 // just consider current signing certs 749 final String[] mSignaturesSha256Digests = 750 PackageUtils.computeSignaturesSha256Digests(mSignatures); 751 final String mSignaturesSha256Digest = 752 PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests); 753 return mSignaturesSha256Digest.equals(sha256String); 754 } 755 756 /** 757 * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate 758 * history, including the current signer. Automatically returns false if this object has 759 * multiple signing certificates, since rotation is only supported for single-signers. 760 */ hasSha256Certificate(byte[] sha256Certificate)761 public boolean hasSha256Certificate(byte[] sha256Certificate) { 762 return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS); 763 } 764 765 /** 766 * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing 767 * certificate in this SigningDetails' signing certificate history, including the current 768 * signer, and whether or not it has the given permission. Certificates which match our 769 * current signer automatically get all capabilities. Automatically returns false if this 770 * object has multiple signing certificates, since rotation is only supported for 771 * single-signers. 772 */ hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags)773 public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) { 774 return hasSha256CertificateInternal(sha256Certificate, flags); 775 } 776 hasSha256CertificateInternal(byte[] sha256Certificate, int flags)777 private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) { 778 if (this == UNKNOWN) { 779 return false; 780 } 781 if (hasPastSigningCertificates()) { 782 // check all past certs, except for the last one, which automatically gets all 783 // capabilities, since it is the same as the current signature, and is checked below 784 for (int i = 0; i < mPastSigningCertificates.length - 1; i++) { 785 byte[] digest = PackageUtils.computeSha256DigestBytes( 786 mPastSigningCertificates[i].toByteArray()); 787 if (Arrays.equals(sha256Certificate, digest)) { 788 if (flags == PAST_CERT_EXISTS 789 || (flags & mPastSigningCertificates[i].getFlags()) == flags) { 790 return true; 791 } 792 } 793 } 794 } 795 796 // not in previous certs signing history, just check the current signer 797 if (mSignatures.length == 1) { 798 byte[] digest = PackageUtils.computeSha256DigestBytes(mSignatures[0].toByteArray()); 799 return Arrays.equals(sha256Certificate, digest); 800 } 801 return false; 802 } 803 804 /** Returns true if the signatures in this and other match exactly. */ signaturesMatchExactly(@onNull SigningDetails other)805 public boolean signaturesMatchExactly(@NonNull SigningDetails other) { 806 return Signature.areExactMatch(this, other); 807 } 808 809 @Override describeContents()810 public int describeContents() { 811 return 0; 812 } 813 814 @Override writeToParcel(@onNull Parcel dest, int flags)815 public void writeToParcel(@NonNull Parcel dest, int flags) { 816 boolean isUnknown = UNKNOWN == this; 817 dest.writeBoolean(isUnknown); 818 if (isUnknown) { 819 return; 820 } 821 dest.writeTypedArray(mSignatures, flags); 822 dest.writeInt(mSignatureSchemeVersion); 823 dest.writeArraySet(mPublicKeys); 824 dest.writeTypedArray(mPastSigningCertificates, flags); 825 } 826 SigningDetails(@onNull Parcel in)827 protected SigningDetails(@NonNull Parcel in) { 828 final ClassLoader boot = Object.class.getClassLoader(); 829 mSignatures = in.createTypedArray(Signature.CREATOR); 830 mSignatureSchemeVersion = in.readInt(); 831 mPublicKeys = (ArraySet<PublicKey>) in.readArraySet(boot); 832 mPastSigningCertificates = in.createTypedArray(Signature.CREATOR); 833 } 834 835 public static final @NonNull Parcelable.Creator<SigningDetails> CREATOR = 836 new Creator<SigningDetails>() { 837 @Override 838 public SigningDetails createFromParcel(@NonNull Parcel source) { 839 if (source.readBoolean()) { 840 return UNKNOWN; 841 } 842 return new SigningDetails(source); 843 } 844 845 @Override 846 public SigningDetails[] newArray(int size) { 847 return new SigningDetails[size]; 848 } 849 }; 850 851 @Override equals(@ullable Object o)852 public boolean equals(@Nullable Object o) { 853 if (this == o) return true; 854 if (!(o instanceof SigningDetails)) return false; 855 856 final SigningDetails that = (SigningDetails) o; 857 858 if (mSignatureSchemeVersion != that.mSignatureSchemeVersion) return false; 859 if (!Signature.areExactMatch(this, that)) return false; 860 if (mPublicKeys != null) { 861 if (!mPublicKeys.equals((that.mPublicKeys))) { 862 return false; 863 } 864 } else if (that.mPublicKeys != null) { 865 return false; 866 } 867 868 // can't use Signature.areExactMatch() because order matters with the past signing certs 869 if (!Arrays.equals(mPastSigningCertificates, that.mPastSigningCertificates)) { 870 return false; 871 } 872 // The capabilities for the past signing certs must match as well. 873 if (mPastSigningCertificates != null) { 874 for (int i = 0; i < mPastSigningCertificates.length; i++) { 875 if (mPastSigningCertificates[i].getFlags() 876 != that.mPastSigningCertificates[i].getFlags()) { 877 return false; 878 } 879 } 880 } 881 return true; 882 } 883 884 @Override hashCode()885 public int hashCode() { 886 int result = +Arrays.hashCode(mSignatures); 887 result = 31 * result + mSignatureSchemeVersion; 888 result = 31 * result + (mPublicKeys != null ? mPublicKeys.hashCode() : 0); 889 result = 31 * result + Arrays.hashCode(mPastSigningCertificates); 890 return result; 891 } 892 893 /** 894 * Builder of {@code SigningDetails} instances. 895 */ 896 public static class Builder { 897 private @NonNull Signature[] mSignatures; 898 private @SignatureSchemeVersion int mSignatureSchemeVersion = 899 SignatureSchemeVersion.UNKNOWN; 900 private @Nullable Signature[] mPastSigningCertificates; 901 Builder()902 public Builder() { 903 } 904 905 /** get signing certificates used to sign the current APK */ setSignatures(@onNull Signature[] signatures)906 public SigningDetails.Builder setSignatures(@NonNull Signature[] signatures) { 907 mSignatures = signatures; 908 return this; 909 } 910 911 /** set the signature scheme version used to sign the APK */ setSignatureSchemeVersion( @ignatureSchemeVersion int signatureSchemeVersion)912 public SigningDetails.Builder setSignatureSchemeVersion( 913 @SignatureSchemeVersion int signatureSchemeVersion) { 914 mSignatureSchemeVersion = signatureSchemeVersion; 915 return this; 916 } 917 918 /** set the signing certificates by which the APK proved it can be authenticated */ setPastSigningCertificates( @ullable Signature[] pastSigningCertificates)919 public SigningDetails.Builder setPastSigningCertificates( 920 @Nullable Signature[] pastSigningCertificates) { 921 mPastSigningCertificates = pastSigningCertificates; 922 return this; 923 } 924 checkInvariants()925 private void checkInvariants() { 926 // must have signatures and scheme version set 927 if (mSignatures == null) { 928 throw new IllegalStateException("SigningDetails requires the current signing" 929 + " certificates."); 930 } 931 } 932 /** build a {@code SigningDetails} object */ build()933 public SigningDetails build() 934 throws CertificateException { 935 checkInvariants(); 936 return new SigningDetails(mSignatures, mSignatureSchemeVersion, 937 mPastSigningCertificates); 938 } 939 } 940 941 /** Parses the public keys from the set of signatures. */ toSigningKeys(@onNull Signature[] signatures)942 public static ArraySet<PublicKey> toSigningKeys(@NonNull Signature[] signatures) 943 throws CertificateException { 944 final ArraySet<PublicKey> keys = new ArraySet<>(signatures.length); 945 for (int i = 0; i < signatures.length; i++) { 946 keys.add(signatures[i].getPublicKey()); 947 } 948 return keys; 949 } 950 951 952 953 // Code below generated by codegen v1.0.23. 954 // 955 // DO NOT MODIFY! 956 // CHECKSTYLE:OFF Generated code 957 // 958 // To regenerate run: 959 // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/SigningDetails.java 960 // 961 // To exclude the generated code from IntelliJ auto-formatting enable (one-time): 962 // Settings > Editor > Code Style > Formatter Control 963 //@formatter:off 964 965 966 /** 967 * The signing certificates associated with this application package. 968 */ 969 @DataClass.Generated.Member getSignatures()970 public @Nullable Signature[] getSignatures() { 971 return mSignatures; 972 } 973 974 /** 975 * The signature scheme version for this application package. 976 */ 977 @DataClass.Generated.Member getSignatureSchemeVersion()978 public @SignatureSchemeVersion int getSignatureSchemeVersion() { 979 return mSignatureSchemeVersion; 980 } 981 982 /** 983 * The public keys set for the certificates. 984 */ 985 @DataClass.Generated.Member getPublicKeys()986 public @Nullable ArraySet<PublicKey> getPublicKeys() { 987 return mPublicKeys; 988 } 989 990 /** 991 * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that 992 * contains two pieces of information: 993 * 1) the past signing certificates 994 * 2) the flags that APK wants to assign to each of the past signing certificates. 995 * 996 * This collection of {@code Signature} objects, each of which is formed from a former 997 * signing certificate of this APK before it was changed by signing certificate rotation, 998 * represents the first piece of information. It is the APK saying to the rest of the 999 * world: "hey if you trust the old cert, you can trust me!" This is useful, if for 1000 * instance, the platform would like to determine whether or not to allow this APK to do 1001 * something it would've allowed it to do under the old cert (like upgrade). 1002 */ 1003 @DataClass.Generated.Member getPastSigningCertificates()1004 public @Nullable Signature[] getPastSigningCertificates() { 1005 return mPastSigningCertificates; 1006 } 1007 1008 @DataClass.Generated( 1009 time = 1650058974710L, 1010 codegenVersion = "1.0.23", 1011 sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java", 1012 inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails,int)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails,int)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)") 1013 @Deprecated __metadata()1014 private void __metadata() {} 1015 1016 1017 //@formatter:on 1018 // End of generated code 1019 1020 } 1021