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