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