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 com.android.apksig.apk.ApkFormatException;
20 import com.android.apksig.apk.ApkUtils;
21 import com.android.apksig.internal.apk.AndroidBinXmlParser;
22 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
23 import com.android.apksig.internal.apk.v1.V1SchemeVerifier;
24 import com.android.apksig.internal.apk.ContentDigestAlgorithm;
25 import com.android.apksig.internal.apk.SignatureAlgorithm;
26 import com.android.apksig.internal.apk.v2.V2SchemeVerifier;
27 import com.android.apksig.internal.apk.v3.V3SchemeVerifier;
28 import com.android.apksig.internal.util.AndroidSdkVersion;
29 import com.android.apksig.internal.zip.CentralDirectoryRecord;
30 import com.android.apksig.util.DataSource;
31 import com.android.apksig.util.DataSources;
32 import com.android.apksig.zip.ZipFormatException;
33 import java.io.Closeable;
34 import java.io.File;
35 import java.io.IOException;
36 import java.io.RandomAccessFile;
37 import java.nio.ByteBuffer;
38 import java.security.NoSuchAlgorithmException;
39 import java.security.cert.CertificateEncodingException;
40 import java.security.cert.X509Certificate;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 
49 /**
50  * APK signature verifier which mimics the behavior of the Android platform.
51  *
52  * <p>The verifier is designed to closely mimic the behavior of Android platforms. This is to enable
53  * the verifier to be used for checking whether an APK's signatures are expected to verify on
54  * Android.
55  *
56  * <p>Use {@link Builder} to obtain instances of this verifier.
57  *
58  * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a>
59  */
60 public class ApkVerifier {
61 
62     private static final Map<Integer, String> SUPPORTED_APK_SIG_SCHEME_NAMES =
63             loadSupportedApkSigSchemeNames();
64 
loadSupportedApkSigSchemeNames()65     private static Map<Integer,String> loadSupportedApkSigSchemeNames() {
66         Map<Integer, String> supportedMap = new HashMap<>(2);
67         supportedMap.put(
68                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2, "APK Signature Scheme v2");
69         supportedMap.put(
70                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3, "APK Signature Scheme v3");
71         return supportedMap;
72     }
73 
74     private final File mApkFile;
75     private final DataSource mApkDataSource;
76 
77     private final Integer mMinSdkVersion;
78     private final int mMaxSdkVersion;
79 
ApkVerifier( File apkFile, DataSource apkDataSource, Integer minSdkVersion, int maxSdkVersion)80     private ApkVerifier(
81             File apkFile,
82             DataSource apkDataSource,
83             Integer minSdkVersion,
84             int maxSdkVersion) {
85         mApkFile = apkFile;
86         mApkDataSource = apkDataSource;
87         mMinSdkVersion = minSdkVersion;
88         mMaxSdkVersion = maxSdkVersion;
89     }
90 
91     /**
92      * Verifies the APK's signatures and returns the result of verification. The APK can be
93      * considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
94      * The verification result also includes errors, warnings, and information about signers such
95      * as their signing certificates.
96      *
97      * <p>Verification succeeds iff the APK's signature is expected to verify on all Android
98      * platform versions specified via the {@link Builder}. If the APK's signature is expected to
99      * not verify on any of the specified platform versions, this method returns a result with one
100      * or more errors and whose {@link Result#isVerified()} returns {@code false}, or this method
101      * throws an exception.
102      *
103      * @throws IOException if an I/O error is encountered while reading the APK
104      * @throws ApkFormatException if the APK is malformed
105      * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
106      *         required cryptographic algorithm implementation is missing
107      * @throws IllegalStateException if this verifier's configuration is missing required
108      *         information.
109      */
verify()110     public Result verify() throws IOException, ApkFormatException, NoSuchAlgorithmException,
111             IllegalStateException {
112         Closeable in = null;
113         try {
114             DataSource apk;
115             if (mApkDataSource != null) {
116                 apk = mApkDataSource;
117             } else if (mApkFile != null) {
118                 RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
119                 in = f;
120                 apk = DataSources.asDataSource(f, 0, f.length());
121             } else {
122                 throw new IllegalStateException("APK not provided");
123             }
124             return verify(apk);
125         } finally {
126             if (in != null) {
127                 in.close();
128             }
129         }
130     }
131 
132     /**
133      * Verifies the APK's signatures and returns the result of verification. The APK can be
134      * considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
135      * The verification result also includes errors, warnings, and information about signers.
136      *
137      * @param apk APK file contents
138      *
139      * @throws IOException if an I/O error is encountered while reading the APK
140      * @throws ApkFormatException if the APK is malformed
141      * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
142      *         required cryptographic algorithm implementation is missing
143      */
verify(DataSource apk)144     private Result verify(DataSource apk)
145             throws IOException, ApkFormatException, NoSuchAlgorithmException {
146         if (mMinSdkVersion != null) {
147             if (mMinSdkVersion < 0) {
148                 throw new IllegalArgumentException(
149                         "minSdkVersion must not be negative: " + mMinSdkVersion);
150             }
151             if ((mMinSdkVersion != null) && (mMinSdkVersion > mMaxSdkVersion)) {
152                 throw new IllegalArgumentException(
153                         "minSdkVersion (" + mMinSdkVersion + ") > maxSdkVersion (" + mMaxSdkVersion
154                                 + ")");
155             }
156         }
157         int maxSdkVersion = mMaxSdkVersion;
158 
159         ApkUtils.ZipSections zipSections;
160         try {
161             zipSections = ApkUtils.findZipSections(apk);
162         } catch (ZipFormatException e) {
163             throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
164         }
165 
166         ByteBuffer androidManifest = null;
167 
168         int minSdkVersion;
169         if (mMinSdkVersion != null) {
170             // No need to obtain minSdkVersion from the APK's AndroidManifest.xml
171             minSdkVersion = mMinSdkVersion;
172         } else {
173             // Need to obtain minSdkVersion from the APK's AndroidManifest.xml
174             if (androidManifest == null) {
175                 androidManifest = getAndroidManifestFromApk(apk, zipSections);
176             }
177             minSdkVersion =
178                     ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest.slice());
179             if (minSdkVersion > mMaxSdkVersion) {
180                 throw new IllegalArgumentException(
181                         "minSdkVersion from APK (" + minSdkVersion + ") > maxSdkVersion ("
182                                 + mMaxSdkVersion + ")");
183             }
184         }
185 
186         Result result = new Result();
187 
188         // Android N and newer attempts to verify APKs using the APK Signing Block, which can
189         // include v2 and/or v3 signatures.  If none is found, it falls back to JAR signature
190         // verification. If the signature is found but does not verify, the APK is rejected.
191         Set<Integer> foundApkSigSchemeIds = new HashSet<>(2);
192         if (maxSdkVersion >= AndroidSdkVersion.N) {
193 
194             // Android P and newer attempts to verify APKs using APK Signature Scheme v3
195             if (maxSdkVersion >= AndroidSdkVersion.P) {
196                 try {
197                     ApkSigningBlockUtils.Result v3Result =
198                             V3SchemeVerifier.verify(
199                                     apk,
200                                     zipSections,
201                                     Math.max(minSdkVersion, AndroidSdkVersion.P),
202                                     maxSdkVersion);
203                     foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
204                     result.mergeFrom(v3Result);
205                 } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) {
206                     // v3 signature not required
207                 }
208                 if (result.containsErrors()) {
209                     return result;
210                 }
211             }
212 
213             // Attempt to verify the APK using v2 signing if necessary. Platforms prior to Android P
214             // ignore APK Signature Scheme v3 signatures and always attempt to verify either JAR or
215             // APK Signature Scheme v2 signatures.  Android P onwards verifies v2 signatures only if
216             // no APK Signature Scheme v3 (or newer scheme) signatures were found.
217             if (minSdkVersion < AndroidSdkVersion.P || foundApkSigSchemeIds.isEmpty()) {
218                 try {
219                     ApkSigningBlockUtils.Result v2Result =
220                             V2SchemeVerifier.verify(
221                                     apk,
222                                     zipSections,
223                                     SUPPORTED_APK_SIG_SCHEME_NAMES,
224                                     foundApkSigSchemeIds,
225                                     Math.max(minSdkVersion, AndroidSdkVersion.N),
226                                     maxSdkVersion);
227                     foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
228                     result.mergeFrom(v2Result);
229                 } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) {
230                     // v2 signature not required
231                 }
232                 if (result.containsErrors()) {
233                     return result;
234                 }
235             }
236         }
237 
238         // Android O and newer requires that APKs targeting security sandbox version 2 and higher
239         // are signed using APK Signature Scheme v2 or newer.
240         if (maxSdkVersion >= AndroidSdkVersion.O) {
241             if (androidManifest == null) {
242                 androidManifest = getAndroidManifestFromApk(apk, zipSections);
243             }
244             int targetSandboxVersion =
245                     getTargetSandboxVersionFromBinaryAndroidManifest(androidManifest.slice());
246             if (targetSandboxVersion > 1) {
247                 if (foundApkSigSchemeIds.isEmpty()) {
248                     result.addError(
249                             Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION,
250                             targetSandboxVersion);
251                 }
252             }
253         }
254 
255         // Attempt to verify the APK using JAR signing if necessary. Platforms prior to Android N
256         // ignore APK Signature Scheme v2 signatures and always attempt to verify JAR signatures.
257         // Android N onwards verifies JAR signatures only if no APK Signature Scheme v2 (or newer
258         // scheme) signatures were found.
259         if ((minSdkVersion < AndroidSdkVersion.N) || (foundApkSigSchemeIds.isEmpty())) {
260             V1SchemeVerifier.Result v1Result =
261                     V1SchemeVerifier.verify(
262                             apk,
263                             zipSections,
264                             SUPPORTED_APK_SIG_SCHEME_NAMES,
265                             foundApkSigSchemeIds,
266                             minSdkVersion,
267                             maxSdkVersion);
268             result.mergeFrom(v1Result);
269         }
270         if (result.containsErrors()) {
271             return result;
272         }
273 
274         // Check whether v1 and v2 scheme signer identifies match, provided both v1 and v2
275         // signatures verified.
276         if ((result.isVerifiedUsingV1Scheme()) && (result.isVerifiedUsingV2Scheme())) {
277             ArrayList<Result.V1SchemeSignerInfo> v1Signers =
278                     new ArrayList<>(result.getV1SchemeSigners());
279             ArrayList<Result.V2SchemeSignerInfo> v2Signers =
280                     new ArrayList<>(result.getV2SchemeSigners());
281             ArrayList<ByteArray> v1SignerCerts = new ArrayList<>();
282             ArrayList<ByteArray> v2SignerCerts = new ArrayList<>();
283             for (Result.V1SchemeSignerInfo signer : v1Signers) {
284                 try {
285                     v1SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
286                 } catch (CertificateEncodingException e) {
287                     throw new RuntimeException(
288                             "Failed to encode JAR signer " + signer.getName() + " certs", e);
289                 }
290             }
291             for (Result.V2SchemeSignerInfo signer : v2Signers) {
292                 try {
293                     v2SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
294                 } catch (CertificateEncodingException e) {
295                     throw new RuntimeException(
296                             "Failed to encode APK Signature Scheme v2 signer (index: "
297                                     + signer.getIndex() + ") certs",
298                             e);
299                 }
300             }
301 
302             for (int i = 0; i < v1SignerCerts.size(); i++) {
303                 ByteArray v1Cert = v1SignerCerts.get(i);
304                 if (!v2SignerCerts.contains(v1Cert)) {
305                     Result.V1SchemeSignerInfo v1Signer = v1Signers.get(i);
306                     v1Signer.addError(Issue.V2_SIG_MISSING);
307                     break;
308                 }
309             }
310             for (int i = 0; i < v2SignerCerts.size(); i++) {
311                 ByteArray v2Cert = v2SignerCerts.get(i);
312                 if (!v1SignerCerts.contains(v2Cert)) {
313                     Result.V2SchemeSignerInfo v2Signer = v2Signers.get(i);
314                     v2Signer.addError(Issue.JAR_SIG_MISSING);
315                     break;
316                 }
317             }
318         }
319 
320         // If there is a v3 scheme signer and an earlier scheme signer, make sure that there is a
321         // match, or in the event of signing certificate rotation, that the v1/v2 scheme signer
322         // matches the oldest signing certificate in the provided SigningCertificateLineage
323         if (result.isVerifiedUsingV3Scheme()
324                 && (result.isVerifiedUsingV1Scheme() || result.isVerifiedUsingV2Scheme())) {
325             SigningCertificateLineage lineage = result.getSigningCertificateLineage();
326             X509Certificate oldSignerCert;
327             if (result.isVerifiedUsingV1Scheme()) {
328                 List<Result.V1SchemeSignerInfo> v1Signers = result.getV1SchemeSigners();
329                 if (v1Signers.size() != 1) {
330                     // APK Signature Scheme v3 only supports single-signers, error to sign with
331                     // multiple and then only one
332                     result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS);
333                 }
334                 oldSignerCert = v1Signers.get(0).mCertChain.get(0);
335             } else {
336                 List<Result.V2SchemeSignerInfo> v2Signers = result.getV2SchemeSigners();
337                 if (v2Signers.size() != 1) {
338                     // APK Signature Scheme v3 only supports single-signers, error to sign with
339                     // multiple and then only one
340                     result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS);
341                 }
342                 oldSignerCert = v2Signers.get(0).mCerts.get(0);
343             }
344             if (lineage == null) {
345                 // no signing certificate history with which to contend, just make sure that v3
346                 // matches previous versions
347                 List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
348                 if (v3Signers.size() != 1) {
349                     // multiple v3 signers should never exist without rotation history, since
350                     // multiple signers implies a different signer for different platform versions
351                     result.addError(Issue.V3_SIG_MULTIPLE_SIGNERS);
352                 }
353                 try {
354                     if (!Arrays.equals(oldSignerCert.getEncoded(),
355                            v3Signers.get(0).mCerts.get(0).getEncoded())) {
356                         result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
357                     }
358                 } catch (CertificateEncodingException e) {
359                     // we just go the encoding for the v1/v2 certs above, so must be v3
360                     throw new RuntimeException(
361                             "Failed to encode APK Signature Scheme v3 signer cert", e);
362                 }
363             } else {
364                 // we have some signing history, make sure that the root of the history is the same
365                 // as our v1/v2 signer
366                 try {
367                     lineage = lineage.getSubLineage(oldSignerCert);
368                     if (lineage.size() != 1) {
369                         // the v1/v2 signer was found, but not at the root of the lineage
370                         result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
371                     }
372                 } catch (IllegalArgumentException e) {
373                     // the v1/v2 signer was not found in the lineage
374                     result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
375                 }
376             }
377         }
378 
379         if (result.containsErrors()) {
380             return result;
381         }
382 
383         // Verified
384         result.setVerified();
385         if (result.isVerifiedUsingV3Scheme()) {
386             List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
387             result.addSignerCertificate(v3Signers.get(v3Signers.size() - 1).getCertificate());
388         } else if (result.isVerifiedUsingV2Scheme()) {
389             for (Result.V2SchemeSignerInfo signerInfo : result.getV2SchemeSigners()) {
390                 result.addSignerCertificate(signerInfo.getCertificate());
391             }
392         } else if (result.isVerifiedUsingV1Scheme()) {
393             for (Result.V1SchemeSignerInfo signerInfo : result.getV1SchemeSigners()) {
394                 result.addSignerCertificate(signerInfo.getCertificate());
395             }
396         } else {
397             throw new RuntimeException(
398                     "APK verified, but has not verified using any of v1, v2 or v3schemes");
399         }
400 
401         return result;
402     }
403 
getAndroidManifestFromApk( DataSource apk, ApkUtils.ZipSections zipSections)404     private static ByteBuffer getAndroidManifestFromApk(
405             DataSource apk, ApkUtils.ZipSections zipSections)
406                     throws IOException, ApkFormatException {
407         List<CentralDirectoryRecord> cdRecords =
408                 V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections);
409         try {
410             return ApkSigner.getAndroidManifestFromApk(
411                     cdRecords,
412                     apk.slice(0, zipSections.getZipCentralDirectoryOffset()));
413         } catch (ZipFormatException e) {
414             throw new ApkFormatException("Failed to read AndroidManifest.xml", e);
415         }
416     }
417 
418     /**
419      * Android resource ID of the {@code android:targetSandboxVersion} attribute in
420      * AndroidManifest.xml.
421      */
422     private static final int TARGET_SANDBOX_VERSION_ATTR_ID = 0x0101054c;
423 
424     /**
425      * Returns the security sandbox version targeted by an APK with the provided
426      * {@code AndroidManifest.xml}.
427      *
428      * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android
429      *        resource format
430      *
431      * @throws ApkFormatException if an error occurred while determining the version
432      */
getTargetSandboxVersionFromBinaryAndroidManifest( ByteBuffer androidManifestContents)433     private static int getTargetSandboxVersionFromBinaryAndroidManifest(
434             ByteBuffer androidManifestContents) throws ApkFormatException {
435         // Return the value of the android:targetSandboxVersion attribute of the top-level manifest
436         // element
437         try {
438             AndroidBinXmlParser parser = new AndroidBinXmlParser(androidManifestContents);
439             int eventType = parser.getEventType();
440             while (eventType != AndroidBinXmlParser.EVENT_END_DOCUMENT) {
441                 if ((eventType == AndroidBinXmlParser.EVENT_START_ELEMENT)
442                         && (parser.getDepth() == 1)
443                         && ("manifest".equals(parser.getName()))
444                         && (parser.getNamespace().isEmpty())) {
445                     // In each manifest element, targetSandboxVersion defaults to 1
446                     int result = 1;
447                     for (int i = 0; i < parser.getAttributeCount(); i++) {
448                         if (parser.getAttributeNameResourceId(i)
449                                 == TARGET_SANDBOX_VERSION_ATTR_ID) {
450                             int valueType = parser.getAttributeValueType(i);
451                             switch (valueType) {
452                                 case AndroidBinXmlParser.VALUE_TYPE_INT:
453                                     result = parser.getAttributeIntValue(i);
454                                     break;
455                                 default:
456                                     throw new ApkFormatException(
457                                             "Failed to determine APK's target sandbox version"
458                                                     + ": unsupported value type of"
459                                                     + " AndroidManifest.xml"
460                                                     + " android:targetSandboxVersion"
461                                                     + ". Only integer values supported.");
462                             }
463                             break;
464                         }
465                     }
466                     return result;
467                 }
468                 eventType = parser.next();
469             }
470             throw new ApkFormatException(
471                     "Failed to determine APK's target sandbox version"
472                             + " : no manifest element in AndroidManifest.xml");
473         } catch (AndroidBinXmlParser.XmlParserException e) {
474             throw new ApkFormatException(
475                     "Failed to determine APK's target sandbox version"
476                             + ": malformed AndroidManifest.xml",
477                     e);
478         }
479     }
480 
481     /**
482      * Result of verifying an APKs signatures. The APK can be considered verified iff
483      * {@link #isVerified()} returns {@code true}.
484      */
485     public static class Result {
486         private final List<IssueWithParams> mErrors = new ArrayList<>();
487         private final List<IssueWithParams> mWarnings = new ArrayList<>();
488         private final List<X509Certificate> mSignerCerts = new ArrayList<>();
489         private final List<V1SchemeSignerInfo> mV1SchemeSigners = new ArrayList<>();
490         private final List<V1SchemeSignerInfo> mV1SchemeIgnoredSigners = new ArrayList<>();
491         private final List<V2SchemeSignerInfo> mV2SchemeSigners = new ArrayList<>();
492         private final List<V3SchemeSignerInfo> mV3SchemeSigners = new ArrayList<>();
493 
494         private boolean mVerified;
495         private boolean mVerifiedUsingV1Scheme;
496         private boolean mVerifiedUsingV2Scheme;
497         private boolean mVerifiedUsingV3Scheme;
498         private SigningCertificateLineage mSigningCertificateLineage;
499 
500         /**
501          * Returns {@code true} if the APK's signatures verified.
502          */
isVerified()503         public boolean isVerified() {
504             return mVerified;
505         }
506 
setVerified()507         private void setVerified() {
508             mVerified = true;
509         }
510 
511         /**
512          * Returns {@code true} if the APK's JAR signatures verified.
513          */
isVerifiedUsingV1Scheme()514         public boolean isVerifiedUsingV1Scheme() {
515             return mVerifiedUsingV1Scheme;
516         }
517 
518         /**
519          * Returns {@code true} if the APK's APK Signature Scheme v2 signatures verified.
520          */
isVerifiedUsingV2Scheme()521         public boolean isVerifiedUsingV2Scheme() {
522             return mVerifiedUsingV2Scheme;
523         }
524 
525         /**
526          * Returns {@code true} if the APK's APK Signature Scheme v3 signature verified.
527          */
isVerifiedUsingV3Scheme()528         public boolean isVerifiedUsingV3Scheme() {
529             return mVerifiedUsingV3Scheme;
530         }
531 
532         /**
533          * Returns the verified signers' certificates, one per signer.
534          */
getSignerCertificates()535         public List<X509Certificate> getSignerCertificates() {
536             return mSignerCerts;
537         }
538 
addSignerCertificate(X509Certificate cert)539         private void addSignerCertificate(X509Certificate cert) {
540             mSignerCerts.add(cert);
541         }
542 
543         /**
544          * Returns information about JAR signers associated with the APK's signature. These are the
545          * signers used by Android.
546          *
547          * @see #getV1SchemeIgnoredSigners()
548          */
getV1SchemeSigners()549         public List<V1SchemeSignerInfo> getV1SchemeSigners() {
550             return mV1SchemeSigners;
551         }
552 
553         /**
554          * Returns information about JAR signers ignored by the APK's signature verification
555          * process. These signers are ignored by Android. However, each signer's errors or warnings
556          * will contain information about why they are ignored.
557          *
558          * @see #getV1SchemeSigners()
559          */
getV1SchemeIgnoredSigners()560         public List<V1SchemeSignerInfo> getV1SchemeIgnoredSigners() {
561             return mV1SchemeIgnoredSigners;
562         }
563 
564         /**
565          * Returns information about APK Signature Scheme v2 signers associated with the APK's
566          * signature.
567          */
getV2SchemeSigners()568         public List<V2SchemeSignerInfo> getV2SchemeSigners() {
569             return mV2SchemeSigners;
570         }
571 
572         /**
573          * Returns information about APK Signature Scheme v3 signers associated with the APK's
574          * signature.
575          *
576          * <note> Multiple signers represent different targeted platform versions, not
577          * a signing identity of multiple signers.  APK Signature Scheme v3 only supports single
578          * signer identities.</note>
579          */
getV3SchemeSigners()580         public List<V3SchemeSignerInfo> getV3SchemeSigners() {
581             return mV3SchemeSigners;
582         }
583 
584         /**
585          * Returns the combined SigningCertificateLineage associated with this APK's APK Signature
586          * Scheme v3 signing block.
587          */
getSigningCertificateLineage()588         public SigningCertificateLineage getSigningCertificateLineage() {
589             return mSigningCertificateLineage;
590         }
591 
addError(Issue msg, Object... parameters)592         void addError(Issue msg, Object... parameters) {
593             mErrors.add(new IssueWithParams(msg, parameters));
594         }
595 
596         /**
597          * Returns errors encountered while verifying the APK's signatures.
598          */
getErrors()599         public List<IssueWithParams> getErrors() {
600             return mErrors;
601         }
602 
603         /**
604          * Returns warnings encountered while verifying the APK's signatures.
605          */
getWarnings()606         public List<IssueWithParams> getWarnings() {
607             return mWarnings;
608         }
609 
mergeFrom(V1SchemeVerifier.Result source)610         private void mergeFrom(V1SchemeVerifier.Result source) {
611             mVerifiedUsingV1Scheme = source.verified;
612             mErrors.addAll(source.getErrors());
613             mWarnings.addAll(source.getWarnings());
614             for (V1SchemeVerifier.Result.SignerInfo signer : source.signers) {
615                 mV1SchemeSigners.add(new V1SchemeSignerInfo(signer));
616             }
617             for (V1SchemeVerifier.Result.SignerInfo signer : source.ignoredSigners) {
618                 mV1SchemeIgnoredSigners.add(new V1SchemeSignerInfo(signer));
619             }
620         }
621 
mergeFrom(ApkSigningBlockUtils.Result source)622         private void mergeFrom(ApkSigningBlockUtils.Result source) {
623             switch (source.signatureSchemeVersion) {
624                 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
625                     mVerifiedUsingV2Scheme = source.verified;
626                     for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) {
627                         mV2SchemeSigners.add(new V2SchemeSignerInfo(signer));
628                     }
629                     break;
630                 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3:
631                     mVerifiedUsingV3Scheme = source.verified;
632                     for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) {
633                         mV3SchemeSigners.add(new V3SchemeSignerInfo(signer));
634                     }
635                     mSigningCertificateLineage = source.signingCertificateLineage;
636                     break;
637                 default:
638                     throw new IllegalArgumentException("Unknown Signing Block Scheme Id");
639             }
640             mErrors.addAll(source.getErrors());
641             mWarnings.addAll(source.getWarnings());
642         }
643 
644         /**
645          * Returns {@code true} if an error was encountered while verifying the APK. Any error
646          * prevents the APK from being considered verified.
647          */
containsErrors()648         public boolean containsErrors() {
649             if (!mErrors.isEmpty()) {
650                 return true;
651             }
652             if (!mV1SchemeSigners.isEmpty()) {
653                 for (V1SchemeSignerInfo signer : mV1SchemeSigners) {
654                     if (signer.containsErrors()) {
655                         return true;
656                     }
657                 }
658             }
659             if (!mV2SchemeSigners.isEmpty()) {
660                 for (V2SchemeSignerInfo signer : mV2SchemeSigners) {
661                     if (signer.containsErrors()) {
662                         return true;
663                     }
664                 }
665             }
666             if (!mV3SchemeSigners.isEmpty()) {
667                 for (V3SchemeSignerInfo signer : mV3SchemeSigners) {
668                     if (signer.containsErrors()) {
669                         return true;
670                     }
671                 }
672             }
673 
674             return false;
675         }
676 
677         /**
678          * Information about a JAR signer associated with the APK's signature.
679          */
680         public static class V1SchemeSignerInfo {
681             private final String mName;
682             private final List<X509Certificate> mCertChain;
683             private final String mSignatureBlockFileName;
684             private final String mSignatureFileName;
685 
686             private final List<IssueWithParams> mErrors;
687             private final List<IssueWithParams> mWarnings;
688 
V1SchemeSignerInfo(V1SchemeVerifier.Result.SignerInfo result)689             private V1SchemeSignerInfo(V1SchemeVerifier.Result.SignerInfo result) {
690                 mName = result.name;
691                 mCertChain = result.certChain;
692                 mSignatureBlockFileName = result.signatureBlockFileName;
693                 mSignatureFileName = result.signatureFileName;
694                 mErrors = result.getErrors();
695                 mWarnings = result.getWarnings();
696             }
697 
698             /**
699              * Returns a user-friendly name of the signer.
700              */
getName()701             public String getName() {
702                 return mName;
703             }
704 
705             /**
706              * Returns the name of the JAR entry containing this signer's JAR signature block file.
707              */
getSignatureBlockFileName()708             public String getSignatureBlockFileName() {
709                 return mSignatureBlockFileName;
710             }
711 
712             /**
713              * Returns the name of the JAR entry containing this signer's JAR signature file.
714              */
getSignatureFileName()715             public String getSignatureFileName() {
716                 return mSignatureFileName;
717             }
718 
719             /**
720              * Returns this signer's signing certificate or {@code null} if not available. The
721              * certificate is guaranteed to be available if no errors were encountered during
722              * verification (see {@link #containsErrors()}.
723              *
724              * <p>This certificate contains the signer's public key.
725              */
getCertificate()726             public X509Certificate getCertificate() {
727                 return mCertChain.isEmpty() ? null : mCertChain.get(0);
728             }
729 
730             /**
731              * Returns the certificate chain for the signer's public key. The certificate containing
732              * the public key is first, followed by the certificate (if any) which issued the
733              * signing certificate, and so forth. An empty list may be returned if an error was
734              * encountered during verification (see {@link #containsErrors()}).
735              */
getCertificateChain()736             public List<X509Certificate> getCertificateChain() {
737                 return mCertChain;
738             }
739 
740             /**
741              * Returns {@code true} if an error was encountered while verifying this signer's JAR
742              * signature. Any error prevents the signer's signature from being considered verified.
743              */
containsErrors()744             public boolean containsErrors() {
745                 return !mErrors.isEmpty();
746             }
747 
748             /**
749              * Returns errors encountered while verifying this signer's JAR signature. Any error
750              * prevents the signer's signature from being considered verified.
751              */
getErrors()752             public List<IssueWithParams> getErrors() {
753                 return mErrors;
754             }
755 
756             /**
757              * Returns warnings encountered while verifying this signer's JAR signature. Warnings
758              * do not prevent the signer's signature from being considered verified.
759              */
getWarnings()760             public List<IssueWithParams> getWarnings() {
761                 return mWarnings;
762             }
763 
addError(Issue msg, Object... parameters)764             private void addError(Issue msg, Object... parameters) {
765                 mErrors.add(new IssueWithParams(msg, parameters));
766             }
767         }
768 
769         /**
770          * Information about an APK Signature Scheme v2 signer associated with the APK's signature.
771          */
772         public static class V2SchemeSignerInfo {
773             private final int mIndex;
774             private final List<X509Certificate> mCerts;
775 
776             private final List<IssueWithParams> mErrors;
777             private final List<IssueWithParams> mWarnings;
778 
V2SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result)779             private V2SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) {
780                 mIndex = result.index;
781                 mCerts = result.certs;
782                 mErrors = result.getErrors();
783                 mWarnings = result.getWarnings();
784             }
785 
786             /**
787              * Returns this signer's {@code 0}-based index in the list of signers contained in the
788              * APK's APK Signature Scheme v2 signature.
789              */
getIndex()790             public int getIndex() {
791                 return mIndex;
792             }
793 
794             /**
795              * Returns this signer's signing certificate or {@code null} if not available. The
796              * certificate is guaranteed to be available if no errors were encountered during
797              * verification (see {@link #containsErrors()}.
798              *
799              * <p>This certificate contains the signer's public key.
800              */
getCertificate()801             public X509Certificate getCertificate() {
802                 return mCerts.isEmpty() ? null : mCerts.get(0);
803             }
804 
805             /**
806              * Returns this signer's certificates. The first certificate is for the signer's public
807              * key. An empty list may be returned if an error was encountered during verification
808              * (see {@link #containsErrors()}).
809              */
getCertificates()810             public List<X509Certificate> getCertificates() {
811                 return mCerts;
812             }
813 
addError(Issue msg, Object... parameters)814             private void addError(Issue msg, Object... parameters) {
815                 mErrors.add(new IssueWithParams(msg, parameters));
816             }
817 
containsErrors()818             public boolean containsErrors() {
819                 return !mErrors.isEmpty();
820             }
821 
getErrors()822             public List<IssueWithParams> getErrors() {
823                 return mErrors;
824             }
825 
getWarnings()826             public List<IssueWithParams> getWarnings() {
827                 return mWarnings;
828             }
829         }
830 
831         /**
832          * Information about an APK Signature Scheme v3 signer associated with the APK's signature.
833          */
834         public static class V3SchemeSignerInfo {
835             private final int mIndex;
836             private final List<X509Certificate> mCerts;
837 
838             private final List<IssueWithParams> mErrors;
839             private final List<IssueWithParams> mWarnings;
840 
V3SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result)841             private V3SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) {
842                 mIndex = result.index;
843                 mCerts = result.certs;
844                 mErrors = result.getErrors();
845                 mWarnings = result.getWarnings();
846             }
847 
848             /**
849              * Returns this signer's {@code 0}-based index in the list of signers contained in the
850              * APK's APK Signature Scheme v3 signature.
851              */
getIndex()852             public int getIndex() {
853                 return mIndex;
854             }
855 
856             /**
857              * Returns this signer's signing certificate or {@code null} if not available. The
858              * certificate is guaranteed to be available if no errors were encountered during
859              * verification (see {@link #containsErrors()}.
860              *
861              * <p>This certificate contains the signer's public key.
862              */
getCertificate()863             public X509Certificate getCertificate() {
864                 return mCerts.isEmpty() ? null : mCerts.get(0);
865             }
866 
867             /**
868              * Returns this signer's certificates. The first certificate is for the signer's public
869              * key. An empty list may be returned if an error was encountered during verification
870              * (see {@link #containsErrors()}).
871              */
getCertificates()872             public List<X509Certificate> getCertificates() {
873                 return mCerts;
874             }
875 
containsErrors()876             public boolean containsErrors() {
877                 return !mErrors.isEmpty();
878             }
879 
getErrors()880             public List<IssueWithParams> getErrors() {
881                 return mErrors;
882             }
883 
getWarnings()884             public List<IssueWithParams> getWarnings() {
885                 return mWarnings;
886             }
887         }
888     }
889 
890     /**
891      * Error or warning encountered while verifying an APK's signatures.
892      */
893     public static enum Issue {
894 
895         /**
896          * APK is not JAR-signed.
897          */
898         JAR_SIG_NO_SIGNATURES("No JAR signatures"),
899 
900         /**
901          * APK does not contain any entries covered by JAR signatures.
902          */
903         JAR_SIG_NO_SIGNED_ZIP_ENTRIES("No JAR entries covered by JAR signatures"),
904 
905         /**
906          * APK contains multiple entries with the same name.
907          *
908          * <ul>
909          * <li>Parameter 1: name ({@code String})</li>
910          * </ul>
911          */
912         JAR_SIG_DUPLICATE_ZIP_ENTRY("Duplicate entry: %1$s"),
913 
914         /**
915          * JAR manifest contains a section with a duplicate name.
916          *
917          * <ul>
918          * <li>Parameter 1: section name ({@code String})</li>
919          * </ul>
920          */
921         JAR_SIG_DUPLICATE_MANIFEST_SECTION("Duplicate section in META-INF/MANIFEST.MF: %1$s"),
922 
923         /**
924          * JAR manifest contains a section without a name.
925          *
926          * <ul>
927          * <li>Parameter 1: section index (1-based) ({@code Integer})</li>
928          * </ul>
929          */
930         JAR_SIG_UNNNAMED_MANIFEST_SECTION(
931                 "Malformed META-INF/MANIFEST.MF: invidual section #%1$d does not have a name"),
932 
933         /**
934          * JAR signature file contains a section without a name.
935          *
936          * <ul>
937          * <li>Parameter 1: signature file name ({@code String})</li>
938          * <li>Parameter 2: section index (1-based) ({@code Integer})</li>
939          * </ul>
940          */
941         JAR_SIG_UNNNAMED_SIG_FILE_SECTION(
942                 "Malformed %1$s: invidual section #%2$d does not have a name"),
943 
944         /** APK is missing the JAR manifest entry (META-INF/MANIFEST.MF). */
945         JAR_SIG_NO_MANIFEST("Missing META-INF/MANIFEST.MF"),
946 
947         /**
948          * JAR manifest references an entry which is not there in the APK.
949          *
950          * <ul>
951          * <li>Parameter 1: entry name ({@code String})</li>
952          * </ul>
953          */
954         JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST(
955                 "%1$s entry referenced by META-INF/MANIFEST.MF not found in the APK"),
956 
957         /**
958          * JAR manifest does not list a digest for the specified entry.
959          *
960          * <ul>
961          * <li>Parameter 1: entry name ({@code String})</li>
962          * </ul>
963          */
964         JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST("No digest for %1$s in META-INF/MANIFEST.MF"),
965 
966         /**
967          * JAR signature does not list a digest for the specified entry.
968          *
969          * <ul>
970          * <li>Parameter 1: entry name ({@code String})</li>
971          * <li>Parameter 2: signature file name ({@code String})</li>
972          * </ul>
973          */
974         JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE("No digest for %1$s in %2$s"),
975 
976         /**
977          * The specified JAR entry is not covered by JAR signature.
978          *
979          * <ul>
980          * <li>Parameter 1: entry name ({@code String})</li>
981          * </ul>
982          */
983         JAR_SIG_ZIP_ENTRY_NOT_SIGNED("%1$s entry not signed"),
984 
985         /**
986          * JAR signature uses different set of signers to protect the two specified ZIP entries.
987          *
988          * <ul>
989          * <li>Parameter 1: first entry name ({@code String})</li>
990          * <li>Parameter 2: first entry signer names ({@code List<String>})</li>
991          * <li>Parameter 3: second entry name ({@code String})</li>
992          * <li>Parameter 4: second entry signer names ({@code List<String>})</li>
993          * </ul>
994          */
995         JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH(
996                 "Entries %1$s and %3$s are signed with different sets of signers"
997                         + " : <%2$s> vs <%4$s>"),
998 
999         /**
1000          * Digest of the specified ZIP entry's data does not match the digest expected by the JAR
1001          * signature.
1002          *
1003          * <ul>
1004          * <li>Parameter 1: entry name ({@code String})</li>
1005          * <li>Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})</li>
1006          * <li>Parameter 3: name of the entry in which the expected digest is specified
1007          *     ({@code String})</li>
1008          * <li>Parameter 4: base64-encoded actual digest ({@code String})</li>
1009          * <li>Parameter 5: base64-encoded expected digest ({@code String})</li>
1010          * </ul>
1011          */
1012         JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY(
1013                 "%2$s digest of %1$s does not match the digest specified in %3$s"
1014                         + ". Expected: <%5$s>, actual: <%4$s>"),
1015 
1016         /**
1017          * Digest of the JAR manifest main section did not verify.
1018          *
1019          * <ul>
1020          * <li>Parameter 1: digest algorithm (e.g., SHA-256) ({@code String})</li>
1021          * <li>Parameter 2: name of the entry in which the expected digest is specified
1022          *     ({@code String})</li>
1023          * <li>Parameter 3: base64-encoded actual digest ({@code String})</li>
1024          * <li>Parameter 4: base64-encoded expected digest ({@code String})</li>
1025          * </ul>
1026          */
1027         JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY(
1028                 "%1$s digest of META-INF/MANIFEST.MF main section does not match the digest"
1029                         + " specified in %2$s. Expected: <%4$s>, actual: <%3$s>"),
1030 
1031         /**
1032          * Digest of the specified JAR manifest section does not match the digest expected by the
1033          * JAR signature.
1034          *
1035          * <ul>
1036          * <li>Parameter 1: section name ({@code String})</li>
1037          * <li>Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})</li>
1038          * <li>Parameter 3: name of the signature file in which the expected digest is specified
1039          *     ({@code String})</li>
1040          * <li>Parameter 4: base64-encoded actual digest ({@code String})</li>
1041          * <li>Parameter 5: base64-encoded expected digest ({@code String})</li>
1042          * </ul>
1043          */
1044         JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY(
1045                 "%2$s digest of META-INF/MANIFEST.MF section for %1$s does not match the digest"
1046                         + " specified in %3$s. Expected: <%5$s>, actual: <%4$s>"),
1047 
1048         /**
1049          * JAR signature file does not contain the whole-file digest of the JAR manifest file. The
1050          * digest speeds up verification of JAR signature.
1051          *
1052          * <ul>
1053          * <li>Parameter 1: name of the signature file ({@code String})</li>
1054          * </ul>
1055          */
1056         JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE(
1057                 "%1$s does not specify digest of META-INF/MANIFEST.MF"
1058                         + ". This slows down verification."),
1059 
1060         /**
1061          * APK is signed using APK Signature Scheme v2 or newer, but JAR signature file does not
1062          * contain protections against stripping of these newer scheme signatures.
1063          *
1064          * <ul>
1065          * <li>Parameter 1: name of the signature file ({@code String})</li>
1066          * </ul>
1067          */
1068         JAR_SIG_NO_APK_SIG_STRIP_PROTECTION(
1069                 "APK is signed using APK Signature Scheme v2 but these signatures may be stripped"
1070                         + " without being detected because %1$s does not contain anti-stripping"
1071                         + " protections."),
1072 
1073         /**
1074          * JAR signature of the signer is missing a file/entry.
1075          *
1076          * <ul>
1077          * <li>Parameter 1: name of the encountered file ({@code String})</li>
1078          * <li>Parameter 2: name of the missing file ({@code String})</li>
1079          * </ul>
1080          */
1081         JAR_SIG_MISSING_FILE("Partial JAR signature. Found: %1$s, missing: %2$s"),
1082 
1083         /**
1084          * An exception was encountered while verifying JAR signature contained in a signature block
1085          * against the signature file.
1086          *
1087          * <ul>
1088          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1089          * <li>Parameter 2: name of the signature file ({@code String})</li>
1090          * <li>Parameter 3: exception ({@code Throwable})</li>
1091          * </ul>
1092          */
1093         JAR_SIG_VERIFY_EXCEPTION("Failed to verify JAR signature %1$s against %2$s: %3$s"),
1094 
1095         /**
1096          * JAR signature contains unsupported digest algorithm.
1097          *
1098          * <ul>
1099          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1100          * <li>Parameter 2: digest algorithm OID ({@code String})</li>
1101          * <li>Parameter 3: signature algorithm OID ({@code String})</li>
1102          * <li>Parameter 4: API Levels on which this combination of algorithms is not supported
1103          *     ({@code String})</li>
1104          * <li>Parameter 5: user-friendly variant of digest algorithm ({@code String})</li>
1105          * <li>Parameter 6: user-friendly variant of signature algorithm ({@code String})</li>
1106          * </ul>
1107          */
1108         JAR_SIG_UNSUPPORTED_SIG_ALG(
1109                 "JAR signature %1$s uses digest algorithm %5$s and signature algorithm %6$s which"
1110                         + " is not supported on API Level(s) %4$s for which this APK is being"
1111                         + " verified"),
1112 
1113         /**
1114          * An exception was encountered while parsing JAR signature contained in a signature block.
1115          *
1116          * <ul>
1117          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1118          * <li>Parameter 2: exception ({@code Throwable})</li>
1119          * </ul>
1120          */
1121         JAR_SIG_PARSE_EXCEPTION("Failed to parse JAR signature %1$s: %2$s"),
1122 
1123         /**
1124          * An exception was encountered while parsing a certificate contained in the JAR signature
1125          * block.
1126          *
1127          * <ul>
1128          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1129          * <li>Parameter 2: exception ({@code Throwable})</li>
1130          * </ul>
1131          */
1132         JAR_SIG_MALFORMED_CERTIFICATE("Malformed certificate in JAR signature %1$s: %2$s"),
1133 
1134         /**
1135          * JAR signature contained in a signature block file did not verify against the signature
1136          * file.
1137          *
1138          * <ul>
1139          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1140          * <li>Parameter 2: name of the signature file ({@code String})</li>
1141          * </ul>
1142          */
1143         JAR_SIG_DID_NOT_VERIFY("JAR signature %1$s did not verify against %2$s"),
1144 
1145         /**
1146          * JAR signature contains no verified signers.
1147          *
1148          * <ul>
1149          * <li>Parameter 1: name of the signature block file ({@code String})</li>
1150          * </ul>
1151          */
1152         JAR_SIG_NO_SIGNERS("JAR signature %1$s contains no signers"),
1153 
1154         /**
1155          * JAR signature file contains a section with a duplicate name.
1156          *
1157          * <ul>
1158          * <li>Parameter 1: signature file name ({@code String})</li>
1159          * <li>Parameter 1: section name ({@code String})</li>
1160          * </ul>
1161          */
1162         JAR_SIG_DUPLICATE_SIG_FILE_SECTION("Duplicate section in %1$s: %2$s"),
1163 
1164         /**
1165          * JAR signature file's main section doesn't contain the mandatory Signature-Version
1166          * attribute.
1167          *
1168          * <ul>
1169          * <li>Parameter 1: signature file name ({@code String})</li>
1170          * </ul>
1171          */
1172         JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE(
1173                 "Malformed %1$s: missing Signature-Version attribute"),
1174 
1175         /**
1176          * JAR signature file references an unknown APK signature scheme ID.
1177          *
1178          * <ul>
1179          * <li>Parameter 1: name of the signature file ({@code String})</li>
1180          * <li>Parameter 2: unknown APK signature scheme ID ({@code} Integer)</li>
1181          * </ul>
1182          */
1183         JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
1184                 "JAR signature %1$s references unknown APK signature scheme ID: %2$d"),
1185 
1186         /**
1187          * JAR signature file indicates that the APK is supposed to be signed with a supported APK
1188          * signature scheme (in addition to the JAR signature) but no such signature was found in
1189          * the APK.
1190          *
1191          * <ul>
1192          * <li>Parameter 1: name of the signature file ({@code String})</li>
1193          * <li>Parameter 2: APK signature scheme ID ({@code} Integer)</li>
1194          * <li>Parameter 3: APK signature scheme English name ({@code} String)</li>
1195          * </ul>
1196          */
1197         JAR_SIG_MISSING_APK_SIG_REFERENCED(
1198                 "JAR signature %1$s indicates the APK is signed using %3$s but no such signature"
1199                         + " was found. Signature stripped?"),
1200 
1201         /**
1202          * JAR entry is not covered by signature and thus unauthorized modifications to its contents
1203          * will not be detected.
1204          *
1205          * <ul>
1206          * <li>Parameter 1: entry name ({@code String})</li>
1207          * </ul>
1208          */
1209         JAR_SIG_UNPROTECTED_ZIP_ENTRY(
1210                 "%1$s not protected by signature. Unauthorized modifications to this JAR entry"
1211                         + " will not be detected. Delete or move the entry outside of META-INF/."),
1212 
1213         /**
1214          * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains an APK
1215          * Signature Scheme v2 signature from this signer, but does not contain a JAR signature
1216          * from this signer.
1217          */
1218         JAR_SIG_MISSING("No JAR signature from this signer"),
1219 
1220         /**
1221          * APK is targeting a sandbox version which requires APK Signature Scheme v2 signature but
1222          * no such signature was found.
1223          *
1224          * <ul>
1225          * <li>Parameter 1: target sandbox version ({@code Integer})</li>
1226          * </ul>
1227          */
1228         NO_SIG_FOR_TARGET_SANDBOX_VERSION(
1229                 "Missing APK Signature Scheme v2 signature required for target sandbox version"
1230                         + " %1$d"),
1231 
1232         /**
1233          * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains a JAR
1234          * signature from this signer, but does not contain an APK Signature Scheme v2 signature
1235          * from this signer.
1236          */
1237         V2_SIG_MISSING("No APK Signature Scheme v2 signature from this signer"),
1238 
1239         /**
1240          * Failed to parse the list of signers contained in the APK Signature Scheme v2 signature.
1241          */
1242         V2_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
1243 
1244         /**
1245          * Failed to parse this signer's signer block contained in the APK Signature Scheme v2
1246          * signature.
1247          */
1248         V2_SIG_MALFORMED_SIGNER("Malformed signer block"),
1249 
1250         /**
1251          * Public key embedded in the APK Signature Scheme v2 signature of this signer could not be
1252          * parsed.
1253          *
1254          * <ul>
1255          * <li>Parameter 1: error details ({@code Throwable})</li>
1256          * </ul>
1257          */
1258         V2_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
1259 
1260         /**
1261          * This APK Signature Scheme v2 signer's certificate could not be parsed.
1262          *
1263          * <ul>
1264          * <li>Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of
1265          *     certificates ({@code Integer})</li>
1266          * <li>Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's
1267          *     list of certificates ({@code Integer})</li>
1268          * <li>Parameter 3: error details ({@code Throwable})</li>
1269          * </ul>
1270          */
1271         V2_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
1272 
1273         /**
1274          * Failed to parse this signer's signature record contained in the APK Signature Scheme v2
1275          * signature.
1276          *
1277          * <ul>
1278          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
1279          * </ul>
1280          */
1281         V2_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v2 signature record #%1$d"),
1282 
1283         /**
1284          * Failed to parse this signer's digest record contained in the APK Signature Scheme v2
1285          * signature.
1286          *
1287          * <ul>
1288          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
1289          * </ul>
1290          */
1291         V2_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v2 digest record #%1$d"),
1292 
1293         /**
1294          * This APK Signature Scheme v2 signer contains a malformed additional attribute.
1295          *
1296          * <ul>
1297          * <li>Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})</li>
1298          * </ul>
1299          */
1300         V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
1301 
1302         /**
1303          * APK Signature Scheme v2 signature references an unknown APK signature scheme ID.
1304          *
1305          * <ul>
1306          * <li>Parameter 1: signer index ({@code Integer})</li>
1307          * <li>Parameter 2: unknown APK signature scheme ID ({@code} Integer)</li>
1308          * </ul>
1309          */
1310         V2_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
1311                 "APK Signature Scheme v2 signer: %1$s references unknown APK signature scheme ID: "
1312                         + "%2$d"),
1313 
1314         /**
1315          * APK Signature Scheme v2 signature indicates that the APK is supposed to be signed with a
1316          * supported APK signature scheme (in addition to the v2 signature) but no such signature
1317          * was found in the APK.
1318          *
1319          * <ul>
1320          * <li>Parameter 1: signer index ({@code Integer})</li>
1321          * <li>Parameter 2: APK signature scheme English name ({@code} String)</li>
1322          * </ul>
1323          */
1324         V2_SIG_MISSING_APK_SIG_REFERENCED(
1325                 "APK Signature Scheme v2 signature %1$s indicates the APK is signed using %2$s but "
1326                         + "no such signature was found. Signature stripped?"),
1327 
1328         /**
1329          * APK Signature Scheme v2 signature contains no signers.
1330          */
1331         V2_SIG_NO_SIGNERS("No signers in APK Signature Scheme v2 signature"),
1332 
1333         /**
1334          * This APK Signature Scheme v2 signer contains a signature produced using an unknown
1335          * algorithm.
1336          *
1337          * <ul>
1338          * <li>Parameter 1: algorithm ID ({@code Integer})</li>
1339          * </ul>
1340          */
1341         V2_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
1342 
1343         /**
1344          * This APK Signature Scheme v2 signer contains an unknown additional attribute.
1345          *
1346          * <ul>
1347          * <li>Parameter 1: attribute ID ({@code Integer})</li>
1348          * </ul>
1349          */
1350         V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
1351 
1352         /**
1353          * An exception was encountered while verifying APK Signature Scheme v2 signature of this
1354          * signer.
1355          *
1356          * <ul>
1357          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
1358          * <li>Parameter 2: exception ({@code Throwable})</li>
1359          * </ul>
1360          */
1361         V2_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
1362 
1363         /**
1364          * APK Signature Scheme v2 signature over this signer's signed-data block did not verify.
1365          *
1366          * <ul>
1367          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
1368          * </ul>
1369          */
1370         V2_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
1371 
1372         /**
1373          * This APK Signature Scheme v2 signer offers no signatures.
1374          */
1375         V2_SIG_NO_SIGNATURES("No signatures"),
1376 
1377         /**
1378          * This APK Signature Scheme v2 signer offers signatures but none of them are supported.
1379          */
1380         V2_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures"),
1381 
1382         /**
1383          * This APK Signature Scheme v2 signer offers no certificates.
1384          */
1385         V2_SIG_NO_CERTIFICATES("No certificates"),
1386 
1387         /**
1388          * This APK Signature Scheme v2 signer's public key listed in the signer's certificate does
1389          * not match the public key listed in the signatures record.
1390          *
1391          * <ul>
1392          * <li>Parameter 1: hex-encoded public key from certificate ({@code String})</li>
1393          * <li>Parameter 2: hex-encoded public key from signatures record ({@code String})</li>
1394          * </ul>
1395          */
1396         V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
1397                 "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
1398 
1399         /**
1400          * This APK Signature Scheme v2 signer's signature algorithms listed in the signatures
1401          * record do not match the signature algorithms listed in the signatures record.
1402          *
1403          * <ul>
1404          * <li>Parameter 1: signature algorithms from signatures record ({@code List<Integer>})</li>
1405          * <li>Parameter 2: signature algorithms from digests record ({@code List<Integer>})</li>
1406          * </ul>
1407          */
1408         V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
1409                 "Signature algorithms mismatch between signatures and digests records"
1410                         + ": %1$s vs %2$s"),
1411 
1412         /**
1413          * The APK's digest does not match the digest contained in the APK Signature Scheme v2
1414          * signature.
1415          *
1416          * <ul>
1417          * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
1418          * <li>Parameter 2: hex-encoded expected digest of the APK ({@code String})</li>
1419          * <li>Parameter 3: hex-encoded actual digest of the APK ({@code String})</li>
1420          * </ul>
1421          */
1422         V2_SIG_APK_DIGEST_DID_NOT_VERIFY(
1423                 "APK integrity check failed. %1$s digest mismatch."
1424                         + " Expected: <%2$s>, actual: <%3$s>"),
1425 
1426         /**
1427          * Failed to parse the list of signers contained in the APK Signature Scheme v3 signature.
1428          */
1429         V3_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
1430 
1431         /**
1432          * Failed to parse this signer's signer block contained in the APK Signature Scheme v3
1433          * signature.
1434          */
1435         V3_SIG_MALFORMED_SIGNER("Malformed signer block"),
1436 
1437         /**
1438          * Public key embedded in the APK Signature Scheme v3 signature of this signer could not be
1439          * parsed.
1440          *
1441          * <ul>
1442          * <li>Parameter 1: error details ({@code Throwable})</li>
1443          * </ul>
1444          */
1445         V3_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
1446 
1447         /**
1448          * This APK Signature Scheme v3 signer's certificate could not be parsed.
1449          *
1450          * <ul>
1451          * <li>Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of
1452          *     certificates ({@code Integer})</li>
1453          * <li>Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's
1454          *     list of certificates ({@code Integer})</li>
1455          * <li>Parameter 3: error details ({@code Throwable})</li>
1456          * </ul>
1457          */
1458         V3_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
1459 
1460         /**
1461          * Failed to parse this signer's signature record contained in the APK Signature Scheme v3
1462          * signature.
1463          *
1464          * <ul>
1465          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
1466          * </ul>
1467          */
1468         V3_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v3 signature record #%1$d"),
1469 
1470         /**
1471          * Failed to parse this signer's digest record contained in the APK Signature Scheme v3
1472          * signature.
1473          *
1474          * <ul>
1475          * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
1476          * </ul>
1477          */
1478         V3_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v3 digest record #%1$d"),
1479 
1480         /**
1481          * This APK Signature Scheme v3 signer contains a malformed additional attribute.
1482          *
1483          * <ul>
1484          * <li>Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})</li>
1485          * </ul>
1486          */
1487         V3_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
1488 
1489         /**
1490          * APK Signature Scheme v3 signature contains no signers.
1491          */
1492         V3_SIG_NO_SIGNERS("No signers in APK Signature Scheme v3 signature"),
1493 
1494         /**
1495          * APK Signature Scheme v3 signature contains multiple signers (only one allowed per
1496          * platform version).
1497          */
1498         V3_SIG_MULTIPLE_SIGNERS("Multiple APK Signature Scheme v3 signatures found for a single "
1499                 + " platform version."),
1500 
1501         /**
1502          * APK Signature Scheme v3 signature found, but multiple v1 and/or multiple v2 signers
1503          * found, where only one may be used with APK Signature Scheme v3
1504          */
1505         V3_SIG_MULTIPLE_PAST_SIGNERS("Multiple signatures found for pre-v3 signing with an APK "
1506                 + " Signature Scheme v3 signer.  Only one allowed."),
1507 
1508         /**
1509          * APK Signature Scheme v3 signature found, but its signer doesn't match the v1/v2 signers,
1510          * or have them as the root of its signing certificate history
1511          */
1512         V3_SIG_PAST_SIGNERS_MISMATCH(
1513                 "v3 signer differs from v1/v2 signer without proper signing certificate lineage."),
1514 
1515         /**
1516          * This APK Signature Scheme v3 signer contains a signature produced using an unknown
1517          * algorithm.
1518          *
1519          * <ul>
1520          * <li>Parameter 1: algorithm ID ({@code Integer})</li>
1521          * </ul>
1522          */
1523         V3_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
1524 
1525         /**
1526          * This APK Signature Scheme v3 signer contains an unknown additional attribute.
1527          *
1528          * <ul>
1529          * <li>Parameter 1: attribute ID ({@code Integer})</li>
1530          * </ul>
1531          */
1532         V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
1533 
1534         /**
1535          * An exception was encountered while verifying APK Signature Scheme v3 signature of this
1536          * signer.
1537          *
1538          * <ul>
1539          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
1540          * <li>Parameter 2: exception ({@code Throwable})</li>
1541          * </ul>
1542          */
1543         V3_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
1544 
1545         /**
1546          * The APK Signature Scheme v3 signer contained an invalid value for either min or max SDK
1547          * versions.
1548          *
1549          * <ul>
1550          * <li>Parameter 1: minSdkVersion ({@code Integer})
1551          * <li>Parameter 2: maxSdkVersion ({@code Integer})
1552          * </ul>
1553          */
1554         V3_SIG_INVALID_SDK_VERSIONS("Invalid SDK Version parameter(s) encountered in APK Signature "
1555                 + "scheme v3 signature: minSdkVersion %1$s maxSdkVersion: %2$s"),
1556 
1557         /**
1558          * APK Signature Scheme v3 signature over this signer's signed-data block did not verify.
1559          *
1560          * <ul>
1561          * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
1562          * </ul>
1563          */
1564         V3_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
1565 
1566         /**
1567          * This APK Signature Scheme v3 signer offers no signatures.
1568          */
1569         V3_SIG_NO_SIGNATURES("No signatures"),
1570 
1571         /**
1572          * This APK Signature Scheme v3 signer offers signatures but none of them are supported.
1573          */
1574         V3_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures"),
1575 
1576         /**
1577          * This APK Signature Scheme v3 signer offers no certificates.
1578          */
1579         V3_SIG_NO_CERTIFICATES("No certificates"),
1580 
1581         /**
1582          * This APK Signature Scheme v3 signer's minSdkVersion listed in the signer's signed data
1583          * does not match the minSdkVersion listed in the signatures record.
1584          *
1585          * <ul>
1586          * <li>Parameter 1: minSdkVersion in signature record ({@code Integer}) </li>
1587          * <li>Parameter 2: minSdkVersion in signed data ({@code Integer}) </li>
1588          * </ul>
1589          */
1590         V3_MIN_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD(
1591                 "minSdkVersion mismatch between signed data and signature record:"
1592                         + " <%1$s> vs <%2$s>"),
1593 
1594         /**
1595          * This APK Signature Scheme v3 signer's maxSdkVersion listed in the signer's signed data
1596          * does not match the maxSdkVersion listed in the signatures record.
1597          *
1598          * <ul>
1599          * <li>Parameter 1: maxSdkVersion in signature record ({@code Integer}) </li>
1600          * <li>Parameter 2: maxSdkVersion in signed data ({@code Integer}) </li>
1601          * </ul>
1602          */
1603         V3_MAX_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD(
1604                 "maxSdkVersion mismatch between signed data and signature record:"
1605                         + " <%1$s> vs <%2$s>"),
1606 
1607         /**
1608          * This APK Signature Scheme v3 signer's public key listed in the signer's certificate does
1609          * not match the public key listed in the signatures record.
1610          *
1611          * <ul>
1612          * <li>Parameter 1: hex-encoded public key from certificate ({@code String})</li>
1613          * <li>Parameter 2: hex-encoded public key from signatures record ({@code String})</li>
1614          * </ul>
1615          */
1616         V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
1617                 "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
1618 
1619         /**
1620          * This APK Signature Scheme v3 signer's signature algorithms listed in the signatures
1621          * record do not match the signature algorithms listed in the signatures record.
1622          *
1623          * <ul>
1624          * <li>Parameter 1: signature algorithms from signatures record ({@code List<Integer>})</li>
1625          * <li>Parameter 2: signature algorithms from digests record ({@code List<Integer>})</li>
1626          * </ul>
1627          */
1628         V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
1629                 "Signature algorithms mismatch between signatures and digests records"
1630                         + ": %1$s vs %2$s"),
1631 
1632         /**
1633          * The APK's digest does not match the digest contained in the APK Signature Scheme v3
1634          * signature.
1635          *
1636          * <ul>
1637          * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
1638          * <li>Parameter 2: hex-encoded expected digest of the APK ({@code String})</li>
1639          * <li>Parameter 3: hex-encoded actual digest of the APK ({@code String})</li>
1640          * </ul>
1641          */
1642         V3_SIG_APK_DIGEST_DID_NOT_VERIFY(
1643                 "APK integrity check failed. %1$s digest mismatch."
1644                         + " Expected: <%2$s>, actual: <%3$s>"),
1645 
1646         /**
1647          * The signer's SigningCertificateLineage attribute containd a proof-of-rotation record with
1648          * signature(s) that did not verify.
1649          */
1650         V3_SIG_POR_DID_NOT_VERIFY("SigningCertificateLineage attribute containd a proof-of-rotation"
1651                 + " record with signature(s) that did not verify."),
1652 
1653         /**
1654          * Failed to parse the SigningCertificateLineage structure in the APK Signature Scheme v3
1655          * signature's additional attributes section.
1656          */
1657         V3_SIG_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage structure in the "
1658                 + "APK Signature Scheme v3 signature's additional attributes section."),
1659 
1660         /**
1661          * The APK's signing certificate does not match the terminal node in the provided
1662          * proof-of-rotation structure describing the signing certificate history
1663          */
1664         V3_SIG_POR_CERT_MISMATCH(
1665                 "APK signing certificate differs from the associated certificate found in the "
1666                         + "signer's SigningCertificateLineage."),
1667 
1668         /**
1669          * The APK Signature Scheme v3 signers encountered do not offer a continuous set of
1670          * supported platform versions.  Either they overlap, resulting in potentially two
1671          * acceptable signers for a platform version, or there are holes which would create problems
1672          * in the event of platform version upgrades.
1673          */
1674         V3_INCONSISTENT_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK "
1675                 + "versions are not continuous."),
1676 
1677         /**
1678          * The APK Signature Scheme v3 signers don't cover all requested SDK versions.
1679          *
1680          *  <ul>
1681          * <li>Parameter 1: minSdkVersion ({@code Integer})
1682          * <li>Parameter 2: maxSdkVersion ({@code Integer})
1683          * </ul>
1684          */
1685         V3_MISSING_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK "
1686                 + "versions do not cover the entire desired range.  Found min:  %1$s max %2$s"),
1687 
1688         /**
1689          * The SigningCertificateLineages for different platform versions using APK Signature Scheme
1690          * v3 do not go together.  Specifically, each should be a subset of another, with the size
1691          * of each increasing as the platform level increases.
1692          */
1693         V3_INCONSISTENT_LINEAGES("SigningCertificateLineages targeting different platform versions"
1694                 + " using APK Signature Scheme v3 are not all a part of the same overall lineage."),
1695 
1696         /**
1697          * APK Signing Block contains an unknown entry.
1698          *
1699          * <ul>
1700          * <li>Parameter 1: entry ID ({@code Integer})</li>
1701          * </ul>
1702          */
1703         APK_SIG_BLOCK_UNKNOWN_ENTRY_ID("APK Signing Block contains unknown entry: ID %1$#x");
1704 
1705         private final String mFormat;
1706 
Issue(String format)1707         private Issue(String format) {
1708             mFormat = format;
1709         }
1710 
1711         /**
1712          * Returns the format string suitable for combining the parameters of this issue into a
1713          * readable string. See {@link java.util.Formatter} for format.
1714          */
getFormat()1715         private String getFormat() {
1716             return mFormat;
1717         }
1718     }
1719 
1720     /**
1721      * {@link Issue} with associated parameters. {@link #toString()} produces a readable formatted
1722      * form.
1723      */
1724     public static class IssueWithParams {
1725         private final Issue mIssue;
1726         private final Object[] mParams;
1727 
1728         /**
1729          * Constructs a new {@code IssueWithParams} of the specified type and with provided
1730          * parameters.
1731          */
IssueWithParams(Issue issue, Object[] params)1732         public IssueWithParams(Issue issue, Object[] params) {
1733             mIssue = issue;
1734             mParams = params;
1735         }
1736 
1737         /**
1738          * Returns the type of this issue.
1739          */
getIssue()1740         public Issue getIssue() {
1741             return mIssue;
1742         }
1743 
1744         /**
1745          * Returns the parameters of this issue.
1746          */
getParams()1747         public Object[] getParams() {
1748             return mParams.clone();
1749         }
1750 
1751         /**
1752          * Returns a readable form of this issue.
1753          */
1754         @Override
toString()1755         public String toString() {
1756             return String.format(mIssue.getFormat(), mParams);
1757         }
1758     }
1759 
1760     /**
1761      * Wrapped around {@code byte[]} which ensures that {@code equals} and {@code hashCode} operate
1762      * on the contents of the arrays rather than on references.
1763      */
1764     private static class ByteArray {
1765         private final byte[] mArray;
1766         private final int mHashCode;
1767 
ByteArray(byte[] arr)1768         private ByteArray(byte[] arr) {
1769             mArray = arr;
1770             mHashCode = Arrays.hashCode(mArray);
1771         }
1772 
1773         @Override
hashCode()1774         public int hashCode() {
1775             return mHashCode;
1776         }
1777 
1778         @Override
equals(Object obj)1779         public boolean equals(Object obj) {
1780             if (this == obj) {
1781                 return true;
1782             }
1783             if (obj == null) {
1784                 return false;
1785             }
1786             if (getClass() != obj.getClass()) {
1787                 return false;
1788             }
1789             ByteArray other = (ByteArray) obj;
1790             if (hashCode() != other.hashCode()) {
1791                 return false;
1792             }
1793             if (!Arrays.equals(mArray, other.mArray)) {
1794                 return false;
1795             }
1796             return true;
1797         }
1798     }
1799 
1800     /**
1801      * Builder of {@link ApkVerifier} instances.
1802      *
1803      * <p>The resulting verifier by default checks whether the APK will verify on all platform
1804      * versions supported by the APK, as specified by {@code android:minSdkVersion} attributes in
1805      * the APK's {@code AndroidManifest.xml}. The range of platform versions can be customized using
1806      * {@link #setMinCheckedPlatformVersion(int)} and {@link #setMaxCheckedPlatformVersion(int)}.
1807      */
1808     public static class Builder {
1809         private final File mApkFile;
1810         private final DataSource mApkDataSource;
1811 
1812         private Integer mMinSdkVersion;
1813         private int mMaxSdkVersion = Integer.MAX_VALUE;
1814 
1815         /**
1816          * Constructs a new {@code Builder} for verifying the provided APK file.
1817          */
Builder(File apk)1818         public Builder(File apk) {
1819             if (apk == null) {
1820                 throw new NullPointerException("apk == null");
1821             }
1822             mApkFile = apk;
1823             mApkDataSource = null;
1824         }
1825 
1826         /**
1827          * Constructs a new {@code Builder} for verifying the provided APK.
1828          */
Builder(DataSource apk)1829         public Builder(DataSource apk) {
1830             if (apk == null) {
1831                 throw new NullPointerException("apk == null");
1832             }
1833             mApkDataSource = apk;
1834             mApkFile = null;
1835         }
1836 
1837         /**
1838          * Sets the oldest Android platform version for which the APK is verified. APK verification
1839          * will confirm that the APK is expected to install successfully on all known Android
1840          * platforms starting from the platform version with the provided API Level. The upper end
1841          * of the platform versions range can be modified via
1842          * {@link #setMaxCheckedPlatformVersion(int)}.
1843          *
1844          * <p>This method is useful for overriding the default behavior which checks that the APK
1845          * will verify on all platform versions supported by the APK, as specified by
1846          * {@code android:minSdkVersion} attributes in the APK's {@code AndroidManifest.xml}.
1847          *
1848          * @param minSdkVersion API Level of the oldest platform for which to verify the APK
1849          *
1850          * @see #setMinCheckedPlatformVersion(int)
1851          */
setMinCheckedPlatformVersion(int minSdkVersion)1852         public Builder setMinCheckedPlatformVersion(int minSdkVersion) {
1853             mMinSdkVersion = minSdkVersion;
1854             return this;
1855         }
1856 
1857         /**
1858          * Sets the newest Android platform version for which the APK is verified. APK verification
1859          * will confirm that the APK is expected to install successfully on all platform versions
1860          * supported by the APK up until and including the provided version. The lower end
1861          * of the platform versions range can be modified via
1862          * {@link #setMinCheckedPlatformVersion(int)}.
1863          *
1864          * @param maxSdkVersion API Level of the newest platform for which to verify the APK
1865          *
1866          * @see #setMinCheckedPlatformVersion(int)
1867          */
setMaxCheckedPlatformVersion(int maxSdkVersion)1868         public Builder setMaxCheckedPlatformVersion(int maxSdkVersion) {
1869             mMaxSdkVersion = maxSdkVersion;
1870             return this;
1871         }
1872 
1873         /**
1874          * Returns an {@link ApkVerifier} initialized according to the configuration of this
1875          * builder.
1876          */
build()1877         public ApkVerifier build() {
1878             return new ApkVerifier(
1879                     mApkFile,
1880                     mApkDataSource,
1881                     mMinSdkVersion,
1882                     mMaxSdkVersion);
1883         }
1884     }
1885 }
1886