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