1 /* 2 * Copyright (C) 2017 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.util.apk; 18 19 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; 20 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; 21 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; 22 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; 23 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; 24 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 25 26 import android.content.pm.PackageParser; 27 import android.content.pm.PackageParser.PackageParserException; 28 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; 29 import android.content.pm.Signature; 30 import android.content.pm.parsing.ParsingPackageUtils; 31 import android.os.Build; 32 import android.os.Trace; 33 import android.util.jar.StrictJarFile; 34 35 import com.android.internal.util.ArrayUtils; 36 37 import libcore.io.IoUtils; 38 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.security.DigestException; 42 import java.security.GeneralSecurityException; 43 import java.security.NoSuchAlgorithmException; 44 import java.security.cert.Certificate; 45 import java.security.cert.CertificateEncodingException; 46 import java.util.ArrayList; 47 import java.util.Iterator; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.concurrent.atomic.AtomicReference; 51 import java.util.zip.ZipEntry; 52 53 /** 54 * Facade class that takes care of the details of APK verification on 55 * behalf of PackageParser. 56 * 57 * @hide for internal use only. 58 */ 59 public class ApkSignatureVerifier { 60 61 private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>(); 62 63 /** 64 * Verifies the provided APK and returns the certificates associated with each signer. 65 * 66 * @throws PackageParserException if the APK's signature failed to verify. 67 */ verify(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion)68 public static PackageParser.SigningDetails verify(String apkPath, 69 @SignatureSchemeVersion int minSignatureSchemeVersion) 70 throws PackageParserException { 71 return verifySignatures(apkPath, minSignatureSchemeVersion, true); 72 } 73 74 /** 75 * Returns the certificates associated with each signer for the given APK without verification. 76 * This method is dangerous and should not be used, unless the caller is absolutely certain the 77 * APK is trusted. 78 * 79 * @throws PackageParserException if there was a problem collecting certificates. 80 */ unsafeGetCertsWithoutVerification( String apkPath, int minSignatureSchemeVersion)81 public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification( 82 String apkPath, int minSignatureSchemeVersion) 83 throws PackageParserException { 84 return verifySignatures(apkPath, minSignatureSchemeVersion, false); 85 } 86 87 /** 88 * Verifies the provided APK using all allowed signing schemas. 89 * @return the certificates associated with each signer. 90 * @param verifyFull whether to verify all contents of this APK or just collect certificates. 91 * @throws PackageParserException if there was a problem collecting certificates 92 */ verifySignatures(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)93 private static PackageParser.SigningDetails verifySignatures(String apkPath, 94 @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) 95 throws PackageParserException { 96 return verifySignaturesInternal(apkPath, minSignatureSchemeVersion, 97 verifyFull).signingDetails; 98 } 99 100 /** 101 * Verifies the provided APK using all allowed signing schemas. 102 * @return the certificates associated with each signer and content digests. 103 * @param verifyFull whether to verify all contents of this APK or just collect certificates. 104 * @throws PackageParserException if there was a problem collecting certificates 105 * @hide 106 */ verifySignaturesInternal(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)107 public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath, 108 @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) 109 throws PackageParserException { 110 111 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) { 112 // V3 and before are older than the requested minimum signing version 113 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 114 "No signature found in package of version " + minSignatureSchemeVersion 115 + " or newer for package " + apkPath); 116 } 117 118 // first try v4 119 try { 120 return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull); 121 } catch (SignatureNotFoundException e) { 122 // not signed with v4, try older if allowed 123 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) { 124 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 125 "No APK Signature Scheme v4 signature in package " + apkPath, e); 126 } 127 } 128 129 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { 130 // V3 and before are older than the requested minimum signing version 131 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 132 "No signature found in package of version " + minSignatureSchemeVersion 133 + " or newer for package " + apkPath); 134 } 135 136 return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull); 137 } 138 verifyV3AndBelowSignatures(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)139 private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath, 140 @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) 141 throws PackageParserException { 142 // try v3 143 try { 144 return verifyV3Signature(apkPath, verifyFull); 145 } catch (SignatureNotFoundException e) { 146 // not signed with v3, try older if allowed 147 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) { 148 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 149 "No APK Signature Scheme v3 signature in package " + apkPath, e); 150 } 151 } 152 153 // redundant, protective version check 154 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) { 155 // V2 and before are older than the requested minimum signing version 156 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 157 "No signature found in package of version " + minSignatureSchemeVersion 158 + " or newer for package " + apkPath); 159 } 160 161 // try v2 162 try { 163 return verifyV2Signature(apkPath, verifyFull); 164 } catch (SignatureNotFoundException e) { 165 // not signed with v2, try older if allowed 166 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) { 167 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 168 "No APK Signature Scheme v2 signature in package " + apkPath, e); 169 } 170 } 171 172 // redundant, protective version check 173 if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) { 174 // V1 and is older than the requested minimum signing version 175 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 176 "No signature found in package of version " + minSignatureSchemeVersion 177 + " or newer for package " + apkPath); 178 } 179 180 // v2 didn't work, try jarsigner 181 return verifyV1Signature(apkPath, verifyFull); 182 } 183 184 /** 185 * Verifies the provided APK using V4 schema. 186 * 187 * @param verifyFull whether to verify (V4 vs V3) or just collect certificates. 188 * @return the certificates associated with each signer. 189 * @throws SignatureNotFoundException if there are no V4 signatures in the APK 190 * @throws PackageParserException if there was a problem collecting certificates 191 */ verifyV4Signature(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)192 private static SigningDetailsWithDigests verifyV4Signature(String apkPath, 193 @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) 194 throws SignatureNotFoundException, PackageParserException { 195 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4"); 196 try { 197 ApkSignatureSchemeV4Verifier.VerifiedSigner vSigner = 198 ApkSignatureSchemeV4Verifier.extractCertificates(apkPath); 199 Certificate[][] signerCerts = new Certificate[][]{vSigner.certs}; 200 Signature[] signerSigs = convertToSignatures(signerCerts); 201 Signature[] pastSignerSigs = null; 202 203 if (verifyFull) { 204 Map<Integer, byte[]> nonstreamingDigests; 205 Certificate[][] nonstreamingCerts; 206 207 try { 208 // v4 is an add-on and requires v2 or v3 signature to validate against its 209 // certificate and digest 210 ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer = 211 ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath); 212 nonstreamingDigests = v3Signer.contentDigests; 213 nonstreamingCerts = new Certificate[][]{v3Signer.certs}; 214 if (v3Signer.por != null) { 215 // populate proof-of-rotation information 216 pastSignerSigs = new Signature[v3Signer.por.certs.size()]; 217 for (int i = 0; i < pastSignerSigs.length; i++) { 218 pastSignerSigs[i] = new Signature( 219 v3Signer.por.certs.get(i).getEncoded()); 220 pastSignerSigs[i].setFlags(v3Signer.por.flagsList.get(i)); 221 } 222 } 223 } catch (SignatureNotFoundException e) { 224 try { 225 ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer = 226 ApkSignatureSchemeV2Verifier.verify(apkPath, false); 227 nonstreamingDigests = v2Signer.contentDigests; 228 nonstreamingCerts = v2Signer.certs; 229 } catch (SignatureNotFoundException ee) { 230 throw new SecurityException( 231 "V4 verification failed to collect V2/V3 certificates from : " 232 + apkPath, ee); 233 } 234 } 235 236 Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); 237 if (nonstreamingSigs.length != signerSigs.length) { 238 throw new SecurityException( 239 "Invalid number of certificates: " + nonstreamingSigs.length); 240 } 241 242 for (int i = 0, size = signerSigs.length; i < size; ++i) { 243 if (!nonstreamingSigs[i].equals(signerSigs[i])) { 244 throw new SecurityException( 245 "V4 signature certificate does not match V2/V3"); 246 } 247 } 248 249 boolean found = false; 250 for (byte[] nonstreamingDigest : nonstreamingDigests.values()) { 251 if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest, 252 vSigner.apkDigest.length)) { 253 found = true; 254 break; 255 } 256 } 257 if (!found) { 258 throw new SecurityException("APK digest in V4 signature does not match V2/V3"); 259 } 260 } 261 262 return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs, 263 SignatureSchemeVersion.SIGNING_BLOCK_V4, pastSignerSigs), 264 vSigner.contentDigests); 265 } catch (SignatureNotFoundException e) { 266 throw e; 267 } catch (Exception e) { 268 // APK Signature Scheme v4 signature found but did not verify 269 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 270 "Failed to collect certificates from " + apkPath 271 + " using APK Signature Scheme v4", e); 272 } finally { 273 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 274 } 275 } 276 277 /** 278 * Verifies the provided APK using V3 schema. 279 * 280 * @param verifyFull whether to verify all contents of this APK or just collect certificates. 281 * @return the certificates associated with each signer. 282 * @throws SignatureNotFoundException if there are no V3 signatures in the APK 283 * @throws PackageParserException if there was a problem collecting certificates 284 */ verifyV3Signature(String apkPath, boolean verifyFull)285 private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull) 286 throws SignatureNotFoundException, PackageParserException { 287 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3"); 288 try { 289 ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner = 290 verifyFull ? ApkSignatureSchemeV3Verifier.verify(apkPath) 291 : ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification( 292 apkPath); 293 Certificate[][] signerCerts = new Certificate[][]{vSigner.certs}; 294 Signature[] signerSigs = convertToSignatures(signerCerts); 295 Signature[] pastSignerSigs = null; 296 if (vSigner.por != null) { 297 // populate proof-of-rotation information 298 pastSignerSigs = new Signature[vSigner.por.certs.size()]; 299 for (int i = 0; i < pastSignerSigs.length; i++) { 300 pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded()); 301 pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i)); 302 } 303 } 304 return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs, 305 SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs), 306 vSigner.contentDigests); 307 } catch (SignatureNotFoundException e) { 308 throw e; 309 } catch (Exception e) { 310 // APK Signature Scheme v3 signature found but did not verify 311 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 312 "Failed to collect certificates from " + apkPath 313 + " using APK Signature Scheme v3", e); 314 } finally { 315 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 316 } 317 } 318 319 /** 320 * Verifies the provided APK using V2 schema. 321 * 322 * @param verifyFull whether to verify all contents of this APK or just collect certificates. 323 * @return the certificates associated with each signer. 324 * @throws SignatureNotFoundException if there are no V2 signatures in the APK 325 * @throws PackageParserException if there was a problem collecting certificates 326 */ verifyV2Signature(String apkPath, boolean verifyFull)327 private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull) 328 throws SignatureNotFoundException, PackageParserException { 329 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2"); 330 try { 331 ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner = 332 ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull); 333 Certificate[][] signerCerts = vSigner.certs; 334 Signature[] signerSigs = convertToSignatures(signerCerts); 335 return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs, 336 SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests); 337 } catch (SignatureNotFoundException e) { 338 throw e; 339 } catch (Exception e) { 340 // APK Signature Scheme v2 signature found but did not verify 341 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 342 "Failed to collect certificates from " + apkPath 343 + " using APK Signature Scheme v2", e); 344 } finally { 345 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 346 } 347 } 348 349 /** 350 * Verifies the provided APK using JAR schema. 351 * @return the certificates associated with each signer. 352 * @param verifyFull whether to verify all contents of this APK or just collect certificates. 353 * @throws PackageParserException if there was a problem collecting certificates 354 */ verifyV1Signature(String apkPath, boolean verifyFull)355 private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull) 356 throws PackageParserException { 357 StrictJarFile jarFile = null; 358 359 try { 360 final Certificate[][] lastCerts; 361 final Signature[] lastSigs; 362 363 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor"); 364 365 // we still pass verify = true to ctor to collect certs, even though we're not checking 366 // the whole jar. 367 jarFile = new StrictJarFile( 368 apkPath, 369 true, // collect certs 370 verifyFull); // whether to reject APK with stripped v2 signatures (b/27887819) 371 final List<ZipEntry> toVerify = new ArrayList<>(); 372 373 // Gather certs from AndroidManifest.xml, which every APK must have, as an optimization 374 // to not need to verify the whole APK when verifyFUll == false. 375 final ZipEntry manifestEntry = jarFile.findEntry( 376 ParsingPackageUtils.ANDROID_MANIFEST_FILENAME); 377 if (manifestEntry == null) { 378 throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, 379 "Package " + apkPath + " has no manifest"); 380 } 381 lastCerts = loadCertificates(jarFile, manifestEntry); 382 if (ArrayUtils.isEmpty(lastCerts)) { 383 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package " 384 + apkPath + " has no certificates at entry " 385 + ParsingPackageUtils.ANDROID_MANIFEST_FILENAME); 386 } 387 lastSigs = convertToSignatures(lastCerts); 388 389 // fully verify all contents, except for AndroidManifest.xml and the META-INF/ files. 390 if (verifyFull) { 391 final Iterator<ZipEntry> i = jarFile.iterator(); 392 while (i.hasNext()) { 393 final ZipEntry entry = i.next(); 394 if (entry.isDirectory()) continue; 395 396 final String entryName = entry.getName(); 397 if (entryName.startsWith("META-INF/")) continue; 398 if (entryName.equals(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME)) continue; 399 400 toVerify.add(entry); 401 } 402 403 for (ZipEntry entry : toVerify) { 404 final Certificate[][] entryCerts = loadCertificates(jarFile, entry); 405 if (ArrayUtils.isEmpty(entryCerts)) { 406 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 407 "Package " + apkPath + " has no certificates at entry " 408 + entry.getName()); 409 } 410 411 // make sure all entries use the same signing certs 412 final Signature[] entrySigs = convertToSignatures(entryCerts); 413 if (!Signature.areExactMatch(lastSigs, entrySigs)) { 414 throw new PackageParserException( 415 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, 416 "Package " + apkPath + " has mismatched certificates at entry " 417 + entry.getName()); 418 } 419 } 420 } 421 return new SigningDetailsWithDigests( 422 new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null); 423 } catch (GeneralSecurityException e) { 424 throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, 425 "Failed to collect certificates from " + apkPath, e); 426 } catch (IOException | RuntimeException e) { 427 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 428 "Failed to collect certificates from " + apkPath, e); 429 } finally { 430 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 431 closeQuietly(jarFile); 432 } 433 } 434 loadCertificates(StrictJarFile jarFile, ZipEntry entry)435 private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry) 436 throws PackageParserException { 437 InputStream is = null; 438 try { 439 // We must read the stream for the JarEntry to retrieve 440 // its certificates. 441 is = jarFile.getInputStream(entry); 442 readFullyIgnoringContents(is); 443 return jarFile.getCertificateChains(entry); 444 } catch (IOException | RuntimeException e) { 445 throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 446 "Failed reading " + entry.getName() + " in " + jarFile, e); 447 } finally { 448 IoUtils.closeQuietly(is); 449 } 450 } 451 readFullyIgnoringContents(InputStream in)452 private static void readFullyIgnoringContents(InputStream in) throws IOException { 453 byte[] buffer = sBuffer.getAndSet(null); 454 if (buffer == null) { 455 buffer = new byte[4096]; 456 } 457 458 int n = 0; 459 int count = 0; 460 while ((n = in.read(buffer, 0, buffer.length)) != -1) { 461 count += n; 462 } 463 464 sBuffer.set(buffer); 465 return; 466 } 467 468 /** 469 * Converts an array of certificate chains into the {@code Signature} equivalent used by the 470 * PackageManager. 471 * 472 * @throws CertificateEncodingException if it is unable to create a Signature object. 473 */ convertToSignatures(Certificate[][] certs)474 private static Signature[] convertToSignatures(Certificate[][] certs) 475 throws CertificateEncodingException { 476 final Signature[] res = new Signature[certs.length]; 477 for (int i = 0; i < certs.length; i++) { 478 res[i] = new Signature(certs[i]); 479 } 480 return res; 481 } 482 closeQuietly(StrictJarFile jarFile)483 private static void closeQuietly(StrictJarFile jarFile) { 484 if (jarFile != null) { 485 try { 486 jarFile.close(); 487 } catch (Exception ignored) { 488 } 489 } 490 } 491 492 /** 493 * Returns the minimum signature scheme version required for an app targeting the specified 494 * {@code targetSdk}. 495 */ getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk)496 public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) { 497 if (targetSdk >= Build.VERSION_CODES.R) { 498 return SignatureSchemeVersion.SIGNING_BLOCK_V2; 499 } 500 return SignatureSchemeVersion.JAR; 501 } 502 503 /** 504 * Result of a successful APK verification operation. 505 */ 506 public static class Result { 507 public final Certificate[][] certs; 508 public final Signature[] sigs; 509 public final int signatureSchemeVersion; 510 Result(Certificate[][] certs, Signature[] sigs, int signingVersion)511 public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) { 512 this.certs = certs; 513 this.sigs = sigs; 514 this.signatureSchemeVersion = signingVersion; 515 } 516 } 517 518 /** 519 * @return the verity root hash in the Signing Block. 520 */ getVerityRootHash(String apkPath)521 public static byte[] getVerityRootHash(String apkPath) throws IOException, SecurityException { 522 // first try v3 523 try { 524 return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath); 525 } catch (SignatureNotFoundException e) { 526 // try older version 527 } 528 try { 529 return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath); 530 } catch (SignatureNotFoundException e) { 531 return null; 532 } 533 } 534 535 /** 536 * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code 537 * ByteBufferFactory}. 538 * 539 * @return the verity root hash of the generated Merkle tree. 540 */ generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)541 public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) 542 throws IOException, SignatureNotFoundException, SecurityException, DigestException, 543 NoSuchAlgorithmException { 544 // first try v3 545 try { 546 return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory); 547 } catch (SignatureNotFoundException e) { 548 // try older version 549 } 550 return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory); 551 } 552 553 /** 554 * Generates the FSVerity root hash from FSVerity header, extensions and Merkle tree root hash 555 * in Signing Block. 556 * 557 * @return FSverity root hash 558 */ generateApkVerityRootHash(String apkPath)559 public static byte[] generateApkVerityRootHash(String apkPath) 560 throws NoSuchAlgorithmException, DigestException, IOException { 561 // first try v3 562 try { 563 return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath); 564 } catch (SignatureNotFoundException e) { 565 // try older version 566 } 567 try { 568 return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath); 569 } catch (SignatureNotFoundException e) { 570 return null; 571 } 572 } 573 574 /** 575 * Extended signing details. 576 * @hide for internal use only. 577 */ 578 public static class SigningDetailsWithDigests { 579 public final PackageParser.SigningDetails signingDetails; 580 581 /** 582 * APK Signature Schemes v2/v3/v4 might contain multiple content digests. 583 * SignatureVerifier usually chooses one of them to verify. 584 * For certain signature schemes, e.g. v4, this digest is verified continuously. 585 * For others, e.g. v2, the caller has to specify if they want to verify. 586 * Please refer to documentation for more details. 587 */ 588 public final Map<Integer, byte[]> contentDigests; 589 SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails, Map<Integer, byte[]> contentDigests)590 SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails, 591 Map<Integer, byte[]> contentDigests) { 592 this.signingDetails = signingDetails; 593 this.contentDigests = contentDigests; 594 } 595 } 596 } 597