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