/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; import static com.android.apksig.apk.ApkUtils.computeSha256DigestBytes; import static com.android.apksig.apk.ApkUtils.getTargetSandboxVersionFromBinaryAndroidManifest; import static com.android.apksig.apk.ApkUtils.getTargetSdkVersionFromBinaryAndroidManifest; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME; import static com.android.apksig.internal.apk.v1.V1SchemeConstants.MANIFEST_ENTRY_NAME; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigResult; import com.android.apksig.internal.apk.ApkSignerInfo; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.SignatureNotFoundException; import com.android.apksig.internal.apk.stamp.SourceStampConstants; import com.android.apksig.internal.apk.stamp.V2SourceStampVerifier; import com.android.apksig.internal.apk.v1.V1SchemeVerifier; import com.android.apksig.internal.apk.v2.V2SchemeConstants; import com.android.apksig.internal.apk.v2.V2SchemeVerifier; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeVerifier; import com.android.apksig.internal.apk.v4.V4SchemeVerifier; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.util.RunnablesExecutor; import com.android.apksig.zip.ZipFormatException; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * APK signature verifier which mimics the behavior of the Android platform. * *
The verifier is designed to closely mimic the behavior of Android platforms. This is to enable * the verifier to be used for checking whether an APK's signatures are expected to verify on * Android. * *
Use {@link Builder} to obtain instances of this verifier.
*
* @see Application Signing
*/
public class ApkVerifier {
private static final Map Verification succeeds iff the APK's signature is expected to verify on all Android
* platform versions specified via the {@link Builder}. If the APK's signature is expected to
* not verify on any of the specified platform versions, this method returns a result with one
* or more errors and whose {@link Result#isVerified()} returns {@code false}, or this method
* throws an exception.
*
* @throws IOException if an I/O error is encountered while reading the APK
* @throws ApkFormatException if the APK is malformed
* @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
* required cryptographic algorithm implementation is missing
* @throws IllegalStateException if this verifier's configuration is missing required
* information.
*/
public Result verify() throws IOException, ApkFormatException, NoSuchAlgorithmException,
IllegalStateException {
Closeable in = null;
try {
DataSource apk;
if (mApkDataSource != null) {
apk = mApkDataSource;
} else if (mApkFile != null) {
RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
in = f;
apk = DataSources.asDataSource(f, 0, f.length());
} else {
throw new IllegalStateException("APK not provided");
}
return verify(apk);
} finally {
if (in != null) {
in.close();
}
}
}
/**
* Simply retrieve result that contains V2Signature only by ignoring all the verification process.
*
* @throws IOException if an I/O error is encountered while reading the APK
* @throws ApkFormatException if the APK is malformed
* @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
* required cryptographic algorithm implementation is missing
* @throws IllegalStateException if this verifier's configuration is missing required
* information.
*/
public Result retrieveV2Signature() throws IOException, ApkFormatException, NoSuchAlgorithmException {
Closeable in = null;
try {
DataSource apk;
if (mApkDataSource != null) {
apk = mApkDataSource;
} else if (mApkFile != null) {
RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
in = f;
apk = DataSources.asDataSource(f, 0, f.length());
} else {
throw new IllegalStateException("APK not provided");
}
return retrieveV2Signature(apk);
} finally {
if (in != null) {
in.close();
}
}
}
/**
* Simply retrieve result that contains V2Signature only by ignoring all the verification process.
*
* @param apk APK file contents
* @throws IOException if an I/O error is encountered while reading the APK
* @throws ApkFormatException if the APK is malformed
* @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
* required cryptographic algorithm implementation is missing
*/
private Result retrieveV2Signature(DataSource apk) throws IOException, ApkFormatException, NoSuchAlgorithmException {
int maxSdkVersion = mMaxSdkVersion;
ApkUtils.ZipSections zipSections;
try {
zipSections = ApkUtils.findZipSections(apk);
} catch (ZipFormatException e) {
throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
}
int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections);
Result result = new Result();
Map The APK's source stamp can be considered verified if the result's {@link
* Result#isVerified} returns {@code true}. The details of the source stamp verification can
* be obtained from the result's {@link Result#getSourceStampInfo()}} including the success or
* failure cause from {@link Result.SourceStampInfo#getSourceStampVerificationStatus()}. If the
* verification fails additional details regarding the failure can be obtained from {@link
* Result#getAllErrors()}}.
*/
public Result verifySourceStamp() {
return verifySourceStamp(null);
}
/**
* Verifies the APK's source stamp signature, including verification that the SHA-256 digest of
* the stamp signing certificate matches the {@code expectedCertDigest}, and returns the result
* of the verification.
*
* A value of {@code null} for the {@code expectedCertDigest} will verify the source stamp,
* if present, without verifying the actual source stamp certificate used to sign the source
* stamp. This can be used to verify an APK contains a properly signed source stamp without
* verifying a particular signer.
*
* @see #verifySourceStamp()
*/
public Result verifySourceStamp(String expectedCertDigest) {
Closeable in = null;
try {
DataSource apk;
if (mApkDataSource != null) {
apk = mApkDataSource;
} else if (mApkFile != null) {
RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
in = f;
apk = DataSources.asDataSource(f, 0, f.length());
} else {
throw new IllegalStateException("APK not provided");
}
return verifySourceStamp(apk, expectedCertDigest);
} catch (IOException e) {
return createSourceStampResultWithError(
Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR,
Issue.UNEXPECTED_EXCEPTION, e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignored) {
}
}
}
}
/**
* Verifies the provided {@code apk}'s source stamp signature, including verification of the
* SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and
* returns the result of the verification.
*
* @see #verifySourceStamp(String)
*/
private Result verifySourceStamp(DataSource apk, String expectedCertDigest) {
try {
ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections);
// Attempt to obtain the source stamp's certificate digest from the APK.
List Note, this method currently only supports signature scheme V2 and V3; to obtain the
* content digests for V1 signatures use {@link
* #getApkContentDigestFromV1SigningScheme(List, DataSource, ApkUtils.ZipSections)}. If a
* signature scheme version other than V2 or V3 is provided a {@code null} value will be
* returned.
*/
private ApkSigningBlockUtils.Result getApkContentDigests(DataSource apk,
ApkUtils.ZipSections zipSections, Set This certificate contains the signer's public key.
*/
public X509Certificate getCertificate() {
return mCertChain.isEmpty() ? null : mCertChain.get(0);
}
/**
* Returns the certificate chain for the signer's public key. The certificate containing
* the public key is first, followed by the certificate (if any) which issued the
* signing certificate, and so forth. An empty list may be returned if an error was
* encountered during verification (see {@link #containsErrors()}).
*/
public List This certificate contains the signer's public key.
*/
public X509Certificate getCertificate() {
return mCerts.isEmpty() ? null : mCerts.get(0);
}
/**
* Returns this signer's certificates. The first certificate is for the signer's public
* key. An empty list may be returned if an error was encountered during verification
* (see {@link #containsErrors()}).
*/
public List This certificate contains the signer's public key.
*/
public X509Certificate getCertificate() {
return mCerts.isEmpty() ? null : mCerts.get(0);
}
/**
* Returns this signer's certificates. The first certificate is for the signer's public
* key. An empty list may be returned if an error was encountered during verification
* (see {@link #containsErrors()}).
*/
public List This certificate contains the signer's public key.
*/
public X509Certificate getCertificate() {
return mCerts.isEmpty() ? null : mCerts.get(0);
}
/**
* Returns this signer's certificates. The first certificate is for the signer's public
* key. An empty list may be returned if an error was encountered during verification
* (see {@link #containsErrors()}).
*/
public List This certificate contains the SourceStamp's public key.
*/
public X509Certificate getCertificate() {
return mCertificates.isEmpty() ? null : mCertificates.get(0);
}
/**
* Returns a list containing all of the certificates in the stamp certificate lineage.
*/
public List The resulting verifier by default checks whether the APK will verify on all platform
* versions supported by the APK, as specified by {@code android:minSdkVersion} attributes in
* the APK's {@code AndroidManifest.xml}. The range of platform versions can be customized using
* {@link #setMinCheckedPlatformVersion(int)} and {@link #setMaxCheckedPlatformVersion(int)}.
*/
public static class Builder {
private final File mApkFile;
private final DataSource mApkDataSource;
private File mV4SignatureFile;
private Integer mMinSdkVersion;
private int mMaxSdkVersion = Integer.MAX_VALUE;
/**
* Constructs a new {@code Builder} for verifying the provided APK file.
*/
public Builder(File apk) {
if (apk == null) {
throw new NullPointerException("apk == null");
}
mApkFile = apk;
mApkDataSource = null;
}
/**
* Constructs a new {@code Builder} for verifying the provided APK.
*/
public Builder(DataSource apk) {
if (apk == null) {
throw new NullPointerException("apk == null");
}
mApkDataSource = apk;
mApkFile = null;
}
/**
* Sets the oldest Android platform version for which the APK is verified. APK verification
* will confirm that the APK is expected to install successfully on all known Android
* platforms starting from the platform version with the provided API Level. The upper end
* of the platform versions range can be modified via
* {@link #setMaxCheckedPlatformVersion(int)}.
*
* This method is useful for overriding the default behavior which checks that the APK
* will verify on all platform versions supported by the APK, as specified by
* {@code android:minSdkVersion} attributes in the APK's {@code AndroidManifest.xml}.
*
* @param minSdkVersion API Level of the oldest platform for which to verify the APK
* @see #setMinCheckedPlatformVersion(int)
*/
public Builder setMinCheckedPlatformVersion(int minSdkVersion) {
mMinSdkVersion = minSdkVersion;
return this;
}
/**
* Sets the newest Android platform version for which the APK is verified. APK verification
* will confirm that the APK is expected to install successfully on all platform versions
* supported by the APK up until and including the provided version. The lower end
* of the platform versions range can be modified via
* {@link #setMinCheckedPlatformVersion(int)}.
*
* @param maxSdkVersion API Level of the newest platform for which to verify the APK
* @see #setMinCheckedPlatformVersion(int)
*/
public Builder setMaxCheckedPlatformVersion(int maxSdkVersion) {
mMaxSdkVersion = maxSdkVersion;
return this;
}
public Builder setV4SignatureFile(File v4SignatureFile) {
mV4SignatureFile = v4SignatureFile;
return this;
}
/**
* Returns an {@link ApkVerifier} initialized according to the configuration of this
* builder.
*/
public ApkVerifier build() {
return new ApkVerifier(
mApkFile,
mApkDataSource,
mV4SignatureFile,
mMinSdkVersion,
mMaxSdkVersion);
}
}
/**
* Adapter for converting base {@link ApkVerificationIssue} instances to their {@link
* IssueWithParams} equivalent.
*/
public static class ApkVerificationIssueAdapter {
private ApkVerificationIssueAdapter() {
}
// This field is visible for testing
static final Map
*
*/
JAR_SIG_DUPLICATE_ZIP_ENTRY("Duplicate entry: %1$s"),
/**
* JAR manifest contains a section with a duplicate name.
*
*
*
*/
JAR_SIG_DUPLICATE_MANIFEST_SECTION("Duplicate section in META-INF/MANIFEST.MF: %1$s"),
/**
* JAR manifest contains a section without a name.
*
*
*
*/
JAR_SIG_UNNNAMED_MANIFEST_SECTION(
"Malformed META-INF/MANIFEST.MF: invidual section #%1$d does not have a name"),
/**
* JAR signature file contains a section without a name.
*
*
*
*/
JAR_SIG_UNNNAMED_SIG_FILE_SECTION(
"Malformed %1$s: invidual section #%2$d does not have a name"),
/** APK is missing the JAR manifest entry (META-INF/MANIFEST.MF). */
JAR_SIG_NO_MANIFEST("Missing META-INF/MANIFEST.MF"),
/**
* JAR manifest references an entry which is not there in the APK.
*
*
*
*/
JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST(
"%1$s entry referenced by META-INF/MANIFEST.MF not found in the APK"),
/**
* JAR manifest does not list a digest for the specified entry.
*
*
*
*/
JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST("No digest for %1$s in META-INF/MANIFEST.MF"),
/**
* JAR signature does not list a digest for the specified entry.
*
*
*
*/
JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE("No digest for %1$s in %2$s"),
/**
* The specified JAR entry is not covered by JAR signature.
*
*
*
*/
JAR_SIG_ZIP_ENTRY_NOT_SIGNED("%1$s entry not signed"),
/**
* JAR signature uses different set of signers to protect the two specified ZIP entries.
*
*
*
*/
JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH(
"Entries %1$s and %3$s are signed with different sets of signers"
+ " : <%2$s> vs <%4$s>"),
/**
* Digest of the specified ZIP entry's data does not match the digest expected by the JAR
* signature.
*
*
*
*/
JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY(
"%2$s digest of %1$s does not match the digest specified in %3$s"
+ ". Expected: <%5$s>, actual: <%4$s>"),
/**
* Digest of the JAR manifest main section did not verify.
*
*
*
*/
JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY(
"%1$s digest of META-INF/MANIFEST.MF main section does not match the digest"
+ " specified in %2$s. Expected: <%4$s>, actual: <%3$s>"),
/**
* Digest of the specified JAR manifest section does not match the digest expected by the
* JAR signature.
*
*
*
*/
JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY(
"%2$s digest of META-INF/MANIFEST.MF section for %1$s does not match the digest"
+ " specified in %3$s. Expected: <%5$s>, actual: <%4$s>"),
/**
* JAR signature file does not contain the whole-file digest of the JAR manifest file. The
* digest speeds up verification of JAR signature.
*
*
*
*/
JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE(
"%1$s does not specify digest of META-INF/MANIFEST.MF"
+ ". This slows down verification."),
/**
* APK is signed using APK Signature Scheme v2 or newer, but JAR signature file does not
* contain protections against stripping of these newer scheme signatures.
*
*
*
*/
JAR_SIG_NO_APK_SIG_STRIP_PROTECTION(
"APK is signed using APK Signature Scheme v2 but these signatures may be stripped"
+ " without being detected because %1$s does not contain anti-stripping"
+ " protections."),
/**
* JAR signature of the signer is missing a file/entry.
*
*
*
*/
JAR_SIG_MISSING_FILE("Partial JAR signature. Found: %1$s, missing: %2$s"),
/**
* An exception was encountered while verifying JAR signature contained in a signature block
* against the signature file.
*
*
*
*/
JAR_SIG_VERIFY_EXCEPTION("Failed to verify JAR signature %1$s against %2$s: %3$s"),
/**
* JAR signature contains unsupported digest algorithm.
*
*
*
*/
JAR_SIG_UNSUPPORTED_SIG_ALG(
"JAR signature %1$s uses digest algorithm %5$s and signature algorithm %6$s which"
+ " is not supported on API Level(s) %4$s for which this APK is being"
+ " verified"),
/**
* An exception was encountered while parsing JAR signature contained in a signature block.
*
*
*
*/
JAR_SIG_PARSE_EXCEPTION("Failed to parse JAR signature %1$s: %2$s"),
/**
* An exception was encountered while parsing a certificate contained in the JAR signature
* block.
*
*
*
*/
JAR_SIG_MALFORMED_CERTIFICATE("Malformed certificate in JAR signature %1$s: %2$s"),
/**
* JAR signature contained in a signature block file did not verify against the signature
* file.
*
*
*
*/
JAR_SIG_DID_NOT_VERIFY("JAR signature %1$s did not verify against %2$s"),
/**
* JAR signature contains no verified signers.
*
*
*
*/
JAR_SIG_NO_SIGNERS("JAR signature %1$s contains no signers"),
/**
* JAR signature file contains a section with a duplicate name.
*
*
*
*/
JAR_SIG_DUPLICATE_SIG_FILE_SECTION("Duplicate section in %1$s: %2$s"),
/**
* JAR signature file's main section doesn't contain the mandatory Signature-Version
* attribute.
*
*
*
*/
JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE(
"Malformed %1$s: missing Signature-Version attribute"),
/**
* JAR signature file references an unknown APK signature scheme ID.
*
*
*
*/
JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
"JAR signature %1$s references unknown APK signature scheme ID: %2$d"),
/**
* JAR signature file indicates that the APK is supposed to be signed with a supported APK
* signature scheme (in addition to the JAR signature) but no such signature was found in
* the APK.
*
*
*
*/
JAR_SIG_MISSING_APK_SIG_REFERENCED(
"JAR signature %1$s indicates the APK is signed using %3$s but no such signature"
+ " was found. Signature stripped?"),
/**
* JAR entry is not covered by signature and thus unauthorized modifications to its contents
* will not be detected.
*
*
*
*/
JAR_SIG_UNPROTECTED_ZIP_ENTRY(
"%1$s not protected by signature. Unauthorized modifications to this JAR entry"
+ " will not be detected. Delete or move the entry outside of META-INF/."),
/**
* APK which is both JAR-signed and signed using APK Signature Scheme v2 contains an APK
* Signature Scheme v2 signature from this signer, but does not contain a JAR signature
* from this signer.
*/
JAR_SIG_MISSING("No JAR signature from this signer"),
/**
* APK is targeting a sandbox version which requires APK Signature Scheme v2 signature but
* no such signature was found.
*
*
*
*/
NO_SIG_FOR_TARGET_SANDBOX_VERSION(
"Missing APK Signature Scheme v2 signature required for target sandbox version"
+ " %1$d"),
/**
* APK is targeting an SDK version that requires a minimum signature scheme version, but the
* APK is not signed with that version or later.
*
*
*
*/
MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET(
"Target SDK version %1$d requires a minimum of signature scheme v%2$d; the APK is"
+ " not signed with this or a later signature scheme"),
/**
* APK which is both JAR-signed and signed using APK Signature Scheme v2 contains a JAR
* signature from this signer, but does not contain an APK Signature Scheme v2 signature
* from this signer.
*/
V2_SIG_MISSING("No APK Signature Scheme v2 signature from this signer"),
/**
* Failed to parse the list of signers contained in the APK Signature Scheme v2 signature.
*/
V2_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
/**
* Failed to parse this signer's signer block contained in the APK Signature Scheme v2
* signature.
*/
V2_SIG_MALFORMED_SIGNER("Malformed signer block"),
/**
* Public key embedded in the APK Signature Scheme v2 signature of this signer could not be
* parsed.
*
*
*
*/
V2_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
/**
* This APK Signature Scheme v2 signer's certificate could not be parsed.
*
*
*
*/
V2_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
/**
* Failed to parse this signer's signature record contained in the APK Signature Scheme v2
* signature.
*
*
*
*/
V2_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v2 signature record #%1$d"),
/**
* Failed to parse this signer's digest record contained in the APK Signature Scheme v2
* signature.
*
*
*
*/
V2_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v2 digest record #%1$d"),
/**
* This APK Signature Scheme v2 signer contains a malformed additional attribute.
*
*
*
*/
V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
/**
* APK Signature Scheme v2 signature references an unknown APK signature scheme ID.
*
*
*
*/
V2_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
"APK Signature Scheme v2 signer: %1$s references unknown APK signature scheme ID: "
+ "%2$d"),
/**
* APK Signature Scheme v2 signature indicates that the APK is supposed to be signed with a
* supported APK signature scheme (in addition to the v2 signature) but no such signature
* was found in the APK.
*
*
*
*/
V2_SIG_MISSING_APK_SIG_REFERENCED(
"APK Signature Scheme v2 signature %1$s indicates the APK is signed using %2$s but "
+ "no such signature was found. Signature stripped?"),
/**
* APK Signature Scheme v2 signature contains no signers.
*/
V2_SIG_NO_SIGNERS("No signers in APK Signature Scheme v2 signature"),
/**
* This APK Signature Scheme v2 signer contains a signature produced using an unknown
* algorithm.
*
*
*
*/
V2_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
/**
* This APK Signature Scheme v2 signer contains an unknown additional attribute.
*
*
*
*/
V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
/**
* An exception was encountered while verifying APK Signature Scheme v2 signature of this
* signer.
*
*
*
*/
V2_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
/**
* APK Signature Scheme v2 signature over this signer's signed-data block did not verify.
*
*
*
*/
V2_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
/**
* This APK Signature Scheme v2 signer offers no signatures.
*/
V2_SIG_NO_SIGNATURES("No signatures"),
/**
* This APK Signature Scheme v2 signer offers signatures but none of them are supported.
*/
V2_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures: %1$s"),
/**
* This APK Signature Scheme v2 signer offers no certificates.
*/
V2_SIG_NO_CERTIFICATES("No certificates"),
/**
* This APK Signature Scheme v2 signer's public key listed in the signer's certificate does
* not match the public key listed in the signatures record.
*
*
*
*/
V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
"Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
/**
* This APK Signature Scheme v2 signer's signature algorithms listed in the signatures
* record do not match the signature algorithms listed in the signatures record.
*
*
*
*/
V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
"Signature algorithms mismatch between signatures and digests records"
+ ": %1$s vs %2$s"),
/**
* The APK's digest does not match the digest contained in the APK Signature Scheme v2
* signature.
*
*
*
*/
V2_SIG_APK_DIGEST_DID_NOT_VERIFY(
"APK integrity check failed. %1$s digest mismatch."
+ " Expected: <%2$s>, actual: <%3$s>"),
/**
* Failed to parse the list of signers contained in the APK Signature Scheme v3 signature.
*/
V3_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
/**
* Failed to parse this signer's signer block contained in the APK Signature Scheme v3
* signature.
*/
V3_SIG_MALFORMED_SIGNER("Malformed signer block"),
/**
* Public key embedded in the APK Signature Scheme v3 signature of this signer could not be
* parsed.
*
*
*
*/
V3_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
/**
* This APK Signature Scheme v3 signer's certificate could not be parsed.
*
*
*
*/
V3_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
/**
* Failed to parse this signer's signature record contained in the APK Signature Scheme v3
* signature.
*
*
*
*/
V3_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v3 signature record #%1$d"),
/**
* Failed to parse this signer's digest record contained in the APK Signature Scheme v3
* signature.
*
*
*
*/
V3_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v3 digest record #%1$d"),
/**
* This APK Signature Scheme v3 signer contains a malformed additional attribute.
*
*
*
*/
V3_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
/**
* APK Signature Scheme v3 signature contains no signers.
*/
V3_SIG_NO_SIGNERS("No signers in APK Signature Scheme v3 signature"),
/**
* APK Signature Scheme v3 signature contains multiple signers (only one allowed per
* platform version).
*/
V3_SIG_MULTIPLE_SIGNERS("Multiple APK Signature Scheme v3 signatures found for a single "
+ " platform version."),
/**
* APK Signature Scheme v3 signature found, but multiple v1 and/or multiple v2 signers
* found, where only one may be used with APK Signature Scheme v3
*/
V3_SIG_MULTIPLE_PAST_SIGNERS("Multiple signatures found for pre-v3 signing with an APK "
+ " Signature Scheme v3 signer. Only one allowed."),
/**
* APK Signature Scheme v3 signature found, but its signer doesn't match the v1/v2 signers,
* or have them as the root of its signing certificate history
*/
V3_SIG_PAST_SIGNERS_MISMATCH(
"v3 signer differs from v1/v2 signer without proper signing certificate lineage."),
/**
* This APK Signature Scheme v3 signer contains a signature produced using an unknown
* algorithm.
*
*
*
*/
V3_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
/**
* This APK Signature Scheme v3 signer contains an unknown additional attribute.
*
*
*
*/
V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
/**
* An exception was encountered while verifying APK Signature Scheme v3 signature of this
* signer.
*
*
*
*/
V3_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
/**
* The APK Signature Scheme v3 signer contained an invalid value for either min or max SDK
* versions.
*
*
*
*/
V3_SIG_INVALID_SDK_VERSIONS("Invalid SDK Version parameter(s) encountered in APK Signature "
+ "scheme v3 signature: minSdkVersion %1$s maxSdkVersion: %2$s"),
/**
* APK Signature Scheme v3 signature over this signer's signed-data block did not verify.
*
*
*
*/
V3_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
/**
* This APK Signature Scheme v3 signer offers no signatures.
*/
V3_SIG_NO_SIGNATURES("No signatures"),
/**
* This APK Signature Scheme v3 signer offers signatures but none of them are supported.
*/
V3_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures"),
/**
* This APK Signature Scheme v3 signer offers no certificates.
*/
V3_SIG_NO_CERTIFICATES("No certificates"),
/**
* This APK Signature Scheme v3 signer's minSdkVersion listed in the signer's signed data
* does not match the minSdkVersion listed in the signatures record.
*
*
*
*/
V3_MIN_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD(
"minSdkVersion mismatch between signed data and signature record:"
+ " <%1$s> vs <%2$s>"),
/**
* This APK Signature Scheme v3 signer's maxSdkVersion listed in the signer's signed data
* does not match the maxSdkVersion listed in the signatures record.
*
*
*
*/
V3_MAX_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD(
"maxSdkVersion mismatch between signed data and signature record:"
+ " <%1$s> vs <%2$s>"),
/**
* This APK Signature Scheme v3 signer's public key listed in the signer's certificate does
* not match the public key listed in the signatures record.
*
*
*
*/
V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
"Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
/**
* This APK Signature Scheme v3 signer's signature algorithms listed in the signatures
* record do not match the signature algorithms listed in the signatures record.
*
*
*
*/
V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
"Signature algorithms mismatch between signatures and digests records"
+ ": %1$s vs %2$s"),
/**
* The APK's digest does not match the digest contained in the APK Signature Scheme v3
* signature.
*
*
*
*/
V3_SIG_APK_DIGEST_DID_NOT_VERIFY(
"APK integrity check failed. %1$s digest mismatch."
+ " Expected: <%2$s>, actual: <%3$s>"),
/**
* The signer's SigningCertificateLineage attribute containd a proof-of-rotation record with
* signature(s) that did not verify.
*/
V3_SIG_POR_DID_NOT_VERIFY("SigningCertificateLineage attribute containd a proof-of-rotation"
+ " record with signature(s) that did not verify."),
/**
* Failed to parse the SigningCertificateLineage structure in the APK Signature Scheme v3
* signature's additional attributes section.
*/
V3_SIG_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage structure in the "
+ "APK Signature Scheme v3 signature's additional attributes section."),
/**
* The APK's signing certificate does not match the terminal node in the provided
* proof-of-rotation structure describing the signing certificate history
*/
V3_SIG_POR_CERT_MISMATCH(
"APK signing certificate differs from the associated certificate found in the "
+ "signer's SigningCertificateLineage."),
/**
* The APK Signature Scheme v3 signers encountered do not offer a continuous set of
* supported platform versions. Either they overlap, resulting in potentially two
* acceptable signers for a platform version, or there are holes which would create problems
* in the event of platform version upgrades.
*/
V3_INCONSISTENT_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK "
+ "versions are not continuous."),
/**
* The APK Signature Scheme v3 signers don't cover all requested SDK versions.
*
*
*
*/
V3_MISSING_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK "
+ "versions do not cover the entire desired range. Found min: %1$s max %2$s"),
/**
* The SigningCertificateLineages for different platform versions using APK Signature Scheme
* v3 do not go together. Specifically, each should be a subset of another, with the size
* of each increasing as the platform level increases.
*/
V3_INCONSISTENT_LINEAGES("SigningCertificateLineages targeting different platform versions"
+ " using APK Signature Scheme v3 are not all a part of the same overall lineage."),
/**
* APK Signing Block contains an unknown entry.
*
*
*
*/
APK_SIG_BLOCK_UNKNOWN_ENTRY_ID("APK Signing Block contains unknown entry: ID %1$#x"),
/**
* Failed to parse this signer's signature record contained in the APK Signature Scheme
* V4 signature.
*
*
*
*/
V4_SIG_MALFORMED_SIGNERS(
"V4 signature has malformed signer block"),
/**
* This APK Signature Scheme V4 signer contains a signature produced using an
* unknown algorithm.
*
*
*
*/
V4_SIG_UNKNOWN_SIG_ALGORITHM(
"V4 signature has unknown signing algorithm: %1$#x"),
/**
* This APK Signature Scheme V4 signer offers no signatures.
*/
V4_SIG_NO_SIGNATURES(
"V4 signature has no signature found"),
/**
* This APK Signature Scheme V4 signer offers signatures but none of them are
* supported.
*/
V4_SIG_NO_SUPPORTED_SIGNATURES(
"V4 signature has no supported signature"),
/**
* APK Signature Scheme v3 signature over this signer's signed-data block did not verify.
*
*
*
*/
V4_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
/**
* An exception was encountered while verifying APK Signature Scheme v3 signature of this
* signer.
*
*
*
*/
V4_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
/**
* Public key embedded in the APK Signature Scheme v4 signature of this signer could not be
* parsed.
*
*
*
*/
V4_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
/**
* This APK Signature Scheme V4 signer's certificate could not be parsed.
*
*
*
*/
V4_SIG_MALFORMED_CERTIFICATE(
"V4 signature has malformed certificate"),
/**
* This APK Signature Scheme V4 signer offers no certificate.
*/
V4_SIG_NO_CERTIFICATE("V4 signature has no certificate"),
/**
* This APK Signature Scheme V4 signer's public key listed in the signer's
* certificate does not match the public key listed in the signature proto.
*
*
*
*/
V4_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
"V4 signature has mismatched certificate and signature: <%1$s> vs <%2$s>"),
/**
* The APK's hash root (aka digest) does not match the hash root contained in the Signature
* Scheme V4 signature.
*
*
*
*/
V4_SIG_APK_ROOT_DID_NOT_VERIFY(
"V4 signature's hash tree root (content digest) did not verity"),
/**
* The APK's hash tree does not match the hash tree contained in the Signature
* Scheme V4 signature.
*
*
*
*/
V4_SIG_APK_TREE_DID_NOT_VERIFY(
"V4 signature's hash tree did not verity"),
/**
* Using more than one Signer to sign APK Signature Scheme V4 signature.
*/
V4_SIG_MULTIPLE_SIGNERS(
"V4 signature only supports one signer"),
/**
* The signer used to sign APK Signature Scheme V2/V3 signature does not match the signer
* used to sign APK Signature Scheme V4 signature.
*/
V4_SIG_V2_V3_SIGNERS_MISMATCH(
"V4 signature and V2/V3 signature have mismatched certificates"),
V4_SIG_V2_V3_DIGESTS_MISMATCH(
"V4 signature and V2/V3 signature have mismatched digests"),
/**
* The v4 signature format version isn't the same as the tool's current version, something
* may go wrong.
*/
V4_SIG_VERSION_NOT_CURRENT(
"V4 signature format version %1$d is different from the tool's current "
+ "version %2$d"),
/**
* The APK does not contain the source stamp certificate digest file nor the signature block
* when verification expected a source stamp to be present.
*/
SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING(
"Neither the source stamp certificate digest file nor the signature block are "
+ "present in the APK"),
/** APK contains SourceStamp file, but does not contain a SourceStamp signature. */
SOURCE_STAMP_SIG_MISSING("No SourceStamp signature"),
/**
* SourceStamp's certificate could not be parsed.
*
*
*
*/
SOURCE_STAMP_MALFORMED_CERTIFICATE("Malformed certificate: %1$s"),
/** Failed to parse SourceStamp's signature. */
SOURCE_STAMP_MALFORMED_SIGNATURE("Malformed SourceStamp signature"),
/**
* SourceStamp contains a signature produced using an unknown algorithm.
*
*
*
*/
SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
/**
* An exception was encountered while verifying SourceStamp signature.
*
*
*
*/
SOURCE_STAMP_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
/**
* SourceStamp signature block did not verify.
*
*
*
*/
SOURCE_STAMP_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
/** SourceStamp offers no signatures. */
SOURCE_STAMP_NO_SIGNATURE("No signature"),
/**
* SourceStamp offers an unsupported signature.
*
*
*/
SOURCE_STAMP_NO_SUPPORTED_SIGNATURE("Signature(s) {%1$s} not supported: %2$s"),
/**
* SourceStamp's certificate listed in the APK signing block does not match the certificate
* listed in the SourceStamp file in the APK.
*
*
*
*/
SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK(
"Certificate mismatch between SourceStamp block in APK signing block and"
+ " SourceStamp file in APK: <%1$s> vs <%2$s>"),
/**
* The APK contains a source stamp signature block without the expected certificate digest
* in the APK contents.
*/
SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST(
"A source stamp signature block was found without a corresponding certificate "
+ "digest in the APK"),
/**
* When verifying just the source stamp, the certificate digest in the APK does not match
* the expected digest.
*
*
*/
SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH(
"The source stamp certificate digest in the APK, %1$s, does not match the "
+ "expected digest, %2$s"),
/**
* Source stamp block contains a malformed attribute.
*
*
*
*/
SOURCE_STAMP_MALFORMED_ATTRIBUTE("Malformed stamp attribute #%1$d"),
/**
* Source stamp block contains an unknown attribute.
*
*
*
*/
SOURCE_STAMP_UNKNOWN_ATTRIBUTE("Unknown stamp attribute: ID %1$#x"),
/**
* Failed to parse the SigningCertificateLineage structure in the source stamp
* attributes section.
*/
SOURCE_STAMP_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage "
+ "structure in the source stamp attributes section."),
/**
* The source stamp certificate does not match the terminal node in the provided
* proof-of-rotation structure describing the stamp certificate history.
*/
SOURCE_STAMP_POR_CERT_MISMATCH(
"APK signing certificate differs from the associated certificate found in the "
+ "signer's SigningCertificateLineage."),
/**
* The source stamp SigningCertificateLineage attribute contains a proof-of-rotation record
* with signature(s) that did not verify.
*/
SOURCE_STAMP_POR_DID_NOT_VERIFY("Source stamp SigningCertificateLineage attribute "
+ "contains a proof-of-rotation record with signature(s) that did not verify."),
/**
* The APK could not be properly parsed due to a ZIP or APK format exception.
*
*
*/
MALFORMED_APK(
"Malformed APK; the following exception was caught when attempting to parse the "
+ "APK: %1$s"),
/**
* An unexpected exception was caught when attempting to verify the signature(s) within the
* APK.
*
*
*/
UNEXPECTED_EXCEPTION(
"An unexpected exception was caught when verifying the signature: %1$s");
private final String mFormat;
Issue(String format) {
mFormat = format;
}
/**
* Returns the format string suitable for combining the parameters of this issue into a
* readable string. See {@link java.util.Formatter} for format.
*/
private String getFormat() {
return mFormat;
}
}
/**
* {@link Issue} with associated parameters. {@link #toString()} produces a readable formatted
* form.
*/
public static class IssueWithParams extends ApkVerificationIssue {
private final Issue mIssue;
private final Object[] mParams;
/**
* Constructs a new {@code IssueWithParams} of the specified type and with provided
* parameters.
*/
public IssueWithParams(Issue issue, Object[] params) {
super(issue.mFormat, params);
mIssue = issue;
mParams = params;
}
/**
* Returns the type of this issue.
*/
public Issue getIssue() {
return mIssue;
}
/**
* Returns the parameters of this issue.
*/
public Object[] getParams() {
return mParams.clone();
}
/**
* Returns a readable form of this issue.
*/
@Override
public String toString() {
return String.format(mIssue.getFormat(), mParams);
}
}
/**
* Wrapped around {@code byte[]} which ensures that {@code equals} and {@code hashCode} operate
* on the contents of the arrays rather than on references.
*/
private static class ByteArray {
private final byte[] mArray;
private final int mHashCode;
private ByteArray(byte[] arr) {
mArray = arr;
mHashCode = Arrays.hashCode(mArray);
}
@Override
public int hashCode() {
return mHashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ByteArray)) {
return false;
}
ByteArray other = (ByteArray) obj;
if (hashCode() != other.hashCode()) {
return false;
}
if (!Arrays.equals(mArray, other.mArray)) {
return false;
}
return true;
}
}
/**
* Builder of {@link ApkVerifier} instances.
*
*