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