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