blob: 1b04eb2f7d449f55e95d7c0f366d6124a38dd5a0 [file] [log] [blame]
Daniel Cashman67096e02017-12-28 12:46:33 -08001/*
2 * Copyright (C) 2018 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.SIGNATURE_DSA_WITH_SHA256;
21import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
22import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
23import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256;
24import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512;
25import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256;
26import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512;
Victor Hsieh4acad4c2018-01-04 13:36:15 -080027import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256;
28import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256;
29import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256;
Daniel Cashman67096e02017-12-28 12:46:33 -080030import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
31import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
32import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
33import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm;
34import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
35import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
36import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
37
38import android.os.Build;
39import android.util.ArrayMap;
40import android.util.Pair;
41
42import java.io.ByteArrayInputStream;
Daniel Cashman67096e02017-12-28 12:46:33 -080043import java.io.IOException;
44import java.io.RandomAccessFile;
45import java.nio.BufferUnderflowException;
46import java.nio.ByteBuffer;
Victor Hsieh07bc80c2018-01-11 16:15:47 -080047import java.security.DigestException;
Daniel Cashman67096e02017-12-28 12:46:33 -080048import java.security.InvalidAlgorithmParameterException;
49import java.security.InvalidKeyException;
50import java.security.KeyFactory;
51import java.security.MessageDigest;
52import java.security.NoSuchAlgorithmException;
53import java.security.PublicKey;
54import java.security.Signature;
55import java.security.SignatureException;
Dan Cashmancd4cb812018-01-02 14:55:58 -080056import java.security.cert.CertificateEncodingException;
Daniel Cashman67096e02017-12-28 12:46:33 -080057import java.security.cert.CertificateException;
58import java.security.cert.CertificateFactory;
59import java.security.cert.X509Certificate;
60import java.security.spec.AlgorithmParameterSpec;
61import java.security.spec.InvalidKeySpecException;
62import java.security.spec.X509EncodedKeySpec;
63import java.util.ArrayList;
64import java.util.Arrays;
65import java.util.List;
66import java.util.Map;
67
68/**
69 * APK Signature Scheme v3 verifier.
70 *
71 * @hide for internal use only.
72 */
73public class ApkSignatureSchemeV3Verifier {
74
75 /**
76 * ID of this signature scheme as used in X-Android-APK-Signed header used in JAR signing.
77 */
78 public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 3;
79
80 private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
81
82 /**
83 * Returns {@code true} if the provided APK contains an APK Signature Scheme V3 signature.
84 *
85 * <p><b>NOTE: This method does not verify the signature.</b>
86 */
87 public static boolean hasSignature(String apkFile) throws IOException {
88 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
89 findSignature(apk);
90 return true;
91 } catch (SignatureNotFoundException e) {
92 return false;
93 }
94 }
95
96 /**
97 * Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates
98 * associated with each signer.
99 *
100 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
101 * @throws SecurityException if the APK Signature Scheme v3 signature of this APK does not
102 * verify.
103 * @throws IOException if an I/O error occurs while reading the APK file.
104 */
105 public static VerifiedSigner verify(String apkFile)
106 throws SignatureNotFoundException, SecurityException, IOException {
107 return verify(apkFile, true);
108 }
109
110 /**
111 * Returns the certificates associated with each signer for the given APK without verification.
112 * This method is dangerous and should not be used, unless the caller is absolutely certain the
113 * APK is trusted. Specifically, verification is only done for the APK Signature Scheme v3
114 * Block while gathering signer information. The APK contents are not verified.
115 *
116 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
117 * @throws IOException if an I/O error occurs while reading the APK file.
118 */
119 public static VerifiedSigner plsCertsNoVerifyOnlyCerts(String apkFile)
120 throws SignatureNotFoundException, SecurityException, IOException {
121 return verify(apkFile, false);
122 }
123
124 private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
125 throws SignatureNotFoundException, SecurityException, IOException {
126 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
127 return verify(apk, verifyIntegrity);
128 }
129 }
130
131 /**
132 * Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates
133 * associated with each signer.
134 *
135 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
136 * @throws SecurityException if an APK Signature Scheme v3 signature of this APK does not
137 * verify.
138 * @throws IOException if an I/O error occurs while reading the APK file.
139 */
140 private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
141 throws SignatureNotFoundException, SecurityException, IOException {
142 SignatureInfo signatureInfo = findSignature(apk);
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800143 return verify(apk, signatureInfo, verifyIntegrity);
Daniel Cashman67096e02017-12-28 12:46:33 -0800144 }
145
146 /**
147 * Returns the APK Signature Scheme v3 block contained in the provided APK file and the
148 * additional information relevant for verifying the block against the file.
149 *
150 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
151 * @throws IOException if an I/O error occurs while reading the APK file.
152 */
153 private static SignatureInfo findSignature(RandomAccessFile apk)
154 throws IOException, SignatureNotFoundException {
155 return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
156 }
157
158 /**
159 * Verifies the contents of the provided APK file against the provided APK Signature Scheme v3
160 * Block.
161 *
162 * @param signatureInfo APK Signature Scheme v3 Block and information relevant for verifying it
163 * against the APK file.
164 */
165 private static VerifiedSigner verify(
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800166 RandomAccessFile apk,
Daniel Cashman67096e02017-12-28 12:46:33 -0800167 SignatureInfo signatureInfo,
168 boolean doVerifyIntegrity) throws SecurityException {
169 int signerCount = 0;
170 Map<Integer, byte[]> contentDigests = new ArrayMap<>();
171 VerifiedSigner result = null;
172 CertificateFactory certFactory;
173 try {
174 certFactory = CertificateFactory.getInstance("X.509");
175 } catch (CertificateException e) {
176 throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
177 }
178 ByteBuffer signers;
179 try {
180 signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
181 } catch (IOException e) {
182 throw new SecurityException("Failed to read list of signers", e);
183 }
184 while (signers.hasRemaining()) {
185 try {
186 ByteBuffer signer = getLengthPrefixedSlice(signers);
187 result = verifySigner(signer, contentDigests, certFactory);
188 signerCount++;
189 } catch (PlatformNotSupportedException e) {
190 // this signer is for a different platform, ignore it.
191 continue;
192 } catch (IOException | BufferUnderflowException | SecurityException e) {
193 throw new SecurityException(
194 "Failed to parse/verify signer #" + signerCount + " block",
195 e);
196 }
197 }
198
199 if (signerCount < 1 || result == null) {
200 throw new SecurityException("No signers found");
201 }
202
203 if (signerCount != 1) {
204 throw new SecurityException("APK Signature Scheme V3 only supports one signer: "
205 + "multiple signers found.");
206 }
207
208 if (contentDigests.isEmpty()) {
209 throw new SecurityException("No content digests found");
210 }
211
212 if (doVerifyIntegrity) {
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800213 ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
Daniel Cashman67096e02017-12-28 12:46:33 -0800214 }
215
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800216 if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
217 result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
218 }
219
Daniel Cashman67096e02017-12-28 12:46:33 -0800220 return result;
221 }
222
223 private static VerifiedSigner verifySigner(
224 ByteBuffer signerBlock,
225 Map<Integer, byte[]> contentDigests,
226 CertificateFactory certFactory)
227 throws SecurityException, IOException, PlatformNotSupportedException {
228 ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
229 int minSdkVersion = signerBlock.getInt();
230 int maxSdkVersion = signerBlock.getInt();
231
232 if (Build.VERSION.SDK_INT < minSdkVersion || Build.VERSION.SDK_INT > maxSdkVersion) {
233 // this signature isn't meant to be used with this platform, skip it.
234 throw new PlatformNotSupportedException(
235 "Signer not supported by this platform "
236 + "version. This platform: " + Build.VERSION.SDK_INT
237 + ", signer minSdkVersion: " + minSdkVersion
238 + ", maxSdkVersion: " + maxSdkVersion);
239 }
240
241 ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
242 byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
243
244 int signatureCount = 0;
245 int bestSigAlgorithm = -1;
246 byte[] bestSigAlgorithmSignatureBytes = null;
247 List<Integer> signaturesSigAlgorithms = new ArrayList<>();
248 while (signatures.hasRemaining()) {
249 signatureCount++;
250 try {
251 ByteBuffer signature = getLengthPrefixedSlice(signatures);
252 if (signature.remaining() < 8) {
253 throw new SecurityException("Signature record too short");
254 }
255 int sigAlgorithm = signature.getInt();
256 signaturesSigAlgorithms.add(sigAlgorithm);
257 if (!isSupportedSignatureAlgorithm(sigAlgorithm)) {
258 continue;
259 }
260 if ((bestSigAlgorithm == -1)
261 || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
262 bestSigAlgorithm = sigAlgorithm;
263 bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature);
264 }
265 } catch (IOException | BufferUnderflowException e) {
266 throw new SecurityException(
267 "Failed to parse signature record #" + signatureCount,
268 e);
269 }
270 }
271 if (bestSigAlgorithm == -1) {
272 if (signatureCount == 0) {
273 throw new SecurityException("No signatures found");
274 } else {
275 throw new SecurityException("No supported signatures found");
276 }
277 }
278
279 String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm);
280 Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
281 getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm);
282 String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
283 AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
284 boolean sigVerified;
285 try {
286 PublicKey publicKey =
287 KeyFactory.getInstance(keyAlgorithm)
288 .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
289 Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
290 sig.initVerify(publicKey);
291 if (jcaSignatureAlgorithmParams != null) {
292 sig.setParameter(jcaSignatureAlgorithmParams);
293 }
294 sig.update(signedData);
295 sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
296 } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
297 | InvalidAlgorithmParameterException | SignatureException e) {
298 throw new SecurityException(
299 "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
300 }
301 if (!sigVerified) {
302 throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
303 }
304
305 // Signature over signedData has verified.
306
307 byte[] contentDigest = null;
308 signedData.clear();
309 ByteBuffer digests = getLengthPrefixedSlice(signedData);
310 List<Integer> digestsSigAlgorithms = new ArrayList<>();
311 int digestCount = 0;
312 while (digests.hasRemaining()) {
313 digestCount++;
314 try {
315 ByteBuffer digest = getLengthPrefixedSlice(digests);
316 if (digest.remaining() < 8) {
317 throw new IOException("Record too short");
318 }
319 int sigAlgorithm = digest.getInt();
320 digestsSigAlgorithms.add(sigAlgorithm);
321 if (sigAlgorithm == bestSigAlgorithm) {
322 contentDigest = readLengthPrefixedByteArray(digest);
323 }
324 } catch (IOException | BufferUnderflowException e) {
325 throw new IOException("Failed to parse digest record #" + digestCount, e);
326 }
327 }
328
329 if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) {
330 throw new SecurityException(
331 "Signature algorithms don't match between digests and signatures records");
332 }
333 int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm);
334 byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest);
335 if ((previousSignerDigest != null)
336 && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) {
337 throw new SecurityException(
338 getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
339 + " contents digest does not match the digest specified by a preceding signer");
340 }
341
342 ByteBuffer certificates = getLengthPrefixedSlice(signedData);
343 List<X509Certificate> certs = new ArrayList<>();
344 int certificateCount = 0;
345 while (certificates.hasRemaining()) {
346 certificateCount++;
347 byte[] encodedCert = readLengthPrefixedByteArray(certificates);
348 X509Certificate certificate;
349 try {
350 certificate = (X509Certificate)
351 certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
352 } catch (CertificateException e) {
353 throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
354 }
355 certificate = new VerbatimX509Certificate(
356 certificate, encodedCert);
357 certs.add(certificate);
358 }
359
360 if (certs.isEmpty()) {
361 throw new SecurityException("No certificates listed");
362 }
363 X509Certificate mainCertificate = certs.get(0);
364 byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
365 if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
366 throw new SecurityException(
367 "Public key mismatch between certificate and signature record");
368 }
369
370 int signedMinSDK = signedData.getInt();
371 if (signedMinSDK != minSdkVersion) {
372 throw new SecurityException(
373 "minSdkVersion mismatch between signed and unsigned in v3 signer block.");
374 }
375
376 int signedMaxSDK = signedData.getInt();
377 if (signedMaxSDK != maxSdkVersion) {
378 throw new SecurityException(
379 "maxSdkVersion mismatch between signed and unsigned in v3 signer block.");
380 }
381
382 ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData);
383 return verifyAdditionalAttributes(additionalAttrs, certs, certFactory);
384 }
385
386 private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
387
388 private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
389 List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
390 X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
391 VerifiedProofOfRotation por = null;
392
393 while (attrs.hasRemaining()) {
394 ByteBuffer attr = getLengthPrefixedSlice(attrs);
395 if (attr.remaining() < 4) {
396 throw new IOException("Remaining buffer too short to contain additional attribute "
397 + "ID. Remaining: " + attr.remaining());
398 }
399 int id = attr.getInt();
400 switch(id) {
401 case PROOF_OF_ROTATION_ATTR_ID:
402 if (por != null) {
403 throw new SecurityException("Encountered multiple Proof-of-rotation records"
Dan Cashmancd4cb812018-01-02 14:55:58 -0800404 + " when verifying APK Signature Scheme v3 signature");
Daniel Cashman67096e02017-12-28 12:46:33 -0800405 }
406 por = verifyProofOfRotationStruct(attr, certFactory);
Dan Cashmancd4cb812018-01-02 14:55:58 -0800407 // make sure that the last certificate in the Proof-of-rotation record matches
408 // the one used to sign this APK.
409 try {
410 if (por.certs.size() > 0
411 && !Arrays.equals(por.certs.get(por.certs.size() - 1).getEncoded(),
412 certChain[0].getEncoded())) {
413 throw new SecurityException("Terminal certificate in Proof-of-rotation"
414 + " record does not match APK signing certificate");
415 }
416 } catch (CertificateEncodingException e) {
417 throw new SecurityException("Failed to encode certificate when comparing"
418 + " Proof-of-rotation record and signing certificate", e);
419 }
420
Daniel Cashman67096e02017-12-28 12:46:33 -0800421 break;
422 default:
423 // not the droid we're looking for, move along, move along.
424 break;
425 }
426 }
427 return new VerifiedSigner(certChain, por);
428 }
429
430 private static VerifiedProofOfRotation verifyProofOfRotationStruct(
431 ByteBuffer porBuf,
432 CertificateFactory certFactory)
433 throws SecurityException, IOException {
434 int levelCount = 0;
435 int lastSigAlgorithm = -1;
436 X509Certificate lastCert = null;
437 List<X509Certificate> certs = new ArrayList<>();
438 List<Integer> flagsList = new ArrayList<>();
439
440 // Proof-of-rotation struct:
441 // is basically a singly linked list of nodes, called levels here, each of which have the
442 // following structure:
443 // * length-prefix for the entire level
444 // - length-prefixed signed data (if previous level exists)
445 // * length-prefixed X509 Certificate
446 // * uint32 signature algorithm ID describing how this signed data was signed
447 // - uint32 flags describing how to treat the cert contained in this level
448 // - uint32 signature algorithm ID to use to verify the signature of the next level. The
449 // algorithm here must match the one in the signed data section of the next level.
450 // - length-prefixed signature over the signed data in this level. The signature here
451 // is verified using the certificate from the previous level.
452 // The linking is provided by the certificate of each level signing the one of the next.
453 while (porBuf.hasRemaining()) {
454 levelCount++;
455 try {
456 ByteBuffer level = getLengthPrefixedSlice(porBuf);
457 ByteBuffer signedData = getLengthPrefixedSlice(level);
458 int flags = level.getInt();
459 int sigAlgorithm = level.getInt();
460 byte[] signature = readLengthPrefixedByteArray(level);
461
462 if (lastCert != null) {
463 // Use previous level cert to verify current level
464 Pair<String, ? extends AlgorithmParameterSpec> sigAlgParams =
465 getSignatureAlgorithmJcaSignatureAlgorithm(lastSigAlgorithm);
466 PublicKey publicKey = lastCert.getPublicKey();
467 Signature sig = Signature.getInstance(sigAlgParams.first);
468 sig.initVerify(publicKey);
469 if (sigAlgParams.second != null) {
470 sig.setParameter(sigAlgParams.second);
471 }
472 sig.update(signedData);
473 if (!sig.verify(signature)) {
474 throw new SecurityException("Unable to verify signature of certificate #"
475 + levelCount + " using " + sigAlgParams.first + " when verifying"
476 + " Proof-of-rotation record");
477 }
478 }
479
480 byte[] encodedCert = readLengthPrefixedByteArray(signedData);
481 int signedSigAlgorithm = signedData.getInt();
482 if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) {
483 throw new SecurityException("Signing algorithm ID mismatch for certificate #"
484 + levelCount + " when verifying Proof-of-rotation record");
485 }
486 lastCert = (X509Certificate)
487 certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
488 lastCert = new VerbatimX509Certificate(lastCert, encodedCert);
489
490 lastSigAlgorithm = sigAlgorithm;
491 certs.add(lastCert);
492 flagsList.add(flags);
493 } catch (IOException | BufferUnderflowException e) {
494 throw new IOException("Failed to parse Proof-of-rotation record", e);
495 } catch (NoSuchAlgorithmException | InvalidKeyException
496 | InvalidAlgorithmParameterException | SignatureException e) {
497 throw new SecurityException(
498 "Failed to verify signature over signed data for certificate #"
499 + levelCount + " when verifying Proof-of-rotation record", e);
500 } catch (CertificateException e) {
501 throw new SecurityException("Failed to decode certificate #" + levelCount
502 + " when verifying Proof-of-rotation record", e);
503 }
504 }
505 return new VerifiedProofOfRotation(certs, flagsList);
506 }
507
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800508 static byte[] getVerityRootHash(String apkPath)
509 throws IOException, SignatureNotFoundException, SecurityException {
510 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
511 SignatureInfo signatureInfo = findSignature(apk);
512 VerifiedSigner vSigner = verify(apk, false);
513 return vSigner.verityRootHash;
514 }
515 }
516
517 static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
518 throws IOException, SignatureNotFoundException, SecurityException, DigestException,
519 NoSuchAlgorithmException {
520 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
521 SignatureInfo signatureInfo = findSignature(apk);
522 return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
523 }
524 }
525
Daniel Cashman67096e02017-12-28 12:46:33 -0800526 private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
527 switch (sigAlgorithm) {
528 case SIGNATURE_RSA_PSS_WITH_SHA256:
529 case SIGNATURE_RSA_PSS_WITH_SHA512:
530 case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
531 case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
532 case SIGNATURE_ECDSA_WITH_SHA256:
533 case SIGNATURE_ECDSA_WITH_SHA512:
534 case SIGNATURE_DSA_WITH_SHA256:
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800535 case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
536 case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
537 case SIGNATURE_VERITY_DSA_WITH_SHA256:
Daniel Cashman67096e02017-12-28 12:46:33 -0800538 return true;
539 default:
540 return false;
541 }
542 }
543
544 /**
545 * Verified processed proof of rotation.
546 *
547 * @hide for internal use only.
548 */
549 public static class VerifiedProofOfRotation {
550 public final List<X509Certificate> certs;
551 public final List<Integer> flagsList;
552
553 public VerifiedProofOfRotation(List<X509Certificate> certs, List<Integer> flagsList) {
554 this.certs = certs;
555 this.flagsList = flagsList;
556 }
557 }
558
559 /**
560 * Verified APK Signature Scheme v3 signer, including the proof of rotation structure.
561 *
562 * @hide for internal use only.
563 */
564 public static class VerifiedSigner {
565 public final X509Certificate[] certs;
566 public final VerifiedProofOfRotation por;
567
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800568 public byte[] verityRootHash;
569
Daniel Cashman67096e02017-12-28 12:46:33 -0800570 public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
571 this.certs = certs;
572 this.por = por;
573 }
574
575 }
576
577 private static class PlatformNotSupportedException extends Exception {
578
579 PlatformNotSupportedException(String s) {
580 super(s);
581 }
582 }
583}