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