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