• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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