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