• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.apksig;
18 
19 import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME;
20 import static com.android.apksig.apk.ApkUtils.computeSha256DigestBytes;
21 import static com.android.apksig.apk.ApkUtils.getTargetSandboxVersionFromBinaryAndroidManifest;
22 import static com.android.apksig.apk.ApkUtils.getTargetSdkVersionFromBinaryAndroidManifest;
23 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2;
24 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3;
25 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME;
26 import static com.android.apksig.internal.apk.v1.V1SchemeConstants.MANIFEST_ENTRY_NAME;
27 
28 import com.android.apksig.apk.ApkFormatException;
29 import com.android.apksig.apk.ApkUtils;
30 import com.android.apksig.internal.apk.ApkSigResult;
31 import com.android.apksig.internal.apk.ApkSignerInfo;
32 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
33 import com.android.apksig.internal.apk.ContentDigestAlgorithm;
34 import com.android.apksig.internal.apk.SignatureAlgorithm;
35 import com.android.apksig.internal.apk.SignatureInfo;
36 import com.android.apksig.internal.apk.SignatureNotFoundException;
37 import com.android.apksig.internal.apk.stamp.SourceStampConstants;
38 import com.android.apksig.internal.apk.stamp.V2SourceStampVerifier;
39 import com.android.apksig.internal.apk.v1.V1SchemeVerifier;
40 import com.android.apksig.internal.apk.v2.V2SchemeConstants;
41 import com.android.apksig.internal.apk.v2.V2SchemeVerifier;
42 import com.android.apksig.internal.apk.v3.V3SchemeConstants;
43 import com.android.apksig.internal.apk.v3.V3SchemeVerifier;
44 import com.android.apksig.internal.apk.v4.V4SchemeVerifier;
45 import com.android.apksig.internal.util.AndroidSdkVersion;
46 import com.android.apksig.internal.zip.CentralDirectoryRecord;
47 import com.android.apksig.internal.zip.LocalFileRecord;
48 import com.android.apksig.util.DataSource;
49 import com.android.apksig.util.DataSources;
50 import com.android.apksig.util.RunnablesExecutor;
51 import com.android.apksig.zip.ZipFormatException;
52 
53 import java.io.Closeable;
54 import java.io.File;
55 import java.io.IOException;
56 import java.io.RandomAccessFile;
57 import java.nio.ByteBuffer;
58 import java.security.NoSuchAlgorithmException;
59 import java.security.cert.CertificateEncodingException;
60 import java.security.cert.X509Certificate;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collections;
64 import java.util.EnumMap;
65 import java.util.HashMap;
66 import java.util.HashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Set;
70 
71 /**
72  * APK signature verifier which mimics the behavior of the Android platform.
73  *
74  * <p>The verifier is designed to closely mimic the behavior of Android platforms. This is to enable
75  * the verifier to be used for checking whether an APK's signatures are expected to verify on
76  * Android.
77  *
78  * <p>Use {@link Builder} to obtain instances of this verifier.
79  *
80  * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a>
81  */
82 public class ApkVerifier {
83 
84     private static final Map<Integer, String> SUPPORTED_APK_SIG_SCHEME_NAMES =
85             loadSupportedApkSigSchemeNames();
86 
loadSupportedApkSigSchemeNames()87     private static Map<Integer, String> loadSupportedApkSigSchemeNames() {
88         Map<Integer, String> supportedMap = new HashMap<>(2);
89         supportedMap.put(
90                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2, "APK Signature Scheme v2");
91         supportedMap.put(
92                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3, "APK Signature Scheme v3");
93         return supportedMap;
94     }
95 
96     private final File mApkFile;
97     private final DataSource mApkDataSource;
98     private final File mV4SignatureFile;
99 
100     private final Integer mMinSdkVersion;
101     private final int mMaxSdkVersion;
102 
ApkVerifier( File apkFile, DataSource apkDataSource, File v4SignatureFile, Integer minSdkVersion, int maxSdkVersion)103     private ApkVerifier(
104             File apkFile,
105             DataSource apkDataSource,
106             File v4SignatureFile,
107             Integer minSdkVersion,
108             int maxSdkVersion) {
109         mApkFile = apkFile;
110         mApkDataSource = apkDataSource;
111         mV4SignatureFile = v4SignatureFile;
112         mMinSdkVersion = minSdkVersion;
113         mMaxSdkVersion = maxSdkVersion;
114     }
115 
116     /**
117      * Verifies the APK's signatures and returns the result of verification. The APK can be
118      * considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
119      * The verification result also includes errors, warnings, and information about signers such
120      * as their signing certificates.
121      *
122      * <p>Verification succeeds iff the APK's signature is expected to verify on all Android
123      * platform versions specified via the {@link Builder}. If the APK's signature is expected to
124      * not verify on any of the specified platform versions, this method returns a result with one
125      * or more errors and whose {@link Result#isVerified()} returns {@code false}, or this method
126      * throws an exception.
127      *
128      * @throws IOException              if an I/O error is encountered while reading the APK
129      * @throws ApkFormatException       if the APK is malformed
130      * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
131      *                                  required cryptographic algorithm implementation is missing
132      * @throws IllegalStateException    if this verifier's configuration is missing required
133      *                                  information.
134      */
verify()135     public Result verify() throws IOException, ApkFormatException, NoSuchAlgorithmException,
136             IllegalStateException {
137         Closeable in = null;
138         try {
139             DataSource apk;
140             if (mApkDataSource != null) {
141                 apk = mApkDataSource;
142             } else if (mApkFile != null) {
143                 RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
144                 in = f;
145                 apk = DataSources.asDataSource(f, 0, f.length());
146             } else {
147                 throw new IllegalStateException("APK not provided");
148             }
149             return verify(apk);
150         } finally {
151             if (in != null) {
152                 in.close();
153             }
154         }
155     }
156 
157     /**
158      * Verifies the APK's signatures and returns the result of verification. The APK can be
159      * considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
160      * The verification result also includes errors, warnings, and information about signers.
161      *
162      * @param apk APK file contents
163      * @throws IOException              if an I/O error is encountered while reading the APK
164      * @throws ApkFormatException       if the APK is malformed
165      * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
166      *                                  required cryptographic algorithm implementation is missing
167      */
verify(DataSource apk)168     private Result verify(DataSource apk)
169             throws IOException, ApkFormatException, NoSuchAlgorithmException {
170         int maxSdkVersion = mMaxSdkVersion;
171 
172         ApkUtils.ZipSections zipSections;
173         try {
174             zipSections = ApkUtils.findZipSections(apk);
175         } catch (ZipFormatException e) {
176             throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
177         }
178 
179         ByteBuffer androidManifest = null;
180 
181         int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections);
182 
183         Result result = new Result();
184         Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests =
185                 new HashMap<>();
186 
187         // The SUPPORTED_APK_SIG_SCHEME_NAMES contains the mapping from version number to scheme
188         // name, but the verifiers use this parameter as the schemes supported by the target SDK
189         // range. Since the code below skips signature verification based on max SDK the mapping of
190         // supported schemes needs to be modified to ensure the verifiers do not report a stripped
191         // signature for an SDK range that does not support that signature version. For instance an
192         // APK with V1, V2, and V3 signatures and a max SDK of O would skip the V3 signature
193         // verification, but the SUPPORTED_APK_SIG_SCHEME_NAMES contains version 3, so when the V2
194         // verification is performed it would see the stripping protection attribute, see that V3
195         // is in the list of supported signatures, and report a stripped signature.
196         Map<Integer, String> supportedSchemeNames = getSupportedSchemeNames(maxSdkVersion);
197 
198         // Android N and newer attempts to verify APKs using the APK Signing Block, which can
199         // include v2 and/or v3 signatures.  If none is found, it falls back to JAR signature
200         // verification. If the signature is found but does not verify, the APK is rejected.
201         Set<Integer> foundApkSigSchemeIds = new HashSet<>(2);
202         if (maxSdkVersion >= AndroidSdkVersion.N) {
203             RunnablesExecutor executor = RunnablesExecutor.SINGLE_THREADED;
204             // Android P and newer attempts to verify APKs using APK Signature Scheme v3
205             if (maxSdkVersion >= AndroidSdkVersion.P) {
206                 try {
207                     ApkSigningBlockUtils.Result v3Result =
208                             V3SchemeVerifier.verify(
209                                     executor,
210                                     apk,
211                                     zipSections,
212                                     Math.max(minSdkVersion, AndroidSdkVersion.P),
213                                     maxSdkVersion);
214                     foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
215                     result.mergeFrom(v3Result);
216                     signatureSchemeApkContentDigests.put(
217                             ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3,
218                             getApkContentDigestsFromSigningSchemeResult(v3Result));
219                 } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) {
220                     // v3 signature not required
221                 }
222                 if (result.containsErrors()) {
223                     return result;
224                 }
225             }
226 
227             // Attempt to verify the APK using v2 signing if necessary. Platforms prior to Android P
228             // ignore APK Signature Scheme v3 signatures and always attempt to verify either JAR or
229             // APK Signature Scheme v2 signatures.  Android P onwards verifies v2 signatures only if
230             // no APK Signature Scheme v3 (or newer scheme) signatures were found.
231             if (minSdkVersion < AndroidSdkVersion.P || foundApkSigSchemeIds.isEmpty()) {
232                 try {
233                     ApkSigningBlockUtils.Result v2Result =
234                             V2SchemeVerifier.verify(
235                                     executor,
236                                     apk,
237                                     zipSections,
238                                     supportedSchemeNames,
239                                     foundApkSigSchemeIds,
240                                     Math.max(minSdkVersion, AndroidSdkVersion.N),
241                                     maxSdkVersion);
242                     foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
243                     result.mergeFrom(v2Result);
244                     signatureSchemeApkContentDigests.put(
245                             ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2,
246                             getApkContentDigestsFromSigningSchemeResult(v2Result));
247                 } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) {
248                     // v2 signature not required
249                 }
250                 if (result.containsErrors()) {
251                     return result;
252                 }
253             }
254 
255             // If v4 file is specified, use additional verification on it
256             if (mV4SignatureFile != null) {
257                 final ApkSigningBlockUtils.Result v4Result =
258                         V4SchemeVerifier.verify(apk, mV4SignatureFile);
259                 foundApkSigSchemeIds.add(
260                         ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4);
261                 result.mergeFrom(v4Result);
262                 if (result.containsErrors()) {
263                     return result;
264                 }
265             }
266         }
267 
268         // Android O and newer requires that APKs targeting security sandbox version 2 and higher
269         // are signed using APK Signature Scheme v2 or newer.
270         if (maxSdkVersion >= AndroidSdkVersion.O) {
271             if (androidManifest == null) {
272                 androidManifest = getAndroidManifestFromApk(apk, zipSections);
273             }
274             int targetSandboxVersion =
275                     getTargetSandboxVersionFromBinaryAndroidManifest(androidManifest.slice());
276             if (targetSandboxVersion > 1) {
277                 if (foundApkSigSchemeIds.isEmpty()) {
278                     result.addError(
279                             Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION,
280                             targetSandboxVersion);
281                 }
282             }
283         }
284 
285         List<CentralDirectoryRecord> cdRecords =
286                 V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections);
287 
288         // Attempt to verify the APK using JAR signing if necessary. Platforms prior to Android N
289         // ignore APK Signature Scheme v2 signatures and always attempt to verify JAR signatures.
290         // Android N onwards verifies JAR signatures only if no APK Signature Scheme v2 (or newer
291         // scheme) signatures were found.
292         if ((minSdkVersion < AndroidSdkVersion.N) || (foundApkSigSchemeIds.isEmpty())) {
293             V1SchemeVerifier.Result v1Result =
294                     V1SchemeVerifier.verify(
295                             apk,
296                             zipSections,
297                             supportedSchemeNames,
298                             foundApkSigSchemeIds,
299                             minSdkVersion,
300                             maxSdkVersion);
301             result.mergeFrom(v1Result);
302             signatureSchemeApkContentDigests.put(
303                     ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME,
304                     getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections));
305         }
306         if (result.containsErrors()) {
307             return result;
308         }
309 
310         // Verify the SourceStamp, if found in the APK.
311         try {
312             CentralDirectoryRecord sourceStampCdRecord = null;
313             for (CentralDirectoryRecord cdRecord : cdRecords) {
314                 if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(
315                         cdRecord.getName())) {
316                     sourceStampCdRecord = cdRecord;
317                     break;
318                 }
319             }
320             // If SourceStamp file is found inside the APK, there must be a SourceStamp
321             // block in the APK signing block as well.
322             if (sourceStampCdRecord != null) {
323                 byte[] sourceStampCertificateDigest =
324                         LocalFileRecord.getUncompressedData(
325                                 apk,
326                                 sourceStampCdRecord,
327                                 zipSections.getZipCentralDirectoryOffset());
328                 ApkSigResult sourceStampResult =
329                         V2SourceStampVerifier.verify(
330                                 apk,
331                                 zipSections,
332                                 sourceStampCertificateDigest,
333                                 signatureSchemeApkContentDigests,
334                                 Math.max(minSdkVersion, AndroidSdkVersion.R),
335                                 maxSdkVersion);
336                 result.mergeFrom(sourceStampResult);
337             }
338         } catch (SignatureNotFoundException ignored) {
339             result.addWarning(Issue.SOURCE_STAMP_SIG_MISSING);
340         } catch (ZipFormatException e) {
341             throw new ApkFormatException("Failed to read APK", e);
342         }
343         if (result.containsErrors()) {
344             return result;
345         }
346 
347         // Check whether v1 and v2 scheme signer identifies match, provided both v1 and v2
348         // signatures verified.
349         if ((result.isVerifiedUsingV1Scheme()) && (result.isVerifiedUsingV2Scheme())) {
350             ArrayList<Result.V1SchemeSignerInfo> v1Signers =
351                     new ArrayList<>(result.getV1SchemeSigners());
352             ArrayList<Result.V2SchemeSignerInfo> v2Signers =
353                     new ArrayList<>(result.getV2SchemeSigners());
354             ArrayList<ByteArray> v1SignerCerts = new ArrayList<>();
355             ArrayList<ByteArray> v2SignerCerts = new ArrayList<>();
356             for (Result.V1SchemeSignerInfo signer : v1Signers) {
357                 try {
358                     v1SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
359                 } catch (CertificateEncodingException e) {
360                     throw new IllegalStateException(
361                             "Failed to encode JAR signer " + signer.getName() + " certs", e);
362                 }
363             }
364             for (Result.V2SchemeSignerInfo signer : v2Signers) {
365                 try {
366                     v2SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
367                 } catch (CertificateEncodingException e) {
368                     throw new IllegalStateException(
369                             "Failed to encode APK Signature Scheme v2 signer (index: "
370                                     + signer.getIndex() + ") certs",
371                             e);
372                 }
373             }
374 
375             for (int i = 0; i < v1SignerCerts.size(); i++) {
376                 ByteArray v1Cert = v1SignerCerts.get(i);
377                 if (!v2SignerCerts.contains(v1Cert)) {
378                     Result.V1SchemeSignerInfo v1Signer = v1Signers.get(i);
379                     v1Signer.addError(Issue.V2_SIG_MISSING);
380                     break;
381                 }
382             }
383             for (int i = 0; i < v2SignerCerts.size(); i++) {
384                 ByteArray v2Cert = v2SignerCerts.get(i);
385                 if (!v1SignerCerts.contains(v2Cert)) {
386                     Result.V2SchemeSignerInfo v2Signer = v2Signers.get(i);
387                     v2Signer.addError(Issue.JAR_SIG_MISSING);
388                     break;
389                 }
390             }
391         }
392 
393         // If there is a v3 scheme signer and an earlier scheme signer, make sure that there is a
394         // match, or in the event of signing certificate rotation, that the v1/v2 scheme signer
395         // matches the oldest signing certificate in the provided SigningCertificateLineage
396         if (result.isVerifiedUsingV3Scheme()
397                 && (result.isVerifiedUsingV1Scheme() || result.isVerifiedUsingV2Scheme())) {
398             SigningCertificateLineage lineage = result.getSigningCertificateLineage();
399             X509Certificate oldSignerCert;
400             if (result.isVerifiedUsingV1Scheme()) {
401                 List<Result.V1SchemeSignerInfo> v1Signers = result.getV1SchemeSigners();
402                 if (v1Signers.size() != 1) {
403                     // APK Signature Scheme v3 only supports single-signers, error to sign with
404                     // multiple and then only one
405                     result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS);
406                 }
407                 oldSignerCert = v1Signers.get(0).mCertChain.get(0);
408             } else {
409                 List<Result.V2SchemeSignerInfo> v2Signers = result.getV2SchemeSigners();
410                 if (v2Signers.size() != 1) {
411                     // APK Signature Scheme v3 only supports single-signers, error to sign with
412                     // multiple and then only one
413                     result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS);
414                 }
415                 oldSignerCert = v2Signers.get(0).mCerts.get(0);
416             }
417             if (lineage == null) {
418                 // no signing certificate history with which to contend, just make sure that v3
419                 // matches previous versions
420                 List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
421                 if (v3Signers.size() != 1) {
422                     // multiple v3 signers should never exist without rotation history, since
423                     // multiple signers implies a different signer for different platform versions
424                     result.addError(Issue.V3_SIG_MULTIPLE_SIGNERS);
425                 }
426                 try {
427                     if (!Arrays.equals(oldSignerCert.getEncoded(),
428                             v3Signers.get(0).mCerts.get(0).getEncoded())) {
429                         result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
430                     }
431                 } catch (CertificateEncodingException e) {
432                     // we just go the encoding for the v1/v2 certs above, so must be v3
433                     throw new RuntimeException(
434                             "Failed to encode APK Signature Scheme v3 signer cert", e);
435                 }
436             } else {
437                 // we have some signing history, make sure that the root of the history is the same
438                 // as our v1/v2 signer
439                 try {
440                     lineage = lineage.getSubLineage(oldSignerCert);
441                     if (lineage.size() != 1) {
442                         // the v1/v2 signer was found, but not at the root of the lineage
443                         result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
444                     }
445                 } catch (IllegalArgumentException e) {
446                     // the v1/v2 signer was not found in the lineage
447                     result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
448                 }
449             }
450         }
451 
452 
453         // If there is a v4 scheme signer, make sure that their certificates match.
454         // The apkDigest field in the v4 signature should match the selected v2/v3.
455         if (result.isVerifiedUsingV4Scheme()) {
456             List<Result.V4SchemeSignerInfo> v4Signers = result.getV4SchemeSigners();
457             if (v4Signers.size() != 1) {
458                 result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS);
459             }
460 
461             List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> digestsFromV4 =
462                     v4Signers.get(0).getContentDigests();
463             if (digestsFromV4.size() != 1) {
464                 result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH);
465             }
466             final byte[] digestFromV4 = digestsFromV4.get(0).getValue();
467 
468             if (result.isVerifiedUsingV3Scheme()) {
469                 List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
470                 if (v3Signers.size() != 1) {
471                     result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS);
472                 }
473 
474                 // Compare certificates.
475                 checkV4Certificate(v4Signers.get(0).mCerts, v3Signers.get(0).mCerts, result);
476 
477                 // Compare digests.
478                 final byte[] digestFromV3 = pickBestDigestForV4(
479                         v3Signers.get(0).getContentDigests());
480                 if (!Arrays.equals(digestFromV4, digestFromV3)) {
481                     result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH);
482                 }
483             } else if (result.isVerifiedUsingV2Scheme()) {
484                 List<Result.V2SchemeSignerInfo> v2Signers = result.getV2SchemeSigners();
485                 if (v2Signers.size() != 1) {
486                     result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS);
487                 }
488 
489                 // Compare certificates.
490                 checkV4Certificate(v4Signers.get(0).mCerts, v2Signers.get(0).mCerts, result);
491 
492                 // Compare digests.
493                 final byte[] digestFromV2 = pickBestDigestForV4(
494                         v2Signers.get(0).getContentDigests());
495                 if (!Arrays.equals(digestFromV4, digestFromV2)) {
496                     result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH);
497                 }
498             } else {
499                 throw new RuntimeException("V4 signature must be also verified with V2/V3");
500             }
501         }
502 
503         // If the targetSdkVersion has a minimum required signature scheme version then verify
504         // that the APK was signed with at least that version.
505         try {
506             if (androidManifest == null) {
507                 androidManifest = getAndroidManifestFromApk(apk, zipSections);
508             }
509         } catch (ApkFormatException e) {
510             // If the manifest is not available then skip the minimum signature scheme requirement
511             // to support bundle verification.
512         }
513         if (androidManifest != null) {
514             int targetSdkVersion = getTargetSdkVersionFromBinaryAndroidManifest(
515                     androidManifest.slice());
516             int minSchemeVersion = getMinimumSignatureSchemeVersionForTargetSdk(targetSdkVersion);
517             // The platform currently only enforces a single minimum signature scheme version, but
518             // when later platform versions support another minimum version this will need to be
519             // expanded to verify the minimum based on the target and maximum SDK version.
520             if (minSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME
521                     && maxSdkVersion >= targetSdkVersion) {
522                 switch (minSchemeVersion) {
523                     case VERSION_APK_SIGNATURE_SCHEME_V2:
524                         if (result.isVerifiedUsingV2Scheme()) {
525                             break;
526                         }
527                         // Allow this case to fall through to the next as a signature satisfying a
528                         // later scheme version will also satisfy this requirement.
529                     case VERSION_APK_SIGNATURE_SCHEME_V3:
530                         if (result.isVerifiedUsingV3Scheme()) {
531                             break;
532                         }
533                         result.addError(Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET,
534                                 targetSdkVersion,
535                                 minSchemeVersion);
536                 }
537             }
538         }
539 
540         if (result.containsErrors()) {
541             return result;
542         }
543 
544         // Verified
545         result.setVerified();
546         if (result.isVerifiedUsingV3Scheme()) {
547             List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
548             result.addSignerCertificate(v3Signers.get(v3Signers.size() - 1).getCertificate());
549         } else if (result.isVerifiedUsingV2Scheme()) {
550             for (Result.V2SchemeSignerInfo signerInfo : result.getV2SchemeSigners()) {
551                 result.addSignerCertificate(signerInfo.getCertificate());
552             }
553         } else if (result.isVerifiedUsingV1Scheme()) {
554             for (Result.V1SchemeSignerInfo signerInfo : result.getV1SchemeSigners()) {
555                 result.addSignerCertificate(signerInfo.getCertificate());
556             }
557         } else {
558             throw new RuntimeException(
559                     "APK verified, but has not verified using any of v1, v2 or v3 schemes");
560         }
561 
562         return result;
563     }
564 
565     /**
566      * Verifies and returns the minimum SDK version, either as provided to the builder or as read
567      * from the {@code apk}'s AndroidManifest.xml.
568      */
verifyAndGetMinSdkVersion(DataSource apk, ApkUtils.ZipSections zipSections)569     private int verifyAndGetMinSdkVersion(DataSource apk, ApkUtils.ZipSections zipSections)
570             throws ApkFormatException, IOException {
571         if (mMinSdkVersion != null) {
572             if (mMinSdkVersion < 0) {
573                 throw new IllegalArgumentException(
574                         "minSdkVersion must not be negative: " + mMinSdkVersion);
575             }
576             if ((mMinSdkVersion != null) && (mMinSdkVersion > mMaxSdkVersion)) {
577                 throw new IllegalArgumentException(
578                         "minSdkVersion (" + mMinSdkVersion + ") > maxSdkVersion (" + mMaxSdkVersion
579                                 + ")");
580             }
581             return mMinSdkVersion;
582         }
583 
584         ByteBuffer androidManifest = null;
585         // Need to obtain minSdkVersion from the APK's AndroidManifest.xml
586         if (androidManifest == null) {
587             androidManifest = getAndroidManifestFromApk(apk, zipSections);
588         }
589         int minSdkVersion =
590                 ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest.slice());
591         if (minSdkVersion > mMaxSdkVersion) {
592             throw new IllegalArgumentException(
593                     "minSdkVersion from APK (" + minSdkVersion + ") > maxSdkVersion ("
594                             + mMaxSdkVersion + ")");
595         }
596         return minSdkVersion;
597     }
598 
599     /**
600      * Returns the mapping of signature scheme version to signature scheme name for all signature
601      * schemes starting from V2 supported by the {@code maxSdkVersion}.
602      */
getSupportedSchemeNames(int maxSdkVersion)603     private static Map<Integer, String> getSupportedSchemeNames(int maxSdkVersion) {
604         Map<Integer, String> supportedSchemeNames;
605         if (maxSdkVersion >= AndroidSdkVersion.P) {
606             supportedSchemeNames = SUPPORTED_APK_SIG_SCHEME_NAMES;
607         } else if (maxSdkVersion >= AndroidSdkVersion.N) {
608             supportedSchemeNames = new HashMap<>(1);
609             supportedSchemeNames.put(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2,
610                     SUPPORTED_APK_SIG_SCHEME_NAMES.get(
611                             ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2));
612         } else {
613             supportedSchemeNames = Collections.emptyMap();
614         }
615         return supportedSchemeNames;
616     }
617 
618     /**
619      * Verifies the APK's source stamp signature and returns the result of the verification.
620      *
621      * <p>The APK's source stamp can be considered verified if the result's {@link
622      * Result#isVerified} returns {@code true}. The details of the source stamp verification can
623      * be obtained from the result's {@link Result#getSourceStampInfo()}} including the success or
624      * failure cause from {@link Result.SourceStampInfo#getSourceStampVerificationStatus()}. If the
625      * verification fails additional details regarding the failure can be obtained from {@link
626      * Result#getAllErrors()}}.
627      */
verifySourceStamp()628     public Result verifySourceStamp() {
629         return verifySourceStamp(null);
630     }
631 
632     /**
633      * Verifies the APK's source stamp signature, including verification that the SHA-256 digest of
634      * the stamp signing certificate matches the {@code expectedCertDigest}, and returns the result
635      * of the verification.
636      *
637      * <p>A value of {@code null} for the {@code expectedCertDigest} will verify the source stamp,
638      * if present, without verifying the actual source stamp certificate used to sign the source
639      * stamp. This can be used to verify an APK contains a properly signed source stamp without
640      * verifying a particular signer.
641      *
642      * @see #verifySourceStamp()
643      */
verifySourceStamp(String expectedCertDigest)644     public Result verifySourceStamp(String expectedCertDigest) {
645         Closeable in = null;
646         try {
647             DataSource apk;
648             if (mApkDataSource != null) {
649                 apk = mApkDataSource;
650             } else if (mApkFile != null) {
651                 RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
652                 in = f;
653                 apk = DataSources.asDataSource(f, 0, f.length());
654             } else {
655                 throw new IllegalStateException("APK not provided");
656             }
657             return verifySourceStamp(apk, expectedCertDigest);
658         } catch (IOException e) {
659             return createSourceStampResultWithError(
660                     Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR,
661                     Issue.UNEXPECTED_EXCEPTION, e);
662         } finally {
663             if (in != null) {
664                 try {
665                     in.close();
666                 } catch (IOException ignored) {
667                 }
668             }
669         }
670     }
671 
672     /**
673      * Verifies the provided {@code apk}'s source stamp signature, including verification of the
674      * SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and
675      * returns the result of the verification.
676      *
677      * @see #verifySourceStamp(String)
678      */
verifySourceStamp(DataSource apk, String expectedCertDigest)679     private Result verifySourceStamp(DataSource apk, String expectedCertDigest) {
680         try {
681             ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
682             int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections);
683 
684             // Attempt to obtain the source stamp's certificate digest from the APK.
685             List<CentralDirectoryRecord> cdRecords =
686                     V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections);
687             CentralDirectoryRecord sourceStampCdRecord = null;
688             for (CentralDirectoryRecord cdRecord : cdRecords) {
689                 if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) {
690                     sourceStampCdRecord = cdRecord;
691                     break;
692                 }
693             }
694 
695             // If the source stamp's certificate digest is not available within the APK then the
696             // source stamp cannot be verified; check if a source stamp signing block is in the
697             // APK's signature block to determine the appropriate status to return.
698             if (sourceStampCdRecord == null) {
699                 boolean stampSigningBlockFound;
700                 try {
701                     ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(
702                             ApkSigningBlockUtils.VERSION_SOURCE_STAMP);
703                     ApkSigningBlockUtils.findSignature(apk, zipSections,
704                             SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID, result);
705                     stampSigningBlockFound = true;
706                 } catch (ApkSigningBlockUtils.SignatureNotFoundException e) {
707                     stampSigningBlockFound = false;
708                 }
709                 if (stampSigningBlockFound) {
710                     return createSourceStampResultWithError(
711                             Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_NOT_VERIFIED,
712                             Issue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST);
713                 } else {
714                     return createSourceStampResultWithError(
715                             Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_MISSING,
716                             Issue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING);
717                 }
718             }
719 
720             // Verify that the contents of the source stamp certificate digest match the expected
721             // value, if provided.
722             byte[] sourceStampCertificateDigest =
723                     LocalFileRecord.getUncompressedData(
724                             apk,
725                             sourceStampCdRecord,
726                             zipSections.getZipCentralDirectoryOffset());
727             if (expectedCertDigest != null) {
728                 String actualCertDigest = ApkSigningBlockUtils.toHex(sourceStampCertificateDigest);
729                 if (!expectedCertDigest.equalsIgnoreCase(actualCertDigest)) {
730                     return createSourceStampResultWithError(
731                             Result.SourceStampInfo.SourceStampVerificationStatus
732                                     .CERT_DIGEST_MISMATCH,
733                             Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, actualCertDigest,
734                             expectedCertDigest);
735                 }
736             }
737 
738             Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests =
739                     new HashMap<>();
740             Map<Integer, String> supportedSchemeNames = getSupportedSchemeNames(mMaxSdkVersion);
741             Set<Integer> foundApkSigSchemeIds = new HashSet<>(2);
742 
743             Result result = new Result();
744             ApkSigningBlockUtils.Result v3Result = null;
745             if (mMaxSdkVersion >= AndroidSdkVersion.P) {
746                 v3Result = getApkContentDigests(apk, zipSections, foundApkSigSchemeIds,
747                         supportedSchemeNames, signatureSchemeApkContentDigests,
748                         VERSION_APK_SIGNATURE_SCHEME_V3,
749                         Math.max(minSdkVersion, AndroidSdkVersion.P));
750                 if (v3Result != null && v3Result.containsErrors()) {
751                     result.mergeFrom(v3Result);
752                     return mergeSourceStampResult(
753                             Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR,
754                             result);
755                 }
756             }
757 
758             ApkSigningBlockUtils.Result v2Result = null;
759             if (mMaxSdkVersion >= AndroidSdkVersion.N && (minSdkVersion < AndroidSdkVersion.P
760                     || foundApkSigSchemeIds.isEmpty())) {
761                 v2Result = getApkContentDigests(apk, zipSections, foundApkSigSchemeIds,
762                         supportedSchemeNames, signatureSchemeApkContentDigests,
763                         VERSION_APK_SIGNATURE_SCHEME_V2,
764                         Math.max(minSdkVersion, AndroidSdkVersion.N));
765                 if (v2Result != null && v2Result.containsErrors()) {
766                     result.mergeFrom(v2Result);
767                     return mergeSourceStampResult(
768                             Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR,
769                             result);
770                 }
771             }
772 
773             if (minSdkVersion < AndroidSdkVersion.N || foundApkSigSchemeIds.isEmpty()) {
774                 signatureSchemeApkContentDigests.put(VERSION_JAR_SIGNATURE_SCHEME,
775                         getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections));
776             }
777 
778             ApkSigResult sourceStampResult =
779                     V2SourceStampVerifier.verify(
780                             apk,
781                             zipSections,
782                             sourceStampCertificateDigest,
783                             signatureSchemeApkContentDigests,
784                             minSdkVersion,
785                             mMaxSdkVersion);
786             result.mergeFrom(sourceStampResult);
787             // Since the caller is only seeking to verify the source stamp the Result can be marked
788             // as verified if the source stamp verification was successful.
789             if (sourceStampResult.verified) {
790                 result.setVerified();
791             } else {
792                 // To prevent APK signature verification with a failed / missing source stamp the
793                 // source stamp verification will only log warnings; to allow the caller to capture
794                 // the failure reason treat all warnings as errors.
795                 result.setWarningsAsErrors(true);
796             }
797             return result;
798         } catch (ApkFormatException | IOException | ZipFormatException e) {
799             return createSourceStampResultWithError(
800                     Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR,
801                     Issue.MALFORMED_APK, e);
802         } catch (NoSuchAlgorithmException e) {
803             return createSourceStampResultWithError(
804                     Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR,
805                     Issue.UNEXPECTED_EXCEPTION, e);
806         } catch (SignatureNotFoundException e) {
807             return createSourceStampResultWithError(
808                     Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_NOT_VERIFIED,
809                     Issue.SOURCE_STAMP_SIG_MISSING);
810         }
811     }
812 
813     /**
814      * Creates and returns a {@code Result} that can be returned for source stamp verification
815      * with the provided source stamp {@code verificationStatus}, and logs an error for the
816      * specified {@code issue} and {@code params}.
817      */
createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus verificationStatus, Issue issue, Object... params)818     private static Result createSourceStampResultWithError(
819             Result.SourceStampInfo.SourceStampVerificationStatus verificationStatus, Issue issue,
820             Object... params) {
821         Result result = new Result();
822         result.addError(issue, params);
823         return mergeSourceStampResult(verificationStatus, result);
824     }
825 
826     /**
827      * Creates a new {@link Result.SourceStampInfo} under the provided {@code result} and sets the
828      * source stamp status to the provided {@code verificationStatus}.
829      */
mergeSourceStampResult( Result.SourceStampInfo.SourceStampVerificationStatus verificationStatus, Result result)830     private static Result mergeSourceStampResult(
831             Result.SourceStampInfo.SourceStampVerificationStatus verificationStatus,
832             Result result) {
833         result.mSourceStampInfo = new Result.SourceStampInfo(verificationStatus);
834         return result;
835     }
836 
837     /**
838      * Obtains the APK content digest(s) and adds them to the provided {@code
839      * sigSchemeApkContentDigests}, returning an {@code ApkSigningBlockUtils.Result} that can be
840      * merged with a {@code Result} to notify the client of any errors.
841      *
842      * <p>Note, this method currently only supports signature scheme V2 and V3; to obtain the
843      * content digests for V1 signatures use {@link
844      * #getApkContentDigestFromV1SigningScheme(List, DataSource, ApkUtils.ZipSections)}. If a
845      * signature scheme version other than V2 or V3 is provided a {@code null} value will be
846      * returned.
847      */
getApkContentDigests(DataSource apk, ApkUtils.ZipSections zipSections, Set<Integer> foundApkSigSchemeIds, Map<Integer, String> supportedSchemeNames, Map<Integer, Map<ContentDigestAlgorithm, byte[]>> sigSchemeApkContentDigests, int apkSigSchemeVersion, int minSdkVersion)848     private ApkSigningBlockUtils.Result getApkContentDigests(DataSource apk,
849             ApkUtils.ZipSections zipSections, Set<Integer> foundApkSigSchemeIds,
850             Map<Integer, String> supportedSchemeNames,
851             Map<Integer, Map<ContentDigestAlgorithm, byte[]>> sigSchemeApkContentDigests,
852             int apkSigSchemeVersion, int minSdkVersion)
853             throws IOException, NoSuchAlgorithmException {
854         if (!(apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2
855                 || apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V3)) {
856             return null;
857         }
858         ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(apkSigSchemeVersion);
859         SignatureInfo signatureInfo;
860         try {
861             int sigSchemeBlockId = apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V3
862                     ? V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID
863                     : V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID;
864             signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections,
865                     sigSchemeBlockId, result);
866         } catch (ApkSigningBlockUtils.SignatureNotFoundException e) {
867             return null;
868         }
869         foundApkSigSchemeIds.add(apkSigSchemeVersion);
870 
871         Set<ContentDigestAlgorithm> contentDigestsToVerify = new HashSet<>(1);
872         if (apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2) {
873             V2SchemeVerifier.parseSigners(signatureInfo.signatureBlock,
874                     contentDigestsToVerify, supportedSchemeNames,
875                     foundApkSigSchemeIds, minSdkVersion, mMaxSdkVersion, result);
876         } else {
877             V3SchemeVerifier.parseSigners(signatureInfo.signatureBlock,
878                     contentDigestsToVerify, result);
879         }
880         Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new EnumMap<>(
881                 ContentDigestAlgorithm.class);
882         for (ApkSigningBlockUtils.Result.SignerInfo signerInfo : result.signers) {
883             for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest :
884                     signerInfo.contentDigests) {
885                 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(
886                         contentDigest.getSignatureAlgorithmId());
887                 if (signatureAlgorithm == null) {
888                     continue;
889                 }
890                 apkContentDigests.put(signatureAlgorithm.getContentDigestAlgorithm(),
891                         contentDigest.getValue());
892             }
893         }
894         sigSchemeApkContentDigests.put(apkSigSchemeVersion, apkContentDigests);
895         return result;
896     }
897 
checkV4Certificate(List<X509Certificate> v4Certs, List<X509Certificate> v2v3Certs, Result result)898     private static void checkV4Certificate(List<X509Certificate> v4Certs,
899             List<X509Certificate> v2v3Certs, Result result) {
900         try {
901             byte[] v4Cert = v4Certs.get(0).getEncoded();
902             byte[] cert = v2v3Certs.get(0).getEncoded();
903             if (!Arrays.equals(cert, v4Cert)) {
904                 result.addError(Issue.V4_SIG_V2_V3_SIGNERS_MISMATCH);
905             }
906         } catch (CertificateEncodingException e) {
907             throw new RuntimeException("Failed to encode APK signer cert", e);
908         }
909     }
910 
pickBestDigestForV4( List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> contentDigests)911     private static byte[] pickBestDigestForV4(
912             List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> contentDigests) {
913         Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new HashMap<>();
914         collectApkContentDigests(contentDigests, apkContentDigests);
915         return ApkSigningBlockUtils.pickBestDigestForV4(apkContentDigests);
916     }
917 
getApkContentDigestsFromSigningSchemeResult( ApkSigningBlockUtils.Result apkSigningSchemeResult)918     private static Map<ContentDigestAlgorithm, byte[]> getApkContentDigestsFromSigningSchemeResult(
919             ApkSigningBlockUtils.Result apkSigningSchemeResult) {
920         Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new HashMap<>();
921         for (ApkSigningBlockUtils.Result.SignerInfo signerInfo : apkSigningSchemeResult.signers) {
922             collectApkContentDigests(signerInfo.contentDigests, apkContentDigests);
923         }
924         return apkContentDigests;
925     }
926 
getApkContentDigestFromV1SigningScheme( List<CentralDirectoryRecord> cdRecords, DataSource apk, ApkUtils.ZipSections zipSections)927     private static Map<ContentDigestAlgorithm, byte[]> getApkContentDigestFromV1SigningScheme(
928             List<CentralDirectoryRecord> cdRecords,
929             DataSource apk,
930             ApkUtils.ZipSections zipSections)
931             throws IOException, ApkFormatException {
932         CentralDirectoryRecord manifestCdRecord = null;
933         Map<ContentDigestAlgorithm, byte[]> v1ContentDigest = new EnumMap<>(
934                 ContentDigestAlgorithm.class);
935         for (CentralDirectoryRecord cdRecord : cdRecords) {
936             if (MANIFEST_ENTRY_NAME.equals(cdRecord.getName())) {
937                 manifestCdRecord = cdRecord;
938                 break;
939             }
940         }
941         if (manifestCdRecord == null) {
942             // No JAR signing manifest file found. For SourceStamp verification, returning an empty
943             // digest is enough since this would affect the final digest signed by the stamp, and
944             // thus an empty digest will invalidate that signature.
945             return v1ContentDigest;
946         }
947         try {
948             byte[] manifestBytes =
949                     LocalFileRecord.getUncompressedData(
950                             apk, manifestCdRecord, zipSections.getZipCentralDirectoryOffset());
951             v1ContentDigest.put(
952                     ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(manifestBytes));
953             return v1ContentDigest;
954         } catch (ZipFormatException e) {
955             throw new ApkFormatException("Failed to read APK", e);
956         }
957     }
958 
collectApkContentDigests( List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> contentDigests, Map<ContentDigestAlgorithm, byte[]> apkContentDigests)959     private static void collectApkContentDigests(
960             List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> contentDigests,
961             Map<ContentDigestAlgorithm, byte[]> apkContentDigests) {
962         for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : contentDigests) {
963             SignatureAlgorithm signatureAlgorithm =
964                     SignatureAlgorithm.findById(contentDigest.getSignatureAlgorithmId());
965             if (signatureAlgorithm == null) {
966                 continue;
967             }
968             ContentDigestAlgorithm contentDigestAlgorithm =
969                     signatureAlgorithm.getContentDigestAlgorithm();
970             apkContentDigests.put(contentDigestAlgorithm, contentDigest.getValue());
971         }
972 
973     }
974 
getAndroidManifestFromApk( DataSource apk, ApkUtils.ZipSections zipSections)975     private static ByteBuffer getAndroidManifestFromApk(
976             DataSource apk, ApkUtils.ZipSections zipSections)
977             throws IOException, ApkFormatException {
978         List<CentralDirectoryRecord> cdRecords =
979                 V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections);
980         try {
981             return ApkSigner.getAndroidManifestFromApk(
982                     cdRecords,
983                     apk.slice(0, zipSections.getZipCentralDirectoryOffset()));
984         } catch (ZipFormatException e) {
985             throw new ApkFormatException("Failed to read AndroidManifest.xml", e);
986         }
987     }
988 
getMinimumSignatureSchemeVersionForTargetSdk(int targetSdkVersion)989     private static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdkVersion) {
990         if (targetSdkVersion >= AndroidSdkVersion.R) {
991             return VERSION_APK_SIGNATURE_SCHEME_V2;
992         }
993         return VERSION_JAR_SIGNATURE_SCHEME;
994     }
995 
996     /**
997      * Result of verifying an APKs signatures. The APK can be considered verified iff
998      * {@link #isVerified()} returns {@code true}.
999      */
1000     public static class Result {
1001         private final List<IssueWithParams> mErrors = new ArrayList<>();
1002         private final List<IssueWithParams> mWarnings = new ArrayList<>();
1003         private final List<X509Certificate> mSignerCerts = new ArrayList<>();
1004         private final List<V1SchemeSignerInfo> mV1SchemeSigners = new ArrayList<>();
1005         private final List<V1SchemeSignerInfo> mV1SchemeIgnoredSigners = new ArrayList<>();
1006         private final List<V2SchemeSignerInfo> mV2SchemeSigners = new ArrayList<>();
1007         private final List<V3SchemeSignerInfo> mV3SchemeSigners = new ArrayList<>();
1008         private final List<V4SchemeSignerInfo> mV4SchemeSigners = new ArrayList<>();
1009         private SourceStampInfo mSourceStampInfo;
1010 
1011         private boolean mVerified;
1012         private boolean mVerifiedUsingV1Scheme;
1013         private boolean mVerifiedUsingV2Scheme;
1014         private boolean mVerifiedUsingV3Scheme;
1015         private boolean mVerifiedUsingV4Scheme;
1016         private boolean mSourceStampVerified;
1017         private boolean mWarningsAsErrors;
1018         private SigningCertificateLineage mSigningCertificateLineage;
1019 
1020         /**
1021          * Returns {@code true} if the APK's signatures verified.
1022          */
isVerified()1023         public boolean isVerified() {
1024             return mVerified;
1025         }
1026 
setVerified()1027         private void setVerified() {
1028             mVerified = true;
1029         }
1030 
1031         /**
1032          * Returns {@code true} if the APK's JAR signatures verified.
1033          */
isVerifiedUsingV1Scheme()1034         public boolean isVerifiedUsingV1Scheme() {
1035             return mVerifiedUsingV1Scheme;
1036         }
1037 
1038         /**
1039          * Returns {@code true} if the APK's APK Signature Scheme v2 signatures verified.
1040          */
isVerifiedUsingV2Scheme()1041         public boolean isVerifiedUsingV2Scheme() {
1042             return mVerifiedUsingV2Scheme;
1043         }
1044 
1045         /**
1046          * Returns {@code true} if the APK's APK Signature Scheme v3 signature verified.
1047          */
isVerifiedUsingV3Scheme()1048         public boolean isVerifiedUsingV3Scheme() {
1049             return mVerifiedUsingV3Scheme;
1050         }
1051 
1052         /**
1053          * Returns {@code true} if the APK's APK Signature Scheme v4 signature verified.
1054          */
isVerifiedUsingV4Scheme()1055         public boolean isVerifiedUsingV4Scheme() {
1056             return mVerifiedUsingV4Scheme;
1057         }
1058 
1059         /**
1060          * Returns {@code true} if the APK's SourceStamp signature verified.
1061          */
isSourceStampVerified()1062         public boolean isSourceStampVerified() {
1063             return mSourceStampVerified;
1064         }
1065 
1066         /**
1067          * Returns the verified signers' certificates, one per signer.
1068          */
getSignerCertificates()1069         public List<X509Certificate> getSignerCertificates() {
1070             return mSignerCerts;
1071         }
1072 
addSignerCertificate(X509Certificate cert)1073         private void addSignerCertificate(X509Certificate cert) {
1074             mSignerCerts.add(cert);
1075         }
1076 
1077         /**
1078          * Returns information about JAR signers associated with the APK's signature. These are the
1079          * signers used by Android.
1080          *
1081          * @see #getV1SchemeIgnoredSigners()
1082          */
getV1SchemeSigners()1083         public List<V1SchemeSignerInfo> getV1SchemeSigners() {
1084             return mV1SchemeSigners;
1085         }
1086 
1087         /**
1088          * Returns information about JAR signers ignored by the APK's signature verification
1089          * process. These signers are ignored by Android. However, each signer's errors or warnings
1090          * will contain information about why they are ignored.
1091          *
1092          * @see #getV1SchemeSigners()
1093          */
getV1SchemeIgnoredSigners()1094         public List<V1SchemeSignerInfo> getV1SchemeIgnoredSigners() {
1095             return mV1SchemeIgnoredSigners;
1096         }
1097 
1098         /**
1099          * Returns information about APK Signature Scheme v2 signers associated with the APK's
1100          * signature.
1101          */
getV2SchemeSigners()1102         public List<V2SchemeSignerInfo> getV2SchemeSigners() {
1103             return mV2SchemeSigners;
1104         }
1105 
1106         /**
1107          * Returns information about APK Signature Scheme v3 signers associated with the APK's
1108          * signature.
1109          *
1110          * <note> Multiple signers represent different targeted platform versions, not
1111          * a signing identity of multiple signers.  APK Signature Scheme v3 only supports single
1112          * signer identities.</note>
1113          */
getV3SchemeSigners()1114         public List<V3SchemeSignerInfo> getV3SchemeSigners() {
1115             return mV3SchemeSigners;
1116         }
1117 
getV4SchemeSigners()1118         private List<V4SchemeSignerInfo> getV4SchemeSigners() {
1119             return mV4SchemeSigners;
1120         }
1121 
1122         /**
1123          * Returns information about SourceStamp associated with the APK's signature.
1124          */
getSourceStampInfo()1125         public SourceStampInfo getSourceStampInfo() {
1126             return mSourceStampInfo;
1127         }
1128 
1129         /**
1130          * Returns the combined SigningCertificateLineage associated with this APK's APK Signature
1131          * Scheme v3 signing block.
1132          */
getSigningCertificateLineage()1133         public SigningCertificateLineage getSigningCertificateLineage() {
1134             return mSigningCertificateLineage;
1135         }
1136 
addError(Issue msg, Object... parameters)1137         void addError(Issue msg, Object... parameters) {
1138             mErrors.add(new IssueWithParams(msg, parameters));
1139         }
1140 
addWarning(Issue msg, Object... parameters)1141         void addWarning(Issue msg, Object... parameters) {
1142             mWarnings.add(new IssueWithParams(msg, parameters));
1143         }
1144 
1145         /**
1146          * Sets whether warnings should be treated as errors.
1147          */
setWarningsAsErrors(boolean value)1148         void setWarningsAsErrors(boolean value) {
1149             mWarningsAsErrors = value;
1150         }
1151 
1152         /**
1153          * Returns errors encountered while verifying the APK's signatures.
1154          */
getErrors()1155         public List<IssueWithParams> getErrors() {
1156             if (!mWarningsAsErrors) {
1157                 return mErrors;
1158             } else {
1159                 List<IssueWithParams> allErrors = new ArrayList<>();
1160                 allErrors.addAll(mErrors);
1161                 allErrors.addAll(mWarnings);
1162                 return allErrors;
1163             }
1164         }
1165 
1166         /**
1167          * Returns warnings encountered while verifying the APK's signatures.
1168          */
getWarnings()1169         public List<IssueWithParams> getWarnings() {
1170             return mWarnings;
1171         }
1172 
mergeFrom(V1SchemeVerifier.Result source)1173         private void mergeFrom(V1SchemeVerifier.Result source) {
1174             mVerifiedUsingV1Scheme = source.verified;
1175             mErrors.addAll(source.getErrors());
1176             mWarnings.addAll(source.getWarnings());
1177             for (V1SchemeVerifier.Result.SignerInfo signer : source.signers) {
1178                 mV1SchemeSigners.add(new V1SchemeSignerInfo(signer));
1179             }
1180             for (V1SchemeVerifier.Result.SignerInfo signer : source.ignoredSigners) {
1181                 mV1SchemeIgnoredSigners.add(new V1SchemeSignerInfo(signer));
1182             }
1183         }
1184 
mergeFrom(ApkSigResult source)1185         private void mergeFrom(ApkSigResult source) {
1186             switch (source.signatureSchemeVersion) {
1187                 case ApkSigningBlockUtils.VERSION_SOURCE_STAMP:
1188                     mSourceStampVerified = source.verified;
1189                     if (!source.mSigners.isEmpty()) {
1190                         mSourceStampInfo = new SourceStampInfo(source.mSigners.get(0));
1191                     }
1192                     break;
1193                 default:
1194                     throw new IllegalArgumentException(
1195                             "Unknown ApkSigResult Signing Block Scheme Id "
1196                                     + source.signatureSchemeVersion);
1197             }
1198         }
1199 
mergeFrom(ApkSigningBlockUtils.Result source)1200         private void mergeFrom(ApkSigningBlockUtils.Result source) {
1201             switch (source.signatureSchemeVersion) {
1202                 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
1203                     mVerifiedUsingV2Scheme = source.verified;
1204                     for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) {
1205                         mV2SchemeSigners.add(new V2SchemeSignerInfo(signer));
1206                     }
1207                     break;
1208                 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3:
1209                     mVerifiedUsingV3Scheme = source.verified;
1210                     for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) {
1211                         mV3SchemeSigners.add(new V3SchemeSignerInfo(signer));
1212                     }
1213                     mSigningCertificateLineage = source.signingCertificateLineage;
1214                     break;
1215                 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4:
1216                     mVerifiedUsingV4Scheme = source.verified;
1217                     for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) {
1218                         mV4SchemeSigners.add(new V4SchemeSignerInfo(signer));
1219                     }
1220                     break;
1221                 case ApkSigningBlockUtils.VERSION_SOURCE_STAMP:
1222                     mSourceStampVerified = source.verified;
1223                     if (!source.signers.isEmpty()) {
1224                         mSourceStampInfo = new SourceStampInfo(source.signers.get(0));
1225                     }
1226                     break;
1227                 default:
1228                     throw new IllegalArgumentException("Unknown Signing Block Scheme Id");
1229             }
1230         }
1231 
1232         /**
1233          * Returns {@code true} if an error was encountered while verifying the APK. Any error
1234          * prevents the APK from being considered verified.
1235          */
containsErrors()1236         public boolean containsErrors() {
1237             if (!mErrors.isEmpty()) {
1238                 return true;
1239             }
1240             if (mWarningsAsErrors && !mWarnings.isEmpty()) {
1241                 return true;
1242             }
1243             if (!mV1SchemeSigners.isEmpty()) {
1244                 for (V1SchemeSignerInfo signer : mV1SchemeSigners) {
1245                     if (signer.containsErrors()) {
1246                         return true;
1247                     }
1248                     if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) {
1249                         return true;
1250                     }
1251                 }
1252             }
1253             if (!mV2SchemeSigners.isEmpty()) {
1254                 for (V2SchemeSignerInfo signer : mV2SchemeSigners) {
1255                     if (signer.containsErrors()) {
1256                         return true;
1257                     }
1258                     if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) {
1259                         return true;
1260                     }
1261                 }
1262             }
1263             if (!mV3SchemeSigners.isEmpty()) {
1264                 for (V3SchemeSignerInfo signer : mV3SchemeSigners) {
1265                     if (signer.containsErrors()) {
1266                         return true;
1267                     }
1268                     if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) {
1269                         return true;
1270                     }
1271                 }
1272             }
1273             if (mSourceStampInfo != null) {
1274                 if (mSourceStampInfo.containsErrors()) {
1275                     return true;
1276                 }
1277                 if (mWarningsAsErrors && !mSourceStampInfo.getWarnings().isEmpty()) {
1278                     return true;
1279                 }
1280             }
1281 
1282             return false;
1283         }
1284 
1285         /**
1286          * Returns all errors for this result, including any errors from signature scheme signers
1287          * and the source stamp.
1288          */
getAllErrors()1289         public List<IssueWithParams> getAllErrors() {
1290             List<IssueWithParams> errors = new ArrayList<>();
1291             errors.addAll(mErrors);
1292             if (mWarningsAsErrors) {
1293                 errors.addAll(mWarnings);
1294             }
1295             if (!mV1SchemeSigners.isEmpty()) {
1296                 for (V1SchemeSignerInfo signer : mV1SchemeSigners) {
1297                     errors.addAll(signer.mErrors);
1298                     if (mWarningsAsErrors) {
1299                         errors.addAll(signer.getWarnings());
1300                     }
1301                 }
1302             }
1303             if (!mV2SchemeSigners.isEmpty()) {
1304                 for (V2SchemeSignerInfo signer : mV2SchemeSigners) {
1305                     errors.addAll(signer.mErrors);
1306                     if (mWarningsAsErrors) {
1307                         errors.addAll(signer.getWarnings());
1308                     }
1309                 }
1310             }
1311             if (!mV3SchemeSigners.isEmpty()) {
1312                 for (V3SchemeSignerInfo signer : mV3SchemeSigners) {
1313                     errors.addAll(signer.mErrors);
1314                     if (mWarningsAsErrors) {
1315                         errors.addAll(signer.getWarnings());
1316                     }
1317                 }
1318             }
1319             if (mSourceStampInfo != null) {
1320                 errors.addAll(mSourceStampInfo.getErrors());
1321                 if (mWarningsAsErrors) {
1322                     errors.addAll(mSourceStampInfo.getWarnings());
1323                 }
1324             }
1325             return errors;
1326         }
1327 
1328         /**
1329          * Information about a JAR signer associated with the APK's signature.
1330          */
1331         public static class V1SchemeSignerInfo {
1332             private final String mName;
1333             private final List<X509Certificate> mCertChain;
1334             private final String mSignatureBlockFileName;
1335             private final String mSignatureFileName;
1336 
1337             private final List<IssueWithParams> mErrors;
1338             private final List<IssueWithParams> mWarnings;
1339 
V1SchemeSignerInfo(V1SchemeVerifier.Result.SignerInfo result)1340             private V1SchemeSignerInfo(V1SchemeVerifier.Result.SignerInfo result) {
1341                 mName = result.name;
1342                 mCertChain = result.certChain;
1343                 mSignatureBlockFileName = result.signatureBlockFileName;
1344                 mSignatureFileName = result.signatureFileName;
1345                 mErrors = result.getErrors();
1346                 mWarnings = result.getWarnings();
1347             }
1348 
1349             /**
1350              * Returns a user-friendly name of the signer.
1351              */
getName()1352             public String getName() {
1353                 return mName;
1354             }
1355 
1356             /**
1357              * Returns the name of the JAR entry containing this signer's JAR signature block file.
1358              */
getSignatureBlockFileName()1359             public String getSignatureBlockFileName() {
1360                 return mSignatureBlockFileName;
1361             }
1362 
1363             /**
1364              * Returns the name of the JAR entry containing this signer's JAR signature file.
1365              */
getSignatureFileName()1366             public String getSignatureFileName() {
1367                 return mSignatureFileName;
1368             }
1369 
1370             /**
1371              * Returns this signer's signing certificate or {@code null} if not available. The
1372              * certificate is guaranteed to be available if no errors were encountered during
1373              * verification (see {@link #containsErrors()}.
1374              *
1375              * <p>This certificate contains the signer's public key.
1376              */
getCertificate()1377             public X509Certificate getCertificate() {
1378                 return mCertChain.isEmpty() ? null : mCertChain.get(0);
1379             }
1380 
1381             /**
1382              * Returns the certificate chain for the signer's public key. The certificate containing
1383              * the public key is first, followed by the certificate (if any) which issued the
1384              * signing certificate, and so forth. An empty list may be returned if an error was
1385              * encountered during verification (see {@link #containsErrors()}).
1386              */
getCertificateChain()1387             public List<X509Certificate> getCertificateChain() {
1388                 return mCertChain;
1389             }
1390 
1391             /**
1392              * Returns {@code true} if an error was encountered while verifying this signer's JAR
1393              * signature. Any error prevents the signer's signature from being considered verified.
1394              */
containsErrors()1395             public boolean containsErrors() {
1396                 return !mErrors.isEmpty();
1397             }
1398 
1399             /**
1400              * Returns errors encountered while verifying this signer's JAR signature. Any error
1401              * prevents the signer's signature from being considered verified.
1402              */
getErrors()1403             public List<IssueWithParams> getErrors() {
1404                 return mErrors;
1405             }
1406 
1407             /**
1408              * Returns warnings encountered while verifying this signer's JAR signature. Warnings
1409              * do not prevent the signer's signature from being considered verified.
1410              */
getWarnings()1411             public List<IssueWithParams> getWarnings() {
1412                 return mWarnings;
1413             }
1414 
addError(Issue msg, Object... parameters)1415             private void addError(Issue msg, Object... parameters) {
1416                 mErrors.add(new IssueWithParams(msg, parameters));
1417             }
1418         }
1419 
1420         /**
1421          * Information about an APK Signature Scheme v2 signer associated with the APK's signature.
1422          */
1423         public static class V2SchemeSignerInfo {
1424             private final int mIndex;
1425             private final List<X509Certificate> mCerts;
1426 
1427             private final List<IssueWithParams> mErrors;
1428             private final List<IssueWithParams> mWarnings;
1429             private final List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest>
1430                     mContentDigests;
1431 
V2SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result)1432             private V2SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) {
1433                 mIndex = result.index;
1434                 mCerts = result.certs;
1435                 mErrors = result.getErrors();
1436                 mWarnings = result.getWarnings();
1437                 mContentDigests = result.contentDigests;
1438             }
1439 
1440             /**
1441              * Returns this signer's {@code 0}-based index in the list of signers contained in the
1442              * APK's APK Signature Scheme v2 signature.
1443              */
getIndex()1444             public int getIndex() {
1445                 return mIndex;
1446             }
1447 
1448             /**
1449              * Returns this signer's signing certificate or {@code null} if not available. The
1450              * certificate is guaranteed to be available if no errors were encountered during
1451              * verification (see {@link #containsErrors()}.
1452              *
1453              * <p>This certificate contains the signer's public key.
1454              */
getCertificate()1455             public X509Certificate getCertificate() {
1456                 return mCerts.isEmpty() ? null : mCerts.get(0);
1457             }
1458 
1459             /**
1460              * Returns this signer's certificates. The first certificate is for the signer's public
1461              * key. An empty list may be returned if an error was encountered during verification
1462              * (see {@link #containsErrors()}).
1463              */
getCertificates()1464             public List<X509Certificate> getCertificates() {
1465                 return mCerts;
1466             }
1467 
addError(Issue msg, Object... parameters)1468             private void addError(Issue msg, Object... parameters) {
1469                 mErrors.add(new IssueWithParams(msg, parameters));
1470             }
1471 
containsErrors()1472             public boolean containsErrors() {
1473                 return !mErrors.isEmpty();
1474             }
1475 
getErrors()1476             public List<IssueWithParams> getErrors() {
1477                 return mErrors;
1478             }
1479 
getWarnings()1480             public List<IssueWithParams> getWarnings() {
1481                 return mWarnings;
1482             }
1483 
getContentDigests()1484             public List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> getContentDigests() {
1485                 return mContentDigests;
1486             }
1487         }
1488 
1489         /**
1490          * Information about an APK Signature Scheme v3 signer associated with the APK's signature.
1491          */
1492         public static class V3SchemeSignerInfo {
1493             private final int mIndex;
1494             private final List<X509Certificate> mCerts;
1495 
1496             private final List<IssueWithParams> mErrors;
1497             private final List<IssueWithParams> mWarnings;
1498             private final List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest>
1499                     mContentDigests;
1500 
V3SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result)1501             private V3SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) {
1502                 mIndex = result.index;
1503                 mCerts = result.certs;
1504                 mErrors = result.getErrors();
1505                 mWarnings = result.getWarnings();
1506                 mContentDigests = result.contentDigests;
1507             }
1508 
1509             /**
1510              * Returns this signer's {@code 0}-based index in the list of signers contained in the
1511              * APK's APK Signature Scheme v3 signature.
1512              */
getIndex()1513             public int getIndex() {
1514                 return mIndex;
1515             }
1516 
1517             /**
1518              * Returns this signer's signing certificate or {@code null} if not available. The
1519              * certificate is guaranteed to be available if no errors were encountered during
1520              * verification (see {@link #containsErrors()}.
1521              *
1522              * <p>This certificate contains the signer's public key.
1523              */
getCertificate()1524             public X509Certificate getCertificate() {
1525                 return mCerts.isEmpty() ? null : mCerts.get(0);
1526             }
1527 
1528             /**
1529              * Returns this signer's certificates. The first certificate is for the signer's public
1530              * key. An empty list may be returned if an error was encountered during verification
1531              * (see {@link #containsErrors()}).
1532              */
getCertificates()1533             public List<X509Certificate> getCertificates() {
1534                 return mCerts;
1535             }
1536 
containsErrors()1537             public boolean containsErrors() {
1538                 return !mErrors.isEmpty();
1539             }
1540 
getErrors()1541             public List<IssueWithParams> getErrors() {
1542                 return mErrors;
1543             }
1544 
getWarnings()1545             public List<IssueWithParams> getWarnings() {
1546                 return mWarnings;
1547             }
1548 
getContentDigests()1549             public List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> getContentDigests() {
1550                 return mContentDigests;
1551             }
1552         }
1553 
1554         /**
1555          * Information about an APK Signature Scheme V4 signer associated with the APK's
1556          * signature.
1557          */
1558         public static class V4SchemeSignerInfo {
1559             private final int mIndex;
1560             private final List<X509Certificate> mCerts;
1561 
1562             private final List<IssueWithParams> mErrors;
1563             private final List<IssueWithParams> mWarnings;
1564             private final List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest>
1565                     mContentDigests;
1566 
V4SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result)1567             private V4SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) {
1568                 mIndex = result.index;
1569                 mCerts = result.certs;
1570                 mErrors = result.getErrors();
1571                 mWarnings = result.getWarnings();
1572                 mContentDigests = result.contentDigests;
1573             }
1574 
1575             /**
1576              * Returns this signer's {@code 0}-based index in the list of signers contained in the
1577              * APK's APK Signature Scheme v3 signature.
1578              */
getIndex()1579             public int getIndex() {
1580                 return mIndex;
1581             }
1582 
1583             /**
1584              * Returns this signer's signing certificate or {@code null} if not available. The
1585              * certificate is guaranteed to be available if no errors were encountered during
1586              * verification (see {@link #containsErrors()}.
1587              *
1588              * <p>This certificate contains the signer's public key.
1589              */
getCertificate()1590             public X509Certificate getCertificate() {
1591                 return mCerts.isEmpty() ? null : mCerts.get(0);
1592             }
1593 
1594             /**
1595              * Returns this signer's certificates. The first certificate is for the signer's public
1596              * key. An empty list may be returned if an error was encountered during verification
1597              * (see {@link #containsErrors()}).
1598              */
getCertificates()1599             public List<X509Certificate> getCertificates() {
1600                 return mCerts;
1601             }
1602 
containsErrors()1603             public boolean containsErrors() {
1604                 return !mErrors.isEmpty();
1605             }
1606 
getErrors()1607             public List<IssueWithParams> getErrors() {
1608                 return mErrors;
1609             }
1610 
getWarnings()1611             public List<IssueWithParams> getWarnings() {
1612                 return mWarnings;
1613             }
1614 
getContentDigests()1615             public List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> getContentDigests() {
1616                 return mContentDigests;
1617             }
1618         }
1619 
1620         /**
1621          * Information about SourceStamp associated with the APK's signature.
1622          */
1623         public static class SourceStampInfo {
1624             public enum SourceStampVerificationStatus {
1625                 /** The stamp is present and was successfully verified. */
1626                 STAMP_VERIFIED,
1627                 /** The stamp is present but failed verification. */
1628                 STAMP_VERIFICATION_FAILED,
1629                 /** The expected cert digest did not match the digest in the APK. */
1630                 CERT_DIGEST_MISMATCH,
1631                 /** The stamp is not present at all. */
1632                 STAMP_MISSING,
1633                 /** The stamp is at least partially present, but was not able to be verified. */
1634                 STAMP_NOT_VERIFIED,
1635                 /** The stamp was not able to be verified due to an unexpected error. */
1636                 VERIFICATION_ERROR
1637             }
1638 
1639             private final List<X509Certificate> mCertificates;
1640             private final List<X509Certificate> mCertificateLineage;
1641 
1642             private final List<IssueWithParams> mErrors;
1643             private final List<IssueWithParams> mWarnings;
1644 
1645             private final SourceStampVerificationStatus mSourceStampVerificationStatus;
1646 
SourceStampInfo(ApkSignerInfo result)1647             private SourceStampInfo(ApkSignerInfo result) {
1648                 mCertificates = result.certs;
1649                 mCertificateLineage = result.certificateLineage;
1650                 mErrors = ApkVerificationIssueAdapter.getIssuesFromVerificationIssues(
1651                         result.getErrors());
1652                 mWarnings = ApkVerificationIssueAdapter.getIssuesFromVerificationIssues(
1653                         result.getWarnings());
1654                 if (mErrors.isEmpty() && mWarnings.isEmpty()) {
1655                     mSourceStampVerificationStatus = SourceStampVerificationStatus.STAMP_VERIFIED;
1656                 } else {
1657                     mSourceStampVerificationStatus =
1658                             SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED;
1659                 }
1660             }
1661 
SourceStampInfo(SourceStampVerificationStatus sourceStampVerificationStatus)1662             SourceStampInfo(SourceStampVerificationStatus sourceStampVerificationStatus) {
1663                 mCertificates = Collections.emptyList();
1664                 mCertificateLineage = Collections.emptyList();
1665                 mErrors = Collections.emptyList();
1666                 mWarnings = Collections.emptyList();
1667                 mSourceStampVerificationStatus = sourceStampVerificationStatus;
1668             }
1669 
1670             /**
1671              * Returns the SourceStamp's signing certificate or {@code null} if not available. The
1672              * certificate is guaranteed to be available if no errors were encountered during
1673              * verification (see {@link #containsErrors()}.
1674              *
1675              * <p>This certificate contains the SourceStamp's public key.
1676              */
getCertificate()1677             public X509Certificate getCertificate() {
1678                 return mCertificates.isEmpty() ? null : mCertificates.get(0);
1679             }
1680 
1681             /**
1682              * Returns a list containing all of the certificates in the stamp certificate lineage.
1683              */
getCertificatesInLineage()1684             public List<X509Certificate> getCertificatesInLineage() {
1685                 return mCertificateLineage;
1686             }
1687 
containsErrors()1688             public boolean containsErrors() {
1689                 return !mErrors.isEmpty();
1690             }
1691 
getErrors()1692             public List<IssueWithParams> getErrors() {
1693                 return mErrors;
1694             }
1695 
getWarnings()1696             public List<IssueWithParams> getWarnings() {
1697                 return mWarnings;
1698             }
1699 
1700             /**
1701              * Returns the reason for any source stamp verification failures, or {@code
1702              * STAMP_VERIFIED} if the source stamp was successfully verified.
1703              */
getSourceStampVerificationStatus()1704             public SourceStampVerificationStatus getSourceStampVerificationStatus() {
1705                 return mSourceStampVerificationStatus;
1706             }
1707         }
1708     }
1709 
1710     /**
1711      * Error or warning encountered while verifying an APK's signatures.
1712      */
1713     public enum Issue {
1714 
1715         /**
1716          * APK is not JAR-signed.
1717          */
1718         JAR_SIG_NO_SIGNATURES("No JAR signatures"),
1719 
1720         /**
1721          * APK does not contain any entries covered by JAR signatures.
1722          */
1723         JAR_SIG_NO_SIGNED_ZIP_ENTRIES("No JAR entries covered by JAR signatures"),
1724 
1725         /**
1726          * APK contains multiple entries with the same name.
1727          *
1728          * <ul>
1729          * <li>Parameter 1: name ({@code String})</li>
1730          * </ul>
1731          */
1732         JAR_SIG_DUPLICATE_ZIP_ENTRY("Duplicate entry: %1$s"),
1733 
1734         /**
1735          * JAR manifest contains a section with a duplicate name.
1736          *
1737          * <ul>
1738          * <li>Parameter 1: section name ({@code String})</li>
1739          * </ul>
1740          */
1741         JAR_SIG_DUPLICATE_MANIFEST_SECTION("Duplicate section in META-INF/MANIFEST.MF: %1$s"),
1742 
1743         /**
1744          * JAR manifest contains a section without a name.
1745          *
1746          * <ul>
1747          * <li>Parameter 1: section index (1-based) ({@code Integer})</li>
1748          * </ul>
1749          */
1750         JAR_SIG_UNNNAMED_MANIFEST_SECTION(
1751                 "Malformed META-INF/MANIFEST.MF: invidual section #%1$d does not have a name"),
1752 
1753         /**
1754          * JAR signature file contains a section without a name.
1755          *
1756          * <ul>
1757          * <li>Parameter 1: signature file name ({@code String})</li>
1758          * <li>Parameter 2: section index (1-based) ({@code Integer})</li>
1759          * </ul>
1760          */
1761         JAR_SIG_UNNNAMED_SIG_FILE_SECTION(
1762                 "Malformed %1$s: invidual section #%2$d does not have a name"),
1763 
1764         /** APK is missing the JAR manifest entry (META-INF/MANIFEST.MF). */
1765         JAR_SIG_NO_MANIFEST("Missing META-INF/MANIFEST.MF"),
1766 
1767         /**
1768          * JAR manifest references an entry which is not there in the APK.
1769          *
1770          * <ul>
1771          * <li>Parameter 1: entry name ({@code String})</li>
1772          * </ul>
1773          */
1774         JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST(
1775                 "%1$s entry referenced by META-INF/MANIFEST.MF not found in the APK"),
1776 
1777         /**
1778          * JAR manifest does not list a digest for the specified entry.
1779          *
1780          * <ul>
1781          * <li>Parameter 1: entry name ({@code String})</li>
1782          * </ul>
1783          */
1784         JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST("No digest for %1$s in META-INF/MANIFEST.MF"),
1785 
1786         /**
1787          * JAR signature does not list a digest for the specified entry.
1788          *
1789          * <ul>
1790          * <li>Parameter 1: entry name ({@code String})</li>
1791          * <li>Parameter 2: signature file name ({@code String})</li>
1792          * </ul>
1793          */
1794         JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE("No digest for %1$s in %2$s"),
1795 
1796         /**
1797          * The specified JAR entry is not covered by JAR signature.
1798          *
1799          * <ul>
1800          * <li>Parameter 1: entry name ({@code String})</li>
1801          * </ul>
1802          */
1803         JAR_SIG_ZIP_ENTRY_NOT_SIGNED("%1$s entry not signed"),
1804 
1805         /**
1806          * JAR signature uses different set of signers to protect the two specified ZIP entries.
1807          *
1808          * <ul>
1809          * <li>Parameter 1: first entry name ({@code String})</li>
1810          * <li>Parameter 2: first entry signer names ({@code List<String>})</li>
1811          * <li>Parameter 3: second entry name ({@code String})</li>
1812          * <li>Parameter 4: second entry signer names ({@code List<String>})</li>
1813          * </ul>
1814          */
1815         JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH(
1816                 "Entries %1$s and %3$s are signed with different sets of signers"
1817                         + " : <%2$s> vs <%4$s>"),
1818 
1819         /**
1820          * Digest of the specified ZIP entry's data does not match the digest expected by the JAR
1821          * signature.
1822          *
1823          * <ul>
1824          * <li>Parameter 1: entry name ({@code String})</li>
1825          * <li>Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})</li>
1826          * <li>Parameter 3: name of the entry in which the expected digest is specified
1827          *     ({@code String})</li>
1828          * <li>Parameter 4: base64-encoded actual digest ({@code String})</li>
1829          * <li>Parameter 5: base64-encoded expected digest ({@code String})</li>
1830          * </ul>
1831          */
1832         JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY(
1833                 "%2$s digest of %1$s does not match the digest specified in %3$s"
1834                         + ". Expected: <%5$s>, actual: <%4$s>"),
1835 
1836         /**
1837          * Digest of the JAR manifest main section did not verify.
1838          *
1839          * <ul>
1840          * <li>Parameter 1: digest algorithm (e.g., SHA-256) ({@code String})</li>
1841          * <li>Parameter 2: name of the entry in which the expected digest is specified
1842          *     ({@code String})</li>
1843          * <li>Parameter 3: base64-encoded actual digest ({@code String})</li>
1844          * <li>Parameter 4: base64-encoded expected digest ({@code String})</li>
1845          * </ul>
1846          */
1847         JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY(
1848                 "%1$s digest of META-INF/MANIFEST.MF main section does not match the digest"
1849                         + " specified in %2$s. Expected: <%4$s>, actual: <%3$s>"),
1850 
1851         /**
1852          * Digest of the specified JAR manifest section does not match the digest expected by the
1853          * JAR signature.
1854          *
1855          * <ul>
1856          * <li>Parameter 1: section name ({@code String})</li>
1857          * <li>Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})</li>
1858          * <li>Parameter 3: name of the signature file in which the expected digest is specified
1859          *     ({@code String})</li>
1860          * <li>Parameter 4: base64-encoded actual digest ({@code String})</li>
1861          * <li>Parameter 5: base64-encoded expected digest ({@code String})</li>
1862          * </ul>
1863          */
1864         JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY(
1865                 "%2$s digest of META-INF/MANIFEST.MF section for %1$s does not match the digest"
1866                         + " specified in %3$s. Expected: <%5$s>, actual: <%4$s>"),
1867 
1868         /**
1869          * JAR signature file does not contain the whole-file digest of the JAR manifest file. The
1870          * digest speeds up verification of JAR signature.
1871          *
1872          * <ul>
1873          * <li>Parameter 1: name of the signature file ({@code String})</li>
1874          * </ul>
1875          */
1876         JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE(
1877                 "%1$s does not specify digest of META-INF/MANIFEST.MF"
1878                         + ". This slows down verification."),
1879 
1880         /**
1881          * APK is signed using APK Signature Scheme v2 or newer, but JAR signature file does not
1882          * contain protections against stripping of these newer scheme signatures.
1883          *
1884          * <ul>
1885          * <li>Parameter 1: name of the signature file ({@code String})</li>
1886          * </ul>
1887          */
1888         JAR_SIG_NO_APK_SIG_STRIP_PROTECTION(
1889                 "APK is signed using APK Signature Scheme v2 but these signatures may be stripped"
1890                         + " without being detected because %1$s does not contain anti-stripping"
1891                         + " protections."),
1892 
1893         /**
1894          * JAR signature of the signer is missing a file/entry.
1895          *
1896          * <ul>
1897          * <li>Parameter 1: name of the encountered file ({@code String})</li>
1898          * <li>Parameter 2: name of the missing file ({@code String})</li>
1899          * </ul>
1900          */
1901         JAR_SIG_MISSING_FILE("Partial JAR signature. Found: %1$s, missing: %2$s"),
1902 
1903         /**
1904          * An exception was encountered while verifying JAR signature contained in a signature block
1905          * against the signature file.
1906          *
1907          * <ul>
1908          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1909          * <li>Parameter 2: name of the signature file ({@code String})</li>
1910          * <li>Parameter 3: exception ({@code Throwable})</li>
1911          * </ul>
1912          */
1913         JAR_SIG_VERIFY_EXCEPTION("Failed to verify JAR signature %1$s against %2$s: %3$s"),
1914 
1915         /**
1916          * JAR signature contains unsupported digest algorithm.
1917          *
1918          * <ul>
1919          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1920          * <li>Parameter 2: digest algorithm OID ({@code String})</li>
1921          * <li>Parameter 3: signature algorithm OID ({@code String})</li>
1922          * <li>Parameter 4: API Levels on which this combination of algorithms is not supported
1923          *     ({@code String})</li>
1924          * <li>Parameter 5: user-friendly variant of digest algorithm ({@code String})</li>
1925          * <li>Parameter 6: user-friendly variant of signature algorithm ({@code String})</li>
1926          * </ul>
1927          */
1928         JAR_SIG_UNSUPPORTED_SIG_ALG(
1929                 "JAR signature %1$s uses digest algorithm %5$s and signature algorithm %6$s which"
1930                         + " is not supported on API Level(s) %4$s for which this APK is being"
1931                         + " verified"),
1932 
1933         /**
1934          * An exception was encountered while parsing JAR signature contained in a signature block.
1935          *
1936          * <ul>
1937          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1938          * <li>Parameter 2: exception ({@code Throwable})</li>
1939          * </ul>
1940          */
1941         JAR_SIG_PARSE_EXCEPTION("Failed to parse JAR signature %1$s: %2$s"),
1942 
1943         /**
1944          * An exception was encountered while parsing a certificate contained in the JAR signature
1945          * block.
1946          *
1947          * <ul>
1948          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1949          * <li>Parameter 2: exception ({@code Throwable})</li>
1950          * </ul>
1951          */
1952         JAR_SIG_MALFORMED_CERTIFICATE("Malformed certificate in JAR signature %1$s: %2$s"),
1953 
1954         /**
1955          * JAR signature contained in a signature block file did not verify against the signature
1956          * file.
1957          *
1958          * <ul>
1959          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1960          * <li>Parameter 2: name of the signature file ({@code String})</li>
1961          * </ul>
1962          */
1963         JAR_SIG_DID_NOT_VERIFY("JAR signature %1$s did not verify against %2$s"),
1964 
1965         /**
1966          * JAR signature contains no verified signers.
1967          *
1968          * <ul>
1969          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1970          * </ul>
1971          */
1972         JAR_SIG_NO_SIGNERS("JAR signature %1$s contains no signers"),
1973 
1974         /**
1975          * JAR signature file contains a section with a duplicate name.
1976          *
1977          * <ul>
1978          * <li>Parameter 1: signature file name ({@code String})</li>
1979          * <li>Parameter 1: section name ({@code String})</li>
1980          * </ul>
1981          */
1982         JAR_SIG_DUPLICATE_SIG_FILE_SECTION("Duplicate section in %1$s: %2$s"),
1983 
1984         /**
1985          * JAR signature file's main section doesn't contain the mandatory Signature-Version
1986          * attribute.
1987          *
1988          * <ul>
1989          * <li>Parameter 1: signature file name ({@code String})</li>
1990          * </ul>
1991          */
1992         JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE(
1993                 "Malformed %1$s: missing Signature-Version attribute"),
1994 
1995         /**
1996          * JAR signature file references an unknown APK signature scheme ID.
1997          *
1998          * <ul>
1999          * <li>Parameter 1: name of the signature file ({@code String})</li>
2000          * <li>Parameter 2: unknown APK signature scheme ID ({@code} Integer)</li>
2001          * </ul>
2002          */
2003         JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
2004                 "JAR signature %1$s references unknown APK signature scheme ID: %2$d"),
2005 
2006         /**
2007          * JAR signature file indicates that the APK is supposed to be signed with a supported APK
2008          * signature scheme (in addition to the JAR signature) but no such signature was found in
2009          * the APK.
2010          *
2011          * <ul>
2012          * <li>Parameter 1: name of the signature file ({@code String})</li>
2013          * <li>Parameter 2: APK signature scheme ID ({@code} Integer)</li>
2014          * <li>Parameter 3: APK signature scheme English name ({@code} String)</li>
2015          * </ul>
2016          */
2017         JAR_SIG_MISSING_APK_SIG_REFERENCED(
2018                 "JAR signature %1$s indicates the APK is signed using %3$s but no such signature"
2019                         + " was found. Signature stripped?"),
2020 
2021         /**
2022          * JAR entry is not covered by signature and thus unauthorized modifications to its contents
2023          * will not be detected.
2024          *
2025          * <ul>
2026          * <li>Parameter 1: entry name ({@code String})</li>
2027          * </ul>
2028          */
2029         JAR_SIG_UNPROTECTED_ZIP_ENTRY(
2030                 "%1$s not protected by signature. Unauthorized modifications to this JAR entry"
2031                         + " will not be detected. Delete or move the entry outside of META-INF/."),
2032 
2033         /**
2034          * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains an APK
2035          * Signature Scheme v2 signature from this signer, but does not contain a JAR signature
2036          * from this signer.
2037          */
2038         JAR_SIG_MISSING("No JAR signature from this signer"),
2039 
2040         /**
2041          * APK is targeting a sandbox version which requires APK Signature Scheme v2 signature but
2042          * no such signature was found.
2043          *
2044          * <ul>
2045          * <li>Parameter 1: target sandbox version ({@code Integer})</li>
2046          * </ul>
2047          */
2048         NO_SIG_FOR_TARGET_SANDBOX_VERSION(
2049                 "Missing APK Signature Scheme v2 signature required for target sandbox version"
2050                         + " %1$d"),
2051 
2052         /**
2053          * APK is targeting an SDK version that requires a minimum signature scheme version, but the
2054          * APK is not signed with that version or later.
2055          *
2056          * <ul>
2057          *     <li>Parameter 1: target SDK Version (@code Integer})</li>
2058          *     <li>Parameter 2: minimum signature scheme version ((@code Integer})</li>
2059          * </ul>
2060          */
2061         MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET(
2062                 "Target SDK version %1$d requires a minimum of signature scheme v%2$d; the APK is"
2063                         + " not signed with this or a later signature scheme"),
2064 
2065         /**
2066          * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains a JAR
2067          * signature from this signer, but does not contain an APK Signature Scheme v2 signature
2068          * from this signer.
2069          */
2070         V2_SIG_MISSING("No APK Signature Scheme v2 signature from this signer"),
2071 
2072         /**
2073          * Failed to parse the list of signers contained in the APK Signature Scheme v2 signature.
2074          */
2075         V2_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
2076 
2077         /**
2078          * Failed to parse this signer's signer block contained in the APK Signature Scheme v2
2079          * signature.
2080          */
2081         V2_SIG_MALFORMED_SIGNER("Malformed signer block"),
2082 
2083         /**
2084          * Public key embedded in the APK Signature Scheme v2 signature of this signer could not be
2085          * parsed.
2086          *
2087          * <ul>
2088          * <li>Parameter 1: error details ({@code Throwable})</li>
2089          * </ul>
2090          */
2091         V2_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
2092 
2093         /**
2094          * This APK Signature Scheme v2 signer's certificate could not be parsed.
2095          *
2096          * <ul>
2097          * <li>Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of
2098          *     certificates ({@code Integer})</li>
2099          * <li>Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's
2100          *     list of certificates ({@code Integer})</li>
2101          * <li>Parameter 3: error details ({@code Throwable})</li>
2102          * </ul>
2103          */
2104         V2_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
2105 
2106         /**
2107          * Failed to parse this signer's signature record contained in the APK Signature Scheme v2
2108          * signature.
2109          *
2110          * <ul>
2111          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
2112          * </ul>
2113          */
2114         V2_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v2 signature record #%1$d"),
2115 
2116         /**
2117          * Failed to parse this signer's digest record contained in the APK Signature Scheme v2
2118          * signature.
2119          *
2120          * <ul>
2121          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
2122          * </ul>
2123          */
2124         V2_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v2 digest record #%1$d"),
2125 
2126         /**
2127          * This APK Signature Scheme v2 signer contains a malformed additional attribute.
2128          *
2129          * <ul>
2130          * <li>Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})</li>
2131          * </ul>
2132          */
2133         V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
2134 
2135         /**
2136          * APK Signature Scheme v2 signature references an unknown APK signature scheme ID.
2137          *
2138          * <ul>
2139          * <li>Parameter 1: signer index ({@code Integer})</li>
2140          * <li>Parameter 2: unknown APK signature scheme ID ({@code} Integer)</li>
2141          * </ul>
2142          */
2143         V2_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
2144                 "APK Signature Scheme v2 signer: %1$s references unknown APK signature scheme ID: "
2145                         + "%2$d"),
2146 
2147         /**
2148          * APK Signature Scheme v2 signature indicates that the APK is supposed to be signed with a
2149          * supported APK signature scheme (in addition to the v2 signature) but no such signature
2150          * was found in the APK.
2151          *
2152          * <ul>
2153          * <li>Parameter 1: signer index ({@code Integer})</li>
2154          * <li>Parameter 2: APK signature scheme English name ({@code} String)</li>
2155          * </ul>
2156          */
2157         V2_SIG_MISSING_APK_SIG_REFERENCED(
2158                 "APK Signature Scheme v2 signature %1$s indicates the APK is signed using %2$s but "
2159                         + "no such signature was found. Signature stripped?"),
2160 
2161         /**
2162          * APK Signature Scheme v2 signature contains no signers.
2163          */
2164         V2_SIG_NO_SIGNERS("No signers in APK Signature Scheme v2 signature"),
2165 
2166         /**
2167          * This APK Signature Scheme v2 signer contains a signature produced using an unknown
2168          * algorithm.
2169          *
2170          * <ul>
2171          * <li>Parameter 1: algorithm ID ({@code Integer})</li>
2172          * </ul>
2173          */
2174         V2_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
2175 
2176         /**
2177          * This APK Signature Scheme v2 signer contains an unknown additional attribute.
2178          *
2179          * <ul>
2180          * <li>Parameter 1: attribute ID ({@code Integer})</li>
2181          * </ul>
2182          */
2183         V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
2184 
2185         /**
2186          * An exception was encountered while verifying APK Signature Scheme v2 signature of this
2187          * signer.
2188          *
2189          * <ul>
2190          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
2191          * <li>Parameter 2: exception ({@code Throwable})</li>
2192          * </ul>
2193          */
2194         V2_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
2195 
2196         /**
2197          * APK Signature Scheme v2 signature over this signer's signed-data block did not verify.
2198          *
2199          * <ul>
2200          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
2201          * </ul>
2202          */
2203         V2_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
2204 
2205         /**
2206          * This APK Signature Scheme v2 signer offers no signatures.
2207          */
2208         V2_SIG_NO_SIGNATURES("No signatures"),
2209 
2210         /**
2211          * This APK Signature Scheme v2 signer offers signatures but none of them are supported.
2212          */
2213         V2_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures: %1$s"),
2214 
2215         /**
2216          * This APK Signature Scheme v2 signer offers no certificates.
2217          */
2218         V2_SIG_NO_CERTIFICATES("No certificates"),
2219 
2220         /**
2221          * This APK Signature Scheme v2 signer's public key listed in the signer's certificate does
2222          * not match the public key listed in the signatures record.
2223          *
2224          * <ul>
2225          * <li>Parameter 1: hex-encoded public key from certificate ({@code String})</li>
2226          * <li>Parameter 2: hex-encoded public key from signatures record ({@code String})</li>
2227          * </ul>
2228          */
2229         V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
2230                 "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
2231 
2232         /**
2233          * This APK Signature Scheme v2 signer's signature algorithms listed in the signatures
2234          * record do not match the signature algorithms listed in the signatures record.
2235          *
2236          * <ul>
2237          * <li>Parameter 1: signature algorithms from signatures record ({@code List<Integer>})</li>
2238          * <li>Parameter 2: signature algorithms from digests record ({@code List<Integer>})</li>
2239          * </ul>
2240          */
2241         V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
2242                 "Signature algorithms mismatch between signatures and digests records"
2243                         + ": %1$s vs %2$s"),
2244 
2245         /**
2246          * The APK's digest does not match the digest contained in the APK Signature Scheme v2
2247          * signature.
2248          *
2249          * <ul>
2250          * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
2251          * <li>Parameter 2: hex-encoded expected digest of the APK ({@code String})</li>
2252          * <li>Parameter 3: hex-encoded actual digest of the APK ({@code String})</li>
2253          * </ul>
2254          */
2255         V2_SIG_APK_DIGEST_DID_NOT_VERIFY(
2256                 "APK integrity check failed. %1$s digest mismatch."
2257                         + " Expected: <%2$s>, actual: <%3$s>"),
2258 
2259         /**
2260          * Failed to parse the list of signers contained in the APK Signature Scheme v3 signature.
2261          */
2262         V3_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
2263 
2264         /**
2265          * Failed to parse this signer's signer block contained in the APK Signature Scheme v3
2266          * signature.
2267          */
2268         V3_SIG_MALFORMED_SIGNER("Malformed signer block"),
2269 
2270         /**
2271          * Public key embedded in the APK Signature Scheme v3 signature of this signer could not be
2272          * parsed.
2273          *
2274          * <ul>
2275          * <li>Parameter 1: error details ({@code Throwable})</li>
2276          * </ul>
2277          */
2278         V3_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
2279 
2280         /**
2281          * This APK Signature Scheme v3 signer's certificate could not be parsed.
2282          *
2283          * <ul>
2284          * <li>Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of
2285          *     certificates ({@code Integer})</li>
2286          * <li>Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's
2287          *     list of certificates ({@code Integer})</li>
2288          * <li>Parameter 3: error details ({@code Throwable})</li>
2289          * </ul>
2290          */
2291         V3_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
2292 
2293         /**
2294          * Failed to parse this signer's signature record contained in the APK Signature Scheme v3
2295          * signature.
2296          *
2297          * <ul>
2298          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
2299          * </ul>
2300          */
2301         V3_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v3 signature record #%1$d"),
2302 
2303         /**
2304          * Failed to parse this signer's digest record contained in the APK Signature Scheme v3
2305          * signature.
2306          *
2307          * <ul>
2308          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
2309          * </ul>
2310          */
2311         V3_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v3 digest record #%1$d"),
2312 
2313         /**
2314          * This APK Signature Scheme v3 signer contains a malformed additional attribute.
2315          *
2316          * <ul>
2317          * <li>Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})</li>
2318          * </ul>
2319          */
2320         V3_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
2321 
2322         /**
2323          * APK Signature Scheme v3 signature contains no signers.
2324          */
2325         V3_SIG_NO_SIGNERS("No signers in APK Signature Scheme v3 signature"),
2326 
2327         /**
2328          * APK Signature Scheme v3 signature contains multiple signers (only one allowed per
2329          * platform version).
2330          */
2331         V3_SIG_MULTIPLE_SIGNERS("Multiple APK Signature Scheme v3 signatures found for a single "
2332                 + " platform version."),
2333 
2334         /**
2335          * APK Signature Scheme v3 signature found, but multiple v1 and/or multiple v2 signers
2336          * found, where only one may be used with APK Signature Scheme v3
2337          */
2338         V3_SIG_MULTIPLE_PAST_SIGNERS("Multiple signatures found for pre-v3 signing with an APK "
2339                 + " Signature Scheme v3 signer.  Only one allowed."),
2340 
2341         /**
2342          * APK Signature Scheme v3 signature found, but its signer doesn't match the v1/v2 signers,
2343          * or have them as the root of its signing certificate history
2344          */
2345         V3_SIG_PAST_SIGNERS_MISMATCH(
2346                 "v3 signer differs from v1/v2 signer without proper signing certificate lineage."),
2347 
2348         /**
2349          * This APK Signature Scheme v3 signer contains a signature produced using an unknown
2350          * algorithm.
2351          *
2352          * <ul>
2353          * <li>Parameter 1: algorithm ID ({@code Integer})</li>
2354          * </ul>
2355          */
2356         V3_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
2357 
2358         /**
2359          * This APK Signature Scheme v3 signer contains an unknown additional attribute.
2360          *
2361          * <ul>
2362          * <li>Parameter 1: attribute ID ({@code Integer})</li>
2363          * </ul>
2364          */
2365         V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
2366 
2367         /**
2368          * An exception was encountered while verifying APK Signature Scheme v3 signature of this
2369          * signer.
2370          *
2371          * <ul>
2372          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
2373          * <li>Parameter 2: exception ({@code Throwable})</li>
2374          * </ul>
2375          */
2376         V3_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
2377 
2378         /**
2379          * The APK Signature Scheme v3 signer contained an invalid value for either min or max SDK
2380          * versions.
2381          *
2382          * <ul>
2383          * <li>Parameter 1: minSdkVersion ({@code Integer})
2384          * <li>Parameter 2: maxSdkVersion ({@code Integer})
2385          * </ul>
2386          */
2387         V3_SIG_INVALID_SDK_VERSIONS("Invalid SDK Version parameter(s) encountered in APK Signature "
2388                 + "scheme v3 signature: minSdkVersion %1$s maxSdkVersion: %2$s"),
2389 
2390         /**
2391          * APK Signature Scheme v3 signature over this signer's signed-data block did not verify.
2392          *
2393          * <ul>
2394          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
2395          * </ul>
2396          */
2397         V3_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
2398 
2399         /**
2400          * This APK Signature Scheme v3 signer offers no signatures.
2401          */
2402         V3_SIG_NO_SIGNATURES("No signatures"),
2403 
2404         /**
2405          * This APK Signature Scheme v3 signer offers signatures but none of them are supported.
2406          */
2407         V3_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures"),
2408 
2409         /**
2410          * This APK Signature Scheme v3 signer offers no certificates.
2411          */
2412         V3_SIG_NO_CERTIFICATES("No certificates"),
2413 
2414         /**
2415          * This APK Signature Scheme v3 signer's minSdkVersion listed in the signer's signed data
2416          * does not match the minSdkVersion listed in the signatures record.
2417          *
2418          * <ul>
2419          * <li>Parameter 1: minSdkVersion in signature record ({@code Integer}) </li>
2420          * <li>Parameter 2: minSdkVersion in signed data ({@code Integer}) </li>
2421          * </ul>
2422          */
2423         V3_MIN_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD(
2424                 "minSdkVersion mismatch between signed data and signature record:"
2425                         + " <%1$s> vs <%2$s>"),
2426 
2427         /**
2428          * This APK Signature Scheme v3 signer's maxSdkVersion listed in the signer's signed data
2429          * does not match the maxSdkVersion listed in the signatures record.
2430          *
2431          * <ul>
2432          * <li>Parameter 1: maxSdkVersion in signature record ({@code Integer}) </li>
2433          * <li>Parameter 2: maxSdkVersion in signed data ({@code Integer}) </li>
2434          * </ul>
2435          */
2436         V3_MAX_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD(
2437                 "maxSdkVersion mismatch between signed data and signature record:"
2438                         + " <%1$s> vs <%2$s>"),
2439 
2440         /**
2441          * This APK Signature Scheme v3 signer's public key listed in the signer's certificate does
2442          * not match the public key listed in the signatures record.
2443          *
2444          * <ul>
2445          * <li>Parameter 1: hex-encoded public key from certificate ({@code String})</li>
2446          * <li>Parameter 2: hex-encoded public key from signatures record ({@code String})</li>
2447          * </ul>
2448          */
2449         V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
2450                 "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
2451 
2452         /**
2453          * This APK Signature Scheme v3 signer's signature algorithms listed in the signatures
2454          * record do not match the signature algorithms listed in the signatures record.
2455          *
2456          * <ul>
2457          * <li>Parameter 1: signature algorithms from signatures record ({@code List<Integer>})</li>
2458          * <li>Parameter 2: signature algorithms from digests record ({@code List<Integer>})</li>
2459          * </ul>
2460          */
2461         V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
2462                 "Signature algorithms mismatch between signatures and digests records"
2463                         + ": %1$s vs %2$s"),
2464 
2465         /**
2466          * The APK's digest does not match the digest contained in the APK Signature Scheme v3
2467          * signature.
2468          *
2469          * <ul>
2470          * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
2471          * <li>Parameter 2: hex-encoded expected digest of the APK ({@code String})</li>
2472          * <li>Parameter 3: hex-encoded actual digest of the APK ({@code String})</li>
2473          * </ul>
2474          */
2475         V3_SIG_APK_DIGEST_DID_NOT_VERIFY(
2476                 "APK integrity check failed. %1$s digest mismatch."
2477                         + " Expected: <%2$s>, actual: <%3$s>"),
2478 
2479         /**
2480          * The signer's SigningCertificateLineage attribute containd a proof-of-rotation record with
2481          * signature(s) that did not verify.
2482          */
2483         V3_SIG_POR_DID_NOT_VERIFY("SigningCertificateLineage attribute containd a proof-of-rotation"
2484                 + " record with signature(s) that did not verify."),
2485 
2486         /**
2487          * Failed to parse the SigningCertificateLineage structure in the APK Signature Scheme v3
2488          * signature's additional attributes section.
2489          */
2490         V3_SIG_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage structure in the "
2491                 + "APK Signature Scheme v3 signature's additional attributes section."),
2492 
2493         /**
2494          * The APK's signing certificate does not match the terminal node in the provided
2495          * proof-of-rotation structure describing the signing certificate history
2496          */
2497         V3_SIG_POR_CERT_MISMATCH(
2498                 "APK signing certificate differs from the associated certificate found in the "
2499                         + "signer's SigningCertificateLineage."),
2500 
2501         /**
2502          * The APK Signature Scheme v3 signers encountered do not offer a continuous set of
2503          * supported platform versions.  Either they overlap, resulting in potentially two
2504          * acceptable signers for a platform version, or there are holes which would create problems
2505          * in the event of platform version upgrades.
2506          */
2507         V3_INCONSISTENT_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK "
2508                 + "versions are not continuous."),
2509 
2510         /**
2511          * The APK Signature Scheme v3 signers don't cover all requested SDK versions.
2512          *
2513          *  <ul>
2514          * <li>Parameter 1: minSdkVersion ({@code Integer})
2515          * <li>Parameter 2: maxSdkVersion ({@code Integer})
2516          * </ul>
2517          */
2518         V3_MISSING_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK "
2519                 + "versions do not cover the entire desired range.  Found min:  %1$s max %2$s"),
2520 
2521         /**
2522          * The SigningCertificateLineages for different platform versions using APK Signature Scheme
2523          * v3 do not go together.  Specifically, each should be a subset of another, with the size
2524          * of each increasing as the platform level increases.
2525          */
2526         V3_INCONSISTENT_LINEAGES("SigningCertificateLineages targeting different platform versions"
2527                 + " using APK Signature Scheme v3 are not all a part of the same overall lineage."),
2528 
2529         /**
2530          * APK Signing Block contains an unknown entry.
2531          *
2532          * <ul>
2533          * <li>Parameter 1: entry ID ({@code Integer})</li>
2534          * </ul>
2535          */
2536         APK_SIG_BLOCK_UNKNOWN_ENTRY_ID("APK Signing Block contains unknown entry: ID %1$#x"),
2537 
2538         /**
2539          * Failed to parse this signer's signature record contained in the APK Signature Scheme
2540          * V4 signature.
2541          *
2542          * <ul>
2543          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
2544          * </ul>
2545          */
2546         V4_SIG_MALFORMED_SIGNERS(
2547                 "V4 signature has malformed signer block"),
2548 
2549         /**
2550          * This APK Signature Scheme V4 signer contains a signature produced using an
2551          * unknown algorithm.
2552          *
2553          * <ul>
2554          * <li>Parameter 1: algorithm ID ({@code Integer})</li>
2555          * </ul>
2556          */
2557         V4_SIG_UNKNOWN_SIG_ALGORITHM(
2558                 "V4 signature has unknown signing algorithm: %1$#x"),
2559 
2560         /**
2561          * This APK Signature Scheme V4 signer offers no signatures.
2562          */
2563         V4_SIG_NO_SIGNATURES(
2564                 "V4 signature has no signature found"),
2565 
2566         /**
2567          * This APK Signature Scheme V4 signer offers signatures but none of them are
2568          * supported.
2569          */
2570         V4_SIG_NO_SUPPORTED_SIGNATURES(
2571                 "V4 signature has no supported signature"),
2572 
2573         /**
2574          * APK Signature Scheme v3 signature over this signer's signed-data block did not verify.
2575          *
2576          * <ul>
2577          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
2578          * </ul>
2579          */
2580         V4_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
2581 
2582         /**
2583          * An exception was encountered while verifying APK Signature Scheme v3 signature of this
2584          * signer.
2585          *
2586          * <ul>
2587          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
2588          * <li>Parameter 2: exception ({@code Throwable})</li>
2589          * </ul>
2590          */
2591         V4_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
2592 
2593         /**
2594          * Public key embedded in the APK Signature Scheme v4 signature of this signer could not be
2595          * parsed.
2596          *
2597          * <ul>
2598          * <li>Parameter 1: error details ({@code Throwable})</li>
2599          * </ul>
2600          */
2601         V4_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
2602 
2603         /**
2604          * This APK Signature Scheme V4 signer's certificate could not be parsed.
2605          *
2606          * <ul>
2607          * <li>Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of
2608          *     certificates ({@code Integer})</li>
2609          * <li>Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's
2610          *     list of certificates ({@code Integer})</li>
2611          * <li>Parameter 3: error details ({@code Throwable})</li>
2612          * </ul>
2613          */
2614         V4_SIG_MALFORMED_CERTIFICATE(
2615                 "V4 signature has malformed certificate"),
2616 
2617         /**
2618          * This APK Signature Scheme V4 signer offers no certificate.
2619          */
2620         V4_SIG_NO_CERTIFICATE("V4 signature has no certificate"),
2621 
2622         /**
2623          * This APK Signature Scheme V4 signer's public key listed in the signer's
2624          * certificate does not match the public key listed in the signature proto.
2625          *
2626          * <ul>
2627          * <li>Parameter 1: hex-encoded public key from certificate ({@code String})</li>
2628          * <li>Parameter 2: hex-encoded public key from signature proto ({@code String})</li>
2629          * </ul>
2630          */
2631         V4_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
2632                 "V4 signature has mismatched certificate and signature: <%1$s> vs <%2$s>"),
2633 
2634         /**
2635          * The APK's hash root (aka digest) does not match the hash root contained in the Signature
2636          * Scheme V4 signature.
2637          *
2638          * <ul>
2639          * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
2640          * <li>Parameter 2: hex-encoded expected digest of the APK ({@code String})</li>
2641          * <li>Parameter 3: hex-encoded actual digest of the APK ({@code String})</li>
2642          * </ul>
2643          */
2644         V4_SIG_APK_ROOT_DID_NOT_VERIFY(
2645                 "V4 signature's hash tree root (content digest) did not verity"),
2646 
2647         /**
2648          * The APK's hash tree does not match the hash tree contained in the Signature
2649          * Scheme V4 signature.
2650          *
2651          * <ul>
2652          * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
2653          * <li>Parameter 2: hex-encoded expected hash tree of the APK ({@code String})</li>
2654          * <li>Parameter 3: hex-encoded actual hash tree of the APK ({@code String})</li>
2655          * </ul>
2656          */
2657         V4_SIG_APK_TREE_DID_NOT_VERIFY(
2658                 "V4 signature's hash tree did not verity"),
2659 
2660         /**
2661          * Using more than one Signer to sign APK Signature Scheme V4 signature.
2662          */
2663         V4_SIG_MULTIPLE_SIGNERS(
2664                 "V4 signature only supports one signer"),
2665 
2666         /**
2667          * The signer used to sign APK Signature Scheme V2/V3 signature does not match the signer
2668          * used to sign APK Signature Scheme V4 signature.
2669          */
2670         V4_SIG_V2_V3_SIGNERS_MISMATCH(
2671                 "V4 signature and V2/V3 signature have mismatched certificates"),
2672 
2673         V4_SIG_V2_V3_DIGESTS_MISMATCH(
2674                 "V4 signature and V2/V3 signature have mismatched digests"),
2675 
2676         /**
2677          * The v4 signature format version isn't the same as the tool's current version, something
2678          * may go wrong.
2679          */
2680         V4_SIG_VERSION_NOT_CURRENT(
2681                 "V4 signature format version %1$d is different from the tool's current "
2682                         + "version %2$d"),
2683 
2684         /**
2685          * The APK does not contain the source stamp certificate digest file nor the signature block
2686          * when verification expected a source stamp to be present.
2687          */
2688         SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING(
2689                 "Neither the source stamp certificate digest file nor the signature block are "
2690                         + "present in the APK"),
2691 
2692         /** APK contains SourceStamp file, but does not contain a SourceStamp signature. */
2693         SOURCE_STAMP_SIG_MISSING("No SourceStamp signature"),
2694 
2695         /**
2696          * SourceStamp's certificate could not be parsed.
2697          *
2698          * <ul>
2699          *   <li>Parameter 1: error details ({@code Throwable})
2700          * </ul>
2701          */
2702         SOURCE_STAMP_MALFORMED_CERTIFICATE("Malformed certificate: %1$s"),
2703 
2704         /** Failed to parse SourceStamp's signature. */
2705         SOURCE_STAMP_MALFORMED_SIGNATURE("Malformed SourceStamp signature"),
2706 
2707         /**
2708          * SourceStamp contains a signature produced using an unknown algorithm.
2709          *
2710          * <ul>
2711          *   <li>Parameter 1: algorithm ID ({@code Integer})
2712          * </ul>
2713          */
2714         SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
2715 
2716         /**
2717          * An exception was encountered while verifying SourceStamp signature.
2718          *
2719          * <ul>
2720          *   <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})
2721          *   <li>Parameter 2: exception ({@code Throwable})
2722          * </ul>
2723          */
2724         SOURCE_STAMP_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
2725 
2726         /**
2727          * SourceStamp signature block did not verify.
2728          *
2729          * <ul>
2730          *   <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})
2731          * </ul>
2732          */
2733         SOURCE_STAMP_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
2734 
2735         /** SourceStamp offers no signatures. */
2736         SOURCE_STAMP_NO_SIGNATURE("No signature"),
2737 
2738         /**
2739          * SourceStamp offers an unsupported signature.
2740          * <ul>
2741          *     <li>Parameter 1: list of {@link SignatureAlgorithm}s  in the source stamp
2742          *     signing block.
2743          *     <li>Parameter 2: {@code Exception} caught when attempting to obtain the list of
2744          *     supported signatures.
2745          * </ul>
2746          */
2747         SOURCE_STAMP_NO_SUPPORTED_SIGNATURE("Signature(s) {%1$s} not supported: %2$s"),
2748 
2749         /**
2750          * SourceStamp's certificate listed in the APK signing block does not match the certificate
2751          * listed in the SourceStamp file in the APK.
2752          *
2753          * <ul>
2754          *   <li>Parameter 1: SHA-256 hash of certificate from SourceStamp block in APK signing
2755          *       block ({@code String})
2756          *   <li>Parameter 2: SHA-256 hash of certificate from SourceStamp file in APK ({@code
2757          *       String})
2758          * </ul>
2759          */
2760         SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK(
2761                 "Certificate mismatch between SourceStamp block in APK signing block and"
2762                         + " SourceStamp file in APK: <%1$s> vs <%2$s>"),
2763 
2764         /**
2765          * The APK contains a source stamp signature block without the expected certificate digest
2766          * in the APK contents.
2767          */
2768         SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST(
2769                 "A source stamp signature block was found without a corresponding certificate "
2770                         + "digest in the APK"),
2771 
2772         /**
2773          * When verifying just the source stamp, the certificate digest in the APK does not match
2774          * the expected digest.
2775          * <ul>
2776          *     <li>Parameter 1: SHA-256 digest of the source stamp certificate in the APK.
2777          *     <li>Parameter 2: SHA-256 digest of the expected source stamp certificate.
2778          * </ul>
2779          */
2780         SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH(
2781                 "The source stamp certificate digest in the APK, %1$s, does not match the "
2782                         + "expected digest, %2$s"),
2783 
2784         /**
2785          * Source stamp block contains a malformed attribute.
2786          *
2787          * <ul>
2788          * <li>Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})</li>
2789          * </ul>
2790          */
2791         SOURCE_STAMP_MALFORMED_ATTRIBUTE("Malformed stamp attribute #%1$d"),
2792 
2793         /**
2794          * Source stamp block contains an unknown attribute.
2795          *
2796          * <ul>
2797          * <li>Parameter 1: attribute ID ({@code Integer})</li>
2798          * </ul>
2799          */
2800         SOURCE_STAMP_UNKNOWN_ATTRIBUTE("Unknown stamp attribute: ID %1$#x"),
2801 
2802         /**
2803          * Failed to parse the SigningCertificateLineage structure in the source stamp
2804          * attributes section.
2805          */
2806         SOURCE_STAMP_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage "
2807                 + "structure in the source stamp attributes section."),
2808 
2809         /**
2810          * The source stamp certificate does not match the terminal node in the provided
2811          * proof-of-rotation structure describing the stamp certificate history.
2812          */
2813         SOURCE_STAMP_POR_CERT_MISMATCH(
2814                 "APK signing certificate differs from the associated certificate found in the "
2815                         + "signer's SigningCertificateLineage."),
2816 
2817         /**
2818          * The source stamp SigningCertificateLineage attribute contains a proof-of-rotation record
2819          * with signature(s) that did not verify.
2820          */
2821         SOURCE_STAMP_POR_DID_NOT_VERIFY("Source stamp SigningCertificateLineage attribute "
2822                 + "contains a proof-of-rotation record with signature(s) that did not verify."),
2823 
2824         /**
2825          * The APK could not be properly parsed due to a ZIP or APK format exception.
2826          * <ul>
2827          *     <li>Parameter 1: The {@code Exception} caught when attempting to parse the APK.
2828          * </ul>
2829          */
2830         MALFORMED_APK(
2831                 "Malformed APK; the following exception was caught when attempting to parse the "
2832                         + "APK: %1$s"),
2833 
2834         /**
2835          * An unexpected exception was caught when attempting to verify the signature(s) within the
2836          * APK.
2837          * <ul>
2838          *     <li>Parameter 1: The {@code Exception} caught during verification.
2839          * </ul>
2840          */
2841         UNEXPECTED_EXCEPTION(
2842                 "An unexpected exception was caught when verifying the signature: %1$s");
2843 
2844         private final String mFormat;
2845 
Issue(String format)2846         Issue(String format) {
2847             mFormat = format;
2848         }
2849 
2850         /**
2851          * Returns the format string suitable for combining the parameters of this issue into a
2852          * readable string. See {@link java.util.Formatter} for format.
2853          */
getFormat()2854         private String getFormat() {
2855             return mFormat;
2856         }
2857     }
2858 
2859     /**
2860      * {@link Issue} with associated parameters. {@link #toString()} produces a readable formatted
2861      * form.
2862      */
2863     public static class IssueWithParams extends ApkVerificationIssue {
2864         private final Issue mIssue;
2865         private final Object[] mParams;
2866 
2867         /**
2868          * Constructs a new {@code IssueWithParams} of the specified type and with provided
2869          * parameters.
2870          */
IssueWithParams(Issue issue, Object[] params)2871         public IssueWithParams(Issue issue, Object[] params) {
2872             super(issue.mFormat, params);
2873             mIssue = issue;
2874             mParams = params;
2875         }
2876 
2877         /**
2878          * Returns the type of this issue.
2879          */
getIssue()2880         public Issue getIssue() {
2881             return mIssue;
2882         }
2883 
2884         /**
2885          * Returns the parameters of this issue.
2886          */
getParams()2887         public Object[] getParams() {
2888             return mParams.clone();
2889         }
2890 
2891         /**
2892          * Returns a readable form of this issue.
2893          */
2894         @Override
toString()2895         public String toString() {
2896             return String.format(mIssue.getFormat(), mParams);
2897         }
2898     }
2899 
2900     /**
2901      * Wrapped around {@code byte[]} which ensures that {@code equals} and {@code hashCode} operate
2902      * on the contents of the arrays rather than on references.
2903      */
2904     private static class ByteArray {
2905         private final byte[] mArray;
2906         private final int mHashCode;
2907 
ByteArray(byte[] arr)2908         private ByteArray(byte[] arr) {
2909             mArray = arr;
2910             mHashCode = Arrays.hashCode(mArray);
2911         }
2912 
2913         @Override
hashCode()2914         public int hashCode() {
2915             return mHashCode;
2916         }
2917 
2918         @Override
equals(Object obj)2919         public boolean equals(Object obj) {
2920             if (this == obj) {
2921                 return true;
2922             }
2923             if (!(obj instanceof ByteArray)) {
2924                 return false;
2925             }
2926             ByteArray other = (ByteArray) obj;
2927             if (hashCode() != other.hashCode()) {
2928                 return false;
2929             }
2930             if (!Arrays.equals(mArray, other.mArray)) {
2931                 return false;
2932             }
2933             return true;
2934         }
2935     }
2936 
2937     /**
2938      * Builder of {@link ApkVerifier} instances.
2939      *
2940      * <p>The resulting verifier by default checks whether the APK will verify on all platform
2941      * versions supported by the APK, as specified by {@code android:minSdkVersion} attributes in
2942      * the APK's {@code AndroidManifest.xml}. The range of platform versions can be customized using
2943      * {@link #setMinCheckedPlatformVersion(int)} and {@link #setMaxCheckedPlatformVersion(int)}.
2944      */
2945     public static class Builder {
2946         private final File mApkFile;
2947         private final DataSource mApkDataSource;
2948         private File mV4SignatureFile;
2949 
2950         private Integer mMinSdkVersion;
2951         private int mMaxSdkVersion = Integer.MAX_VALUE;
2952 
2953         /**
2954          * Constructs a new {@code Builder} for verifying the provided APK file.
2955          */
Builder(File apk)2956         public Builder(File apk) {
2957             if (apk == null) {
2958                 throw new NullPointerException("apk == null");
2959             }
2960             mApkFile = apk;
2961             mApkDataSource = null;
2962         }
2963 
2964         /**
2965          * Constructs a new {@code Builder} for verifying the provided APK.
2966          */
Builder(DataSource apk)2967         public Builder(DataSource apk) {
2968             if (apk == null) {
2969                 throw new NullPointerException("apk == null");
2970             }
2971             mApkDataSource = apk;
2972             mApkFile = null;
2973         }
2974 
2975         /**
2976          * Sets the oldest Android platform version for which the APK is verified. APK verification
2977          * will confirm that the APK is expected to install successfully on all known Android
2978          * platforms starting from the platform version with the provided API Level. The upper end
2979          * of the platform versions range can be modified via
2980          * {@link #setMaxCheckedPlatformVersion(int)}.
2981          *
2982          * <p>This method is useful for overriding the default behavior which checks that the APK
2983          * will verify on all platform versions supported by the APK, as specified by
2984          * {@code android:minSdkVersion} attributes in the APK's {@code AndroidManifest.xml}.
2985          *
2986          * @param minSdkVersion API Level of the oldest platform for which to verify the APK
2987          * @see #setMinCheckedPlatformVersion(int)
2988          */
setMinCheckedPlatformVersion(int minSdkVersion)2989         public Builder setMinCheckedPlatformVersion(int minSdkVersion) {
2990             mMinSdkVersion = minSdkVersion;
2991             return this;
2992         }
2993 
2994         /**
2995          * Sets the newest Android platform version for which the APK is verified. APK verification
2996          * will confirm that the APK is expected to install successfully on all platform versions
2997          * supported by the APK up until and including the provided version. The lower end
2998          * of the platform versions range can be modified via
2999          * {@link #setMinCheckedPlatformVersion(int)}.
3000          *
3001          * @param maxSdkVersion API Level of the newest platform for which to verify the APK
3002          * @see #setMinCheckedPlatformVersion(int)
3003          */
setMaxCheckedPlatformVersion(int maxSdkVersion)3004         public Builder setMaxCheckedPlatformVersion(int maxSdkVersion) {
3005             mMaxSdkVersion = maxSdkVersion;
3006             return this;
3007         }
3008 
setV4SignatureFile(File v4SignatureFile)3009         public Builder setV4SignatureFile(File v4SignatureFile) {
3010             mV4SignatureFile = v4SignatureFile;
3011             return this;
3012         }
3013 
3014         /**
3015          * Returns an {@link ApkVerifier} initialized according to the configuration of this
3016          * builder.
3017          */
build()3018         public ApkVerifier build() {
3019             return new ApkVerifier(
3020                     mApkFile,
3021                     mApkDataSource,
3022                     mV4SignatureFile,
3023                     mMinSdkVersion,
3024                     mMaxSdkVersion);
3025         }
3026     }
3027 
3028     /**
3029      * Adapter for converting base {@link ApkVerificationIssue} instances to their {@link
3030      * IssueWithParams} equivalent.
3031      */
3032     public static class ApkVerificationIssueAdapter {
ApkVerificationIssueAdapter()3033         private ApkVerificationIssueAdapter() {
3034         }
3035 
3036         // This field is visible for testing
3037         static final Map<Integer, Issue> sVerificationIssueIdToIssue = new HashMap<>();
3038 
3039         static {
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNERS, Issue.V2_SIG_MALFORMED_SIGNERS)3040             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNERS,
3041                     Issue.V2_SIG_MALFORMED_SIGNERS);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_SIGNERS, Issue.V2_SIG_NO_SIGNERS)3042             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_SIGNERS,
3043                     Issue.V2_SIG_NO_SIGNERS);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNER, Issue.V2_SIG_MALFORMED_SIGNER)3044             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNER,
3045                     Issue.V2_SIG_MALFORMED_SIGNER);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNATURE, Issue.V2_SIG_MALFORMED_SIGNATURE)3046             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNATURE,
3047                     Issue.V2_SIG_MALFORMED_SIGNATURE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_SIGNATURES, Issue.V2_SIG_NO_SIGNATURES)3048             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_SIGNATURES,
3049                     Issue.V2_SIG_NO_SIGNATURES);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_CERTIFICATE, Issue.V2_SIG_MALFORMED_CERTIFICATE)3050             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_CERTIFICATE,
3051                     Issue.V2_SIG_MALFORMED_CERTIFICATE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_CERTIFICATES, Issue.V2_SIG_NO_CERTIFICATES)3052             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_CERTIFICATES,
3053                     Issue.V2_SIG_NO_CERTIFICATES);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_DIGEST, Issue.V2_SIG_MALFORMED_DIGEST)3054             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_DIGEST,
3055                     Issue.V2_SIG_MALFORMED_DIGEST);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNERS, Issue.V3_SIG_MALFORMED_SIGNERS)3056             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNERS,
3057                     Issue.V3_SIG_MALFORMED_SIGNERS);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_SIGNERS, Issue.V3_SIG_NO_SIGNERS)3058             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_SIGNERS,
3059                     Issue.V3_SIG_NO_SIGNERS);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNER, Issue.V3_SIG_MALFORMED_SIGNER)3060             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNER,
3061                     Issue.V3_SIG_MALFORMED_SIGNER);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNATURE, Issue.V3_SIG_MALFORMED_SIGNATURE)3062             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNATURE,
3063                     Issue.V3_SIG_MALFORMED_SIGNATURE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_SIGNATURES, Issue.V3_SIG_NO_SIGNATURES)3064             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_SIGNATURES,
3065                     Issue.V3_SIG_NO_SIGNATURES);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_CERTIFICATE, Issue.V3_SIG_MALFORMED_CERTIFICATE)3066             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_CERTIFICATE,
3067                     Issue.V3_SIG_MALFORMED_CERTIFICATE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_CERTIFICATES, Issue.V3_SIG_NO_CERTIFICATES)3068             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_CERTIFICATES,
3069                     Issue.V3_SIG_NO_CERTIFICATES);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_DIGEST, Issue.V3_SIG_MALFORMED_DIGEST)3070             sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_DIGEST,
3071                     Issue.V3_SIG_MALFORMED_DIGEST);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE, Issue.SOURCE_STAMP_NO_SIGNATURE)3072             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE,
3073                     Issue.SOURCE_STAMP_NO_SIGNATURE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_CERTIFICATE, Issue.SOURCE_STAMP_MALFORMED_CERTIFICATE)3074             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_CERTIFICATE,
3075                     Issue.SOURCE_STAMP_MALFORMED_CERTIFICATE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM, Issue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM)3076             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM,
3077                     Issue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE)3078             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE,
3079                     Issue.SOURCE_STAMP_MALFORMED_SIGNATURE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY, Issue.SOURCE_STAMP_DID_NOT_VERIFY)3080             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY,
3081                     Issue.SOURCE_STAMP_DID_NOT_VERIFY);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_VERIFY_EXCEPTION, Issue.SOURCE_STAMP_VERIFY_EXCEPTION)3082             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_VERIFY_EXCEPTION,
3083                     Issue.SOURCE_STAMP_VERIFY_EXCEPTION);
sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH)3084             sVerificationIssueIdToIssue.put(
3085                     ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH,
3086                     Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH);
sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST, Issue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST)3087             sVerificationIssueIdToIssue.put(
3088                     ApkVerificationIssue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST,
3089                     Issue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST);
sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING, Issue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING)3090             sVerificationIssueIdToIssue.put(
3091                     ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING,
3092                     Issue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING);
sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE, Issue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE)3093             sVerificationIssueIdToIssue.put(
3094                     ApkVerificationIssue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE,
3095                     Issue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE);
sVerificationIssueIdToIssue.put( ApkVerificationIssue .SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK, Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK)3096             sVerificationIssueIdToIssue.put(
3097                     ApkVerificationIssue
3098                             .SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK,
3099                     Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.MALFORMED_APK, Issue.MALFORMED_APK)3100             sVerificationIssueIdToIssue.put(ApkVerificationIssue.MALFORMED_APK,
3101                     Issue.MALFORMED_APK);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.UNEXPECTED_EXCEPTION, Issue.UNEXPECTED_EXCEPTION)3102             sVerificationIssueIdToIssue.put(ApkVerificationIssue.UNEXPECTED_EXCEPTION,
3103                     Issue.UNEXPECTED_EXCEPTION);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING, Issue.SOURCE_STAMP_SIG_MISSING)3104             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING,
3105                     Issue.SOURCE_STAMP_SIG_MISSING);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE, Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE)3106             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE,
3107                     Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE, Issue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE)3108             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE,
3109                     Issue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_LINEAGE, Issue.SOURCE_STAMP_MALFORMED_LINEAGE)3110             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_LINEAGE,
3111                     Issue.SOURCE_STAMP_MALFORMED_LINEAGE);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_POR_CERT_MISMATCH, Issue.SOURCE_STAMP_POR_CERT_MISMATCH)3112             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_POR_CERT_MISMATCH,
3113                     Issue.SOURCE_STAMP_POR_CERT_MISMATCH);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_POR_DID_NOT_VERIFY, Issue.SOURCE_STAMP_POR_DID_NOT_VERIFY)3114             sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_POR_DID_NOT_VERIFY,
3115                     Issue.SOURCE_STAMP_POR_DID_NOT_VERIFY);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_NO_SIGNATURES, Issue.JAR_SIG_NO_SIGNATURES)3116             sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_NO_SIGNATURES,
3117                     Issue.JAR_SIG_NO_SIGNATURES);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION, Issue.JAR_SIG_PARSE_EXCEPTION)3118             sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION,
3119                     Issue.JAR_SIG_PARSE_EXCEPTION);
3120         }
3121 
3122         /**
3123          * Converts the provided {@code verificationIssues} to a {@code List} of corresponding
3124          * {@link IssueWithParams} instances.
3125          */
getIssuesFromVerificationIssues( List<? extends ApkVerificationIssue> verificationIssues)3126         public static List<IssueWithParams> getIssuesFromVerificationIssues(
3127                 List<? extends ApkVerificationIssue> verificationIssues) {
3128             List<IssueWithParams> result = new ArrayList<>(verificationIssues.size());
3129             for (ApkVerificationIssue issue : verificationIssues) {
3130                 if (issue instanceof IssueWithParams) {
3131                     result.add((IssueWithParams) issue);
3132                 } else {
3133                     result.add(
3134                             new IssueWithParams(sVerificationIssueIdToIssue.get(issue.getIssueId()),
3135                                     issue.getParams()));
3136                 }
3137             }
3138             return result;
3139         }
3140     }
3141 }
3142