Home | History | Annotate | Download | only in apksig
      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 
     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 
     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      */
    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      */
    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 
    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      */
    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          */
    503         public boolean isVerified() {
    504             return mVerified;
    505         }
    506 
    507         private void setVerified() {
    508             mVerified = true;
    509         }
    510 
    511         /**
    512          * Returns {@code true} if the APK's JAR signatures verified.
    513          */
    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          */
    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          */
    528         public boolean isVerifiedUsingV3Scheme() {
    529             return mVerifiedUsingV3Scheme;
    530         }
    531 
    532         /**
    533          * Returns the verified signers' certificates, one per signer.
    534          */
    535         public List<X509Certificate> getSignerCertificates() {
    536             return mSignerCerts;
    537         }
    538 
    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          */
    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          */
    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          */
    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          */
    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          */
    588         public SigningCertificateLineage getSigningCertificateLineage() {
    589             return mSigningCertificateLineage;
    590         }
    591 
    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          */
    599         public List<IssueWithParams> getErrors() {
    600             return mErrors;
    601         }
    602 
    603         /**
    604          * Returns warnings encountered while verifying the APK's signatures.
    605          */
    606         public List<IssueWithParams> getWarnings() {
    607             return mWarnings;
    608         }
    609 
    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 
    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          */
    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 
    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              */
    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              */
    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              */
    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              */
    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              */
    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              */
    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              */
    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              */
    760             public List<IssueWithParams> getWarnings() {
    761                 return mWarnings;
    762             }
    763 
    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 
    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              */
    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              */
    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              */
    810             public List<X509Certificate> getCertificates() {
    811                 return mCerts;
    812             }
    813 
    814             private void addError(Issue msg, Object... parameters) {
    815                 mErrors.add(new IssueWithParams(msg, parameters));
    816             }
    817 
    818             public boolean containsErrors() {
    819                 return !mErrors.isEmpty();
    820             }
    821 
    822             public List<IssueWithParams> getErrors() {
    823                 return mErrors;
    824             }
    825 
    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 
    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              */
    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              */
    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              */
    872             public List<X509Certificate> getCertificates() {
    873                 return mCerts;
    874             }
    875 
    876             public boolean containsErrors() {
    877                 return !mErrors.isEmpty();
    878             }
    879 
    880             public List<IssueWithParams> getErrors() {
    881                 return mErrors;
    882             }
    883 
    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 
   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          */
   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          */
   1732         public IssueWithParams(Issue issue, Object[] params) {
   1733             mIssue = issue;
   1734             mParams = params;
   1735         }
   1736 
   1737         /**
   1738          * Returns the type of this issue.
   1739          */
   1740         public Issue getIssue() {
   1741             return mIssue;
   1742         }
   1743 
   1744         /**
   1745          * Returns the parameters of this issue.
   1746          */
   1747         public Object[] getParams() {
   1748             return mParams.clone();
   1749         }
   1750 
   1751         /**
   1752          * Returns a readable form of this issue.
   1753          */
   1754         @Override
   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 
   1768         private ByteArray(byte[] arr) {
   1769             mArray = arr;
   1770             mHashCode = Arrays.hashCode(mArray);
   1771         }
   1772 
   1773         @Override
   1774         public int hashCode() {
   1775             return mHashCode;
   1776         }
   1777 
   1778         @Override
   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          */
   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          */
   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          */
   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          */
   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          */
   1877         public ApkVerifier build() {
   1878             return new ApkVerifier(
   1879                     mApkFile,
   1880                     mApkDataSource,
   1881                     mMinSdkVersion,
   1882                     mMaxSdkVersion);
   1883         }
   1884     }
   1885 }
   1886