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