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