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