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