blob: 04be71f2babc040c0339041437370ce3f96b3da0 [file] [log] [blame]
Alex Klyubine4157182016-01-05 13:27:05 -08001/*
2 * Copyright (C) 2016 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
Victor Hsieh07bc80c2018-01-11 16:15:47 -080019import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
Daniel Cashman67096e02017-12-28 12:46:33 -080020import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
21import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
22import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
23import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm;
24import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
25import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
Khaled Abdelmohsenf10fc7a2020-03-02 19:02:28 +000026import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
Daniel Cashman67096e02017-12-28 12:46:33 -080027import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
28
Alex Klyubin0722ffc2016-03-18 16:09:06 -070029import android.util.ArrayMap;
Alex Klyubine4157182016-01-05 13:27:05 -080030import android.util.Pair;
31
32import java.io.ByteArrayInputStream;
33import java.io.IOException;
34import java.io.RandomAccessFile;
Alex Klyubine4157182016-01-05 13:27:05 -080035import java.nio.BufferUnderflowException;
36import java.nio.ByteBuffer;
Victor Hsieh07bc80c2018-01-11 16:15:47 -080037import java.security.DigestException;
Alex Klyubine4157182016-01-05 13:27:05 -080038import java.security.InvalidAlgorithmParameterException;
39import java.security.InvalidKeyException;
40import java.security.KeyFactory;
41import java.security.MessageDigest;
42import java.security.NoSuchAlgorithmException;
Alex Klyubine4157182016-01-05 13:27:05 -080043import java.security.PublicKey;
44import java.security.Signature;
45import java.security.SignatureException;
Alex Klyubine4157182016-01-05 13:27:05 -080046import java.security.cert.CertificateException;
Alex Klyubine4157182016-01-05 13:27:05 -080047import java.security.cert.CertificateFactory;
Alex Klyubine4157182016-01-05 13:27:05 -080048import java.security.cert.X509Certificate;
49import java.security.spec.AlgorithmParameterSpec;
50import java.security.spec.InvalidKeySpecException;
Alex Klyubine4157182016-01-05 13:27:05 -080051import java.security.spec.X509EncodedKeySpec;
52import java.util.ArrayList;
53import java.util.Arrays;
Alex Klyubine4157182016-01-05 13:27:05 -080054import java.util.List;
55import java.util.Map;
Alex Klyubine4157182016-01-05 13:27:05 -080056
57/**
58 * APK Signature Scheme v2 verifier.
59 *
Daniel Cashman67096e02017-12-28 12:46:33 -080060 * <p>APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single
61 * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and
62 * uncompressed contents of ZIP entries.
63 *
64 * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature Scheme v2</a>
65 *
Alex Klyubine4157182016-01-05 13:27:05 -080066 * @hide for internal use only.
67 */
68public class ApkSignatureSchemeV2Verifier {
69
70 /**
Daniel Cashman67096e02017-12-28 12:46:33 -080071 * ID of this signature scheme as used in X-Android-APK-Signed header used in JAR signing.
Alex Klyubine4157182016-01-05 13:27:05 -080072 */
Alex Klyubin3a0095f2016-02-16 12:37:17 -080073 public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2;
Alex Klyubine4157182016-01-05 13:27:05 -080074
Daniel Cashman67096e02017-12-28 12:46:33 -080075 private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
76
Alex Klyubine4157182016-01-05 13:27:05 -080077 /**
Alex Klyubin0722ffc2016-03-18 16:09:06 -070078 * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature.
79 *
80 * <p><b>NOTE: This method does not verify the signature.</b>
Todd Kennedy66c55532016-02-26 16:22:11 -080081 */
82 public static boolean hasSignature(String apkFile) throws IOException {
83 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
Alex Klyubin0722ffc2016-03-18 16:09:06 -070084 findSignature(apk);
Todd Kennedy66c55532016-02-26 16:22:11 -080085 return true;
86 } catch (SignatureNotFoundException e) {
Alex Klyubin0722ffc2016-03-18 16:09:06 -070087 return false;
Todd Kennedy66c55532016-02-26 16:22:11 -080088 }
Todd Kennedy66c55532016-02-26 16:22:11 -080089 }
90
91 /**
Alex Klyubine4157182016-01-05 13:27:05 -080092 * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
93 * associated with each signer.
94 *
95 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
96 * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
97 * @throws IOException if an I/O error occurs while reading the APK file.
98 */
99 public static X509Certificate[][] verify(String apkFile)
100 throws SignatureNotFoundException, SecurityException, IOException {
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800101 VerifiedSigner vSigner = verify(apkFile, true);
102 return vSigner.certs;
Alex Klyubine4157182016-01-05 13:27:05 -0800103 }
104
105 /**
Dan Cashman636ea5e2017-12-18 10:38:20 -0800106 * Returns the certificates associated with each signer for the given APK without verification.
107 * This method is dangerous and should not be used, unless the caller is absolutely certain the
Daniel Cashman67096e02017-12-28 12:46:33 -0800108 * APK is trusted. Specifically, verification is only done for the APK Signature Scheme v2
Dan Cashman636ea5e2017-12-18 10:38:20 -0800109 * Block while gathering signer information. The APK contents are not verified.
110 *
111 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
112 * @throws IOException if an I/O error occurs while reading the APK file.
113 */
Gavin Corkeryed521ab2019-01-31 16:59:41 +0000114 public static X509Certificate[][] unsafeGetCertsWithoutVerification(String apkFile)
Dan Cashman636ea5e2017-12-18 10:38:20 -0800115 throws SignatureNotFoundException, SecurityException, IOException {
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800116 VerifiedSigner vSigner = verify(apkFile, false);
117 return vSigner.certs;
Dan Cashman636ea5e2017-12-18 10:38:20 -0800118 }
119
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800120 private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
Dan Cashman636ea5e2017-12-18 10:38:20 -0800121 throws SignatureNotFoundException, SecurityException, IOException {
122 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
123 return verify(apk, verifyIntegrity);
124 }
125 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800126
Dan Cashman636ea5e2017-12-18 10:38:20 -0800127 /**
Alex Klyubine4157182016-01-05 13:27:05 -0800128 * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
129 * associated with each signer.
130 *
131 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700132 * @throws SecurityException if an APK Signature Scheme v2 signature of this APK does not
133 * verify.
Alex Klyubine4157182016-01-05 13:27:05 -0800134 * @throws IOException if an I/O error occurs while reading the APK file.
135 */
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800136 private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
Alex Klyubine4157182016-01-05 13:27:05 -0800137 throws SignatureNotFoundException, SecurityException, IOException {
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700138 SignatureInfo signatureInfo = findSignature(apk);
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800139 return verify(apk, signatureInfo, verifyIntegrity);
Alex Klyubine4157182016-01-05 13:27:05 -0800140 }
141
142 /**
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700143 * Returns the APK Signature Scheme v2 block contained in the provided APK file and the
144 * additional information relevant for verifying the block against the file.
Alex Klyubine4157182016-01-05 13:27:05 -0800145 *
146 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700147 * @throws IOException if an I/O error occurs while reading the APK file.
Alex Klyubine4157182016-01-05 13:27:05 -0800148 */
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700149 private static SignatureInfo findSignature(RandomAccessFile apk)
150 throws IOException, SignatureNotFoundException {
Daniel Cashman67096e02017-12-28 12:46:33 -0800151 return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
Alex Klyubine4157182016-01-05 13:27:05 -0800152 }
153
154 /**
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700155 * Verifies the contents of the provided APK file against the provided APK Signature Scheme v2
156 * Block.
157 *
158 * @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
159 * against the APK file.
Alex Klyubine4157182016-01-05 13:27:05 -0800160 */
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800161 private static VerifiedSigner verify(
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800162 RandomAccessFile apk,
Dan Cashman636ea5e2017-12-18 10:38:20 -0800163 SignatureInfo signatureInfo,
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800164 boolean doVerifyIntegrity) throws SecurityException, IOException {
Alex Klyubine4157182016-01-05 13:27:05 -0800165 int signerCount = 0;
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700166 Map<Integer, byte[]> contentDigests = new ArrayMap<>();
Alex Klyubine4157182016-01-05 13:27:05 -0800167 List<X509Certificate[]> signerCerts = new ArrayList<>();
168 CertificateFactory certFactory;
169 try {
170 certFactory = CertificateFactory.getInstance("X.509");
171 } catch (CertificateException e) {
172 throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
173 }
174 ByteBuffer signers;
175 try {
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700176 signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
Alex Klyubine4157182016-01-05 13:27:05 -0800177 } catch (IOException e) {
178 throw new SecurityException("Failed to read list of signers", e);
179 }
180 while (signers.hasRemaining()) {
181 signerCount++;
182 try {
183 ByteBuffer signer = getLengthPrefixedSlice(signers);
184 X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory);
185 signerCerts.add(certs);
186 } catch (IOException | BufferUnderflowException | SecurityException e) {
187 throw new SecurityException(
188 "Failed to parse/verify signer #" + signerCount + " block",
189 e);
190 }
191 }
192
193 if (signerCount < 1) {
194 throw new SecurityException("No signers found");
195 }
196
197 if (contentDigests.isEmpty()) {
198 throw new SecurityException("No content digests found");
199 }
200
Dan Cashman636ea5e2017-12-18 10:38:20 -0800201 if (doVerifyIntegrity) {
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800202 ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
Dan Cashman636ea5e2017-12-18 10:38:20 -0800203 }
Alex Klyubine4157182016-01-05 13:27:05 -0800204
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800205 byte[] verityRootHash = null;
206 if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
Victor Hsieh4ba1eea2018-03-02 14:26:19 -0800207 byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
208 verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
209 verityDigest, apk.length(), signatureInfo);
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800210 }
211
212 return new VerifiedSigner(
213 signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
214 verityRootHash);
Alex Klyubine4157182016-01-05 13:27:05 -0800215 }
216
217 private static X509Certificate[] verifySigner(
218 ByteBuffer signerBlock,
219 Map<Integer, byte[]> contentDigests,
220 CertificateFactory certFactory) throws SecurityException, IOException {
221 ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
222 ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
223 byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
224
225 int signatureCount = 0;
226 int bestSigAlgorithm = -1;
227 byte[] bestSigAlgorithmSignatureBytes = null;
228 List<Integer> signaturesSigAlgorithms = new ArrayList<>();
229 while (signatures.hasRemaining()) {
230 signatureCount++;
231 try {
232 ByteBuffer signature = getLengthPrefixedSlice(signatures);
233 if (signature.remaining() < 8) {
234 throw new SecurityException("Signature record too short");
235 }
236 int sigAlgorithm = signature.getInt();
237 signaturesSigAlgorithms.add(sigAlgorithm);
238 if (!isSupportedSignatureAlgorithm(sigAlgorithm)) {
239 continue;
240 }
241 if ((bestSigAlgorithm == -1)
242 || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
243 bestSigAlgorithm = sigAlgorithm;
244 bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature);
245 }
246 } catch (IOException | BufferUnderflowException e) {
247 throw new SecurityException(
248 "Failed to parse signature record #" + signatureCount,
249 e);
250 }
251 }
252 if (bestSigAlgorithm == -1) {
253 if (signatureCount == 0) {
254 throw new SecurityException("No signatures found");
255 } else {
256 throw new SecurityException("No supported signatures found");
257 }
258 }
259
260 String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm);
261 Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
262 getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm);
263 String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
264 AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
265 boolean sigVerified;
266 try {
267 PublicKey publicKey =
268 KeyFactory.getInstance(keyAlgorithm)
269 .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
270 Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
271 sig.initVerify(publicKey);
272 if (jcaSignatureAlgorithmParams != null) {
273 sig.setParameter(jcaSignatureAlgorithmParams);
274 }
275 sig.update(signedData);
276 sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
277 } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
278 | InvalidAlgorithmParameterException | SignatureException e) {
279 throw new SecurityException(
280 "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
281 }
282 if (!sigVerified) {
283 throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
284 }
285
286 // Signature over signedData has verified.
287
288 byte[] contentDigest = null;
289 signedData.clear();
290 ByteBuffer digests = getLengthPrefixedSlice(signedData);
291 List<Integer> digestsSigAlgorithms = new ArrayList<>();
292 int digestCount = 0;
293 while (digests.hasRemaining()) {
294 digestCount++;
295 try {
296 ByteBuffer digest = getLengthPrefixedSlice(digests);
297 if (digest.remaining() < 8) {
298 throw new IOException("Record too short");
299 }
300 int sigAlgorithm = digest.getInt();
301 digestsSigAlgorithms.add(sigAlgorithm);
302 if (sigAlgorithm == bestSigAlgorithm) {
303 contentDigest = readLengthPrefixedByteArray(digest);
304 }
305 } catch (IOException | BufferUnderflowException e) {
306 throw new IOException("Failed to parse digest record #" + digestCount, e);
307 }
308 }
309
310 if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) {
311 throw new SecurityException(
312 "Signature algorithms don't match between digests and signatures records");
313 }
314 int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm);
315 byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest);
316 if ((previousSignerDigest != null)
317 && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) {
318 throw new SecurityException(
319 getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
320 + " contents digest does not match the digest specified by a preceding signer");
321 }
322
323 ByteBuffer certificates = getLengthPrefixedSlice(signedData);
324 List<X509Certificate> certs = new ArrayList<>();
325 int certificateCount = 0;
326 while (certificates.hasRemaining()) {
327 certificateCount++;
328 byte[] encodedCert = readLengthPrefixedByteArray(certificates);
329 X509Certificate certificate;
330 try {
331 certificate = (X509Certificate)
332 certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
333 } catch (CertificateException e) {
334 throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
335 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800336 certificate = new VerbatimX509Certificate(
337 certificate, encodedCert);
Alex Klyubine4157182016-01-05 13:27:05 -0800338 certs.add(certificate);
339 }
340
341 if (certs.isEmpty()) {
342 throw new SecurityException("No certificates listed");
343 }
344 X509Certificate mainCertificate = certs.get(0);
345 byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
346 if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
347 throw new SecurityException(
348 "Public key mismatch between certificate and signature record");
349 }
350
Daniel Cashman67096e02017-12-28 12:46:33 -0800351 ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData);
352 verifyAdditionalAttributes(additionalAttrs);
353
Alex Klyubine4157182016-01-05 13:27:05 -0800354 return certs.toArray(new X509Certificate[certs.size()]);
355 }
356
Daniel Cashman67096e02017-12-28 12:46:33 -0800357 // Attribute to check whether a newer APK Signature Scheme signature was stripped
358 private static final int STRIPPING_PROTECTION_ATTR_ID = 0xbeeff00d;
Alex Klyubine4157182016-01-05 13:27:05 -0800359
Daniel Cashman67096e02017-12-28 12:46:33 -0800360 private static void verifyAdditionalAttributes(ByteBuffer attrs)
361 throws SecurityException, IOException {
362 while (attrs.hasRemaining()) {
363 ByteBuffer attr = getLengthPrefixedSlice(attrs);
364 if (attr.remaining() < 4) {
365 throw new IOException("Remaining buffer too short to contain additional attribute "
366 + "ID. Remaining: " + attr.remaining());
Alex Klyubine4157182016-01-05 13:27:05 -0800367 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800368 int id = attr.getInt();
369 switch (id) {
370 case STRIPPING_PROTECTION_ATTR_ID:
371 if (attr.remaining() < 4) {
372 throw new IOException("V2 Signature Scheme Stripping Protection Attribute "
373 + " value too small. Expected 4 bytes, but found "
374 + attr.remaining());
Alex Klyubine4157182016-01-05 13:27:05 -0800375 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800376 int vers = attr.getInt();
377 if (vers == ApkSignatureSchemeV3Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
378 throw new SecurityException("V2 signature indicates APK is signed using APK"
379 + " Signature Scheme v3, but none was found. Signature stripped?");
380 }
381 break;
382 default:
383 // not the droid we're looking for, move along, move along.
384 break;
Alex Klyubine4157182016-01-05 13:27:05 -0800385 }
386 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800387 return;
Alex Klyubine4157182016-01-05 13:27:05 -0800388 }
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800389
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800390 static byte[] getVerityRootHash(String apkPath)
391 throws IOException, SignatureNotFoundException, SecurityException {
392 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
393 SignatureInfo signatureInfo = findSignature(apk);
394 VerifiedSigner vSigner = verify(apk, false);
395 return vSigner.verityRootHash;
396 }
397 }
398
399 static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
400 throws IOException, SignatureNotFoundException, SecurityException, DigestException,
401 NoSuchAlgorithmException {
402 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
403 SignatureInfo signatureInfo = findSignature(apk);
Victor Hsieh27300922018-09-28 09:31:44 -0700404 return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800405 }
406 }
407
Victor Hsieh25195132018-09-06 16:32:06 -0700408 static byte[] generateApkVerityRootHash(String apkPath)
Victor Hsieh5f761242018-01-20 10:30:12 -0800409 throws IOException, SignatureNotFoundException, DigestException,
410 NoSuchAlgorithmException {
411 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
412 SignatureInfo signatureInfo = findSignature(apk);
413 VerifiedSigner vSigner = verify(apk, false);
414 if (vSigner.verityRootHash == null) {
415 return null;
416 }
Victor Hsieh27300922018-09-28 09:31:44 -0700417 return VerityBuilder.generateApkVerityRootHash(
Victor Hsieh5f761242018-01-20 10:30:12 -0800418 apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
419 }
420 }
421
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800422 /**
423 * Verified APK Signature Scheme v2 signer.
424 *
425 * @hide for internal use only.
426 */
427 public static class VerifiedSigner {
428 public final X509Certificate[][] certs;
429 public final byte[] verityRootHash;
430
431 public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
432 this.certs = certs;
433 this.verityRootHash = verityRootHash;
434 }
435
436 }
Alex Klyubine4157182016-01-05 13:27:05 -0800437}