blob: de9f55b092004a382bfd6fedc34e59303733f6e8 [file] [log] [blame]
Dan Cashmane92f8422017-12-08 14:02:51 -08001/*
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
17package android.util.apk;
18
19import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
20import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
21import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
22import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
23import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
24import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
25
26import android.content.pm.PackageParser;
27import android.content.pm.PackageParser.PackageParserException;
Patrick Baumann420d58a2017-12-19 10:17:21 -080028import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
Dan Cashmane92f8422017-12-08 14:02:51 -080029import android.content.pm.Signature;
30import android.os.Trace;
31import android.util.jar.StrictJarFile;
32
33import com.android.internal.util.ArrayUtils;
34
35import libcore.io.IoUtils;
36
37import java.io.IOException;
38import java.io.InputStream;
Victor Hsieh07bc80c2018-01-11 16:15:47 -080039import java.security.DigestException;
Dan Cashmane92f8422017-12-08 14:02:51 -080040import java.security.GeneralSecurityException;
Victor Hsieh07bc80c2018-01-11 16:15:47 -080041import java.security.NoSuchAlgorithmException;
Dan Cashmane92f8422017-12-08 14:02:51 -080042import java.security.cert.Certificate;
43import java.security.cert.CertificateEncodingException;
44import java.util.ArrayList;
45import java.util.Iterator;
46import java.util.List;
47import java.util.concurrent.atomic.AtomicReference;
48import java.util.zip.ZipEntry;
49
50/**
51 * Facade class that takes care of the details of APK verification on
52 * behalf of PackageParser.
53 *
54 * @hide for internal use only.
55 */
56public class ApkSignatureVerifier {
57
Dan Cashmane92f8422017-12-08 14:02:51 -080058 private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>();
59
60 /**
Dan Cashman636ea5e2017-12-18 10:38:20 -080061 * Verifies the provided APK and returns the certificates associated with each signer.
Dan Cashmane92f8422017-12-08 14:02:51 -080062 *
63 * @throws PackageParserException if the APK's signature failed to verify.
Dan Cashmane92f8422017-12-08 14:02:51 -080064 */
Patrick Baumann420d58a2017-12-19 10:17:21 -080065 public static PackageParser.SigningDetails verify(String apkPath,
66 @SignatureSchemeVersion int minSignatureSchemeVersion)
Patrick Baumann9ff55742017-12-14 10:50:18 -080067 throws PackageParserException {
Dan Cashmane92f8422017-12-08 14:02:51 -080068
Patrick Baumann420d58a2017-12-19 10:17:21 -080069 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
Daniel Cashman67096e02017-12-28 12:46:33 -080070 // V3 and before are older than the requested minimum signing version
71 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
72 "No signature found in package of version " + minSignatureSchemeVersion
73 + " or newer for package " + apkPath);
74 }
75
76 // first try v3
77 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
78 try {
79 ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
80 ApkSignatureSchemeV3Verifier.verify(apkPath);
81 Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
82 Signature[] signerSigs = convertToSignatures(signerCerts);
Daniel Cashman77029c52018-01-18 16:19:29 -080083 Signature[] pastSignerSigs = null;
84 int[] pastSignerSigsFlags = null;
85 if (vSigner.por != null) {
86 // populate proof-of-rotation information
87 pastSignerSigs = new Signature[vSigner.por.certs.size()];
88 pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
89 for (int i = 0; i < pastSignerSigs.length; i++) {
90 pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
91 pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
92 }
93 }
94 return new PackageParser.SigningDetails(
95 signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
96 pastSignerSigs, pastSignerSigsFlags);
Daniel Cashman67096e02017-12-28 12:46:33 -080097 } catch (SignatureNotFoundException e) {
Daniel Cashman77029c52018-01-18 16:19:29 -080098 // not signed with v3, try older if allowed
Patrick Baumann420d58a2017-12-19 10:17:21 -080099 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800100 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
101 "No APK Signature Scheme v3 signature in package " + apkPath, e);
102 }
103 } catch (Exception e) {
104 // APK Signature Scheme v2 signature found but did not verify
105 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
106 "Failed to collect certificates from " + apkPath
Daniel Cashman77029c52018-01-18 16:19:29 -0800107 + " using APK Signature Scheme v3", e);
Daniel Cashman67096e02017-12-28 12:46:33 -0800108 } finally {
109 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
110 }
111
112 // redundant, protective version check
Patrick Baumann420d58a2017-12-19 10:17:21 -0800113 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800114 // V2 and before are older than the requested minimum signing version
115 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
116 "No signature found in package of version " + minSignatureSchemeVersion
117 + " or newer for package " + apkPath);
118 }
119
120 // try v2
Dan Cashmane92f8422017-12-08 14:02:51 -0800121 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
122 try {
Dan Cashman636ea5e2017-12-18 10:38:20 -0800123 Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
Dan Cashmane92f8422017-12-08 14:02:51 -0800124 Signature[] signerSigs = convertToSignatures(signerCerts);
125
Patrick Baumann420d58a2017-12-19 10:17:21 -0800126 return new PackageParser.SigningDetails(
127 signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
Dan Cashmane92f8422017-12-08 14:02:51 -0800128 } catch (SignatureNotFoundException e) {
129 // not signed with v2, try older if allowed
Patrick Baumann420d58a2017-12-19 10:17:21 -0800130 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Patrick Baumann9ff55742017-12-14 10:50:18 -0800131 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
132 "No APK Signature Scheme v2 signature in package " + apkPath, e);
Dan Cashmane92f8422017-12-08 14:02:51 -0800133 }
Dan Cashmane92f8422017-12-08 14:02:51 -0800134 } catch (Exception e) {
135 // APK Signature Scheme v2 signature found but did not verify
136 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
137 "Failed to collect certificates from " + apkPath
138 + " using APK Signature Scheme v2", e);
139 } finally {
140 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
141 }
142
Daniel Cashman67096e02017-12-28 12:46:33 -0800143 // redundant, protective version check
Patrick Baumann420d58a2017-12-19 10:17:21 -0800144 if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800145 // V1 and is older than the requested minimum signing version
146 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
147 "No signature found in package of version " + minSignatureSchemeVersion
148 + " or newer for package " + apkPath);
149 }
150
Dan Cashmane92f8422017-12-08 14:02:51 -0800151 // v2 didn't work, try jarsigner
Dan Cashman636ea5e2017-12-18 10:38:20 -0800152 return verifyV1Signature(apkPath, true);
Dan Cashmane92f8422017-12-08 14:02:51 -0800153 }
154
Dan Cashman636ea5e2017-12-18 10:38:20 -0800155 /**
156 * Verifies the provided APK and returns the certificates associated with each signer.
157 *
158 * @param verifyFull whether to verify all contents of this APK or just collect certificates.
159 *
160 * @throws PackageParserException if there was a problem collecting certificates
161 */
Patrick Baumann420d58a2017-12-19 10:17:21 -0800162 private static PackageParser.SigningDetails verifyV1Signature(
163 String apkPath, boolean verifyFull)
Dan Cashmane92f8422017-12-08 14:02:51 -0800164 throws PackageParserException {
165 StrictJarFile jarFile = null;
166
167 try {
168 final Certificate[][] lastCerts;
169 final Signature[] lastSigs;
170
171 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
Dan Cashman636ea5e2017-12-18 10:38:20 -0800172
173 // we still pass verify = true to ctor to collect certs, even though we're not checking
174 // the whole jar.
175 jarFile = new StrictJarFile(
176 apkPath,
177 true, // collect certs
178 verifyFull); // whether to reject APK with stripped v2 signatures (b/27887819)
Dan Cashmane92f8422017-12-08 14:02:51 -0800179 final List<ZipEntry> toVerify = new ArrayList<>();
180
Dan Cashman636ea5e2017-12-18 10:38:20 -0800181 // Gather certs from AndroidManifest.xml, which every APK must have, as an optimization
182 // to not need to verify the whole APK when verifyFUll == false.
Dan Cashmane92f8422017-12-08 14:02:51 -0800183 final ZipEntry manifestEntry = jarFile.findEntry(
184 PackageParser.ANDROID_MANIFEST_FILENAME);
185 if (manifestEntry == null) {
186 throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
187 "Package " + apkPath + " has no manifest");
188 }
189 lastCerts = loadCertificates(jarFile, manifestEntry);
190 if (ArrayUtils.isEmpty(lastCerts)) {
191 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
192 + apkPath + " has no certificates at entry "
193 + PackageParser.ANDROID_MANIFEST_FILENAME);
194 }
195 lastSigs = convertToSignatures(lastCerts);
196
Dan Cashman636ea5e2017-12-18 10:38:20 -0800197 // fully verify all contents, except for AndroidManifest.xml and the META-INF/ files.
198 if (verifyFull) {
Dan Cashmane92f8422017-12-08 14:02:51 -0800199 final Iterator<ZipEntry> i = jarFile.iterator();
200 while (i.hasNext()) {
201 final ZipEntry entry = i.next();
202 if (entry.isDirectory()) continue;
203
204 final String entryName = entry.getName();
205 if (entryName.startsWith("META-INF/")) continue;
206 if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
207
208 toVerify.add(entry);
209 }
210
Dan Cashmane92f8422017-12-08 14:02:51 -0800211 for (ZipEntry entry : toVerify) {
212 final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
213 if (ArrayUtils.isEmpty(entryCerts)) {
214 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
215 "Package " + apkPath + " has no certificates at entry "
216 + entry.getName());
217 }
218
219 // make sure all entries use the same signing certs
220 final Signature[] entrySigs = convertToSignatures(entryCerts);
221 if (!Signature.areExactMatch(lastSigs, entrySigs)) {
222 throw new PackageParserException(
223 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
224 "Package " + apkPath + " has mismatched certificates at entry "
225 + entry.getName());
226 }
227 }
228 }
Patrick Baumann420d58a2017-12-19 10:17:21 -0800229 return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
Dan Cashmane92f8422017-12-08 14:02:51 -0800230 } catch (GeneralSecurityException e) {
231 throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
232 "Failed to collect certificates from " + apkPath, e);
233 } catch (IOException | RuntimeException e) {
234 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
235 "Failed to collect certificates from " + apkPath, e);
236 } finally {
237 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
238 closeQuietly(jarFile);
239 }
240 }
241
242 private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
243 throws PackageParserException {
244 InputStream is = null;
245 try {
246 // We must read the stream for the JarEntry to retrieve
247 // its certificates.
248 is = jarFile.getInputStream(entry);
249 readFullyIgnoringContents(is);
250 return jarFile.getCertificateChains(entry);
251 } catch (IOException | RuntimeException e) {
252 throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
253 "Failed reading " + entry.getName() + " in " + jarFile, e);
254 } finally {
255 IoUtils.closeQuietly(is);
256 }
257 }
258
259 private static void readFullyIgnoringContents(InputStream in) throws IOException {
260 byte[] buffer = sBuffer.getAndSet(null);
261 if (buffer == null) {
262 buffer = new byte[4096];
263 }
264
265 int n = 0;
266 int count = 0;
267 while ((n = in.read(buffer, 0, buffer.length)) != -1) {
268 count += n;
269 }
270
271 sBuffer.set(buffer);
272 return;
273 }
274
275 /**
276 * Converts an array of certificate chains into the {@code Signature} equivalent used by the
277 * PackageManager.
278 *
279 * @throws CertificateEncodingException if it is unable to create a Signature object.
280 */
281 public static Signature[] convertToSignatures(Certificate[][] certs)
282 throws CertificateEncodingException {
283 final Signature[] res = new Signature[certs.length];
284 for (int i = 0; i < certs.length; i++) {
285 res[i] = new Signature(certs[i]);
286 }
287 return res;
288 }
289
290 private static void closeQuietly(StrictJarFile jarFile) {
291 if (jarFile != null) {
292 try {
293 jarFile.close();
294 } catch (Exception ignored) {
295 }
296 }
297 }
298
299 /**
Dan Cashman636ea5e2017-12-18 10:38:20 -0800300 * Returns the certificates associated with each signer for the given APK without verification.
301 * This method is dangerous and should not be used, unless the caller is absolutely certain the
302 * APK is trusted.
303 *
304 * @throws PackageParserException if the APK's signature failed to verify.
305 * or greater is not found, except in the case of no JAR signature.
306 */
Patrick Baumann420d58a2017-12-19 10:17:21 -0800307 public static PackageParser.SigningDetails plsCertsNoVerifyOnlyCerts(
308 String apkPath, int minSignatureSchemeVersion)
Dan Cashman636ea5e2017-12-18 10:38:20 -0800309 throws PackageParserException {
310
Patrick Baumann420d58a2017-12-19 10:17:21 -0800311 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800312 // V3 and before are older than the requested minimum signing version
313 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
314 "No signature found in package of version " + minSignatureSchemeVersion
315 + " or newer for package " + apkPath);
316 }
317
318 // first try v3
Daniel Cashman77029c52018-01-18 16:19:29 -0800319 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
Daniel Cashman67096e02017-12-28 12:46:33 -0800320 try {
321 ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
322 ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
323 Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
324 Signature[] signerSigs = convertToSignatures(signerCerts);
Daniel Cashman77029c52018-01-18 16:19:29 -0800325 Signature[] pastSignerSigs = null;
326 int[] pastSignerSigsFlags = null;
327 if (vSigner.por != null) {
328 // populate proof-of-rotation information
329 pastSignerSigs = new Signature[vSigner.por.certs.size()];
330 pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
331 for (int i = 0; i < pastSignerSigs.length; i++) {
332 pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
333 pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
334 }
335 }
336 return new PackageParser.SigningDetails(
337 signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
338 pastSignerSigs, pastSignerSigsFlags);
Daniel Cashman67096e02017-12-28 12:46:33 -0800339 } catch (SignatureNotFoundException e) {
Daniel Cashman77029c52018-01-18 16:19:29 -0800340 // not signed with v3, try older if allowed
Patrick Baumann420d58a2017-12-19 10:17:21 -0800341 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800342 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
343 "No APK Signature Scheme v3 signature in package " + apkPath, e);
344 }
345 } catch (Exception e) {
Daniel Cashman77029c52018-01-18 16:19:29 -0800346 // APK Signature Scheme v3 signature found but did not verify
Daniel Cashman67096e02017-12-28 12:46:33 -0800347 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
348 "Failed to collect certificates from " + apkPath
Daniel Cashman77029c52018-01-18 16:19:29 -0800349 + " using APK Signature Scheme v3", e);
Daniel Cashman67096e02017-12-28 12:46:33 -0800350 } finally {
351 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
352 }
353
354 // redundant, protective version check
Patrick Baumann420d58a2017-12-19 10:17:21 -0800355 if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800356 // V2 and before are older than the requested minimum signing version
357 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
358 "No signature found in package of version " + minSignatureSchemeVersion
359 + " or newer for package " + apkPath);
360 }
361
Dan Cashman636ea5e2017-12-18 10:38:20 -0800362 // first try v2
363 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV2");
364 try {
365 Certificate[][] signerCerts =
366 ApkSignatureSchemeV2Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
367 Signature[] signerSigs = convertToSignatures(signerCerts);
Patrick Baumann420d58a2017-12-19 10:17:21 -0800368 return new PackageParser.SigningDetails(signerSigs,
369 SignatureSchemeVersion.SIGNING_BLOCK_V2);
Dan Cashman636ea5e2017-12-18 10:38:20 -0800370 } catch (SignatureNotFoundException e) {
371 // not signed with v2, try older if allowed
Patrick Baumann420d58a2017-12-19 10:17:21 -0800372 if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Dan Cashman636ea5e2017-12-18 10:38:20 -0800373 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
374 "No APK Signature Scheme v2 signature in package " + apkPath, e);
375 }
376 } catch (Exception e) {
377 // APK Signature Scheme v2 signature found but did not verify
378 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
379 "Failed to collect certificates from " + apkPath
380 + " using APK Signature Scheme v2", e);
381 } finally {
382 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
383 }
384
Daniel Cashman67096e02017-12-28 12:46:33 -0800385 // redundant, protective version check
Patrick Baumann420d58a2017-12-19 10:17:21 -0800386 if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
Daniel Cashman67096e02017-12-28 12:46:33 -0800387 // V1 and is older than the requested minimum signing version
388 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
389 "No signature found in package of version " + minSignatureSchemeVersion
390 + " or newer for package " + apkPath);
391 }
392
Dan Cashman636ea5e2017-12-18 10:38:20 -0800393 // v2 didn't work, try jarsigner
394 return verifyV1Signature(apkPath, false);
395 }
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800396
397 /**
398 * @return the verity root hash in the Signing Block.
399 */
400 public static byte[] getVerityRootHash(String apkPath)
401 throws IOException, SignatureNotFoundException, SecurityException {
402 // first try v3
403 try {
404 return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath);
405 } catch (SignatureNotFoundException e) {
406 // try older version
407 }
408 return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath);
409 }
410
411 /**
412 * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code
413 * ByteBufferFactory}.
414 *
415 * @return the verity root hash of the generated Merkle tree.
416 */
417 public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
418 throws IOException, SignatureNotFoundException, SecurityException, DigestException,
419 NoSuchAlgorithmException {
420 // first try v3
421 try {
422 return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
423 } catch (SignatureNotFoundException e) {
424 // try older version
425 }
426 return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);
427 }
428
429 /**
Victor Hsieh5f761242018-01-20 10:30:12 -0800430 * Generates the FSVerity root hash from FSVerity header, extensions and Merkle tree root hash
431 * in Signing Block.
432 *
433 * @return FSverity root hash
434 */
435 public static byte[] generateFsverityRootHash(String apkPath)
436 throws NoSuchAlgorithmException, DigestException, IOException {
437 // first try v3
438 try {
439 return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
440 } catch (SignatureNotFoundException e) {
441 // try older version
442 }
443 try {
444 return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
445 } catch (SignatureNotFoundException e) {
446 return null;
447 }
448 }
449
450 /**
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800451 * Result of a successful APK verification operation.
452 */
453 public static class Result {
454 public final Certificate[][] certs;
455 public final Signature[] sigs;
456 public final int signatureSchemeVersion;
457
458 public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
459 this.certs = certs;
460 this.sigs = sigs;
461 this.signatureSchemeVersion = signingVersion;
462 }
463 }
Dan Cashmane92f8422017-12-08 14:02:51 -0800464}