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