diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:52:11 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:52:11 +0000 |
commit | 4ae12a139a194b22c03d8bd964632cd0b27b1bce (patch) | |
tree | 9377b651b9a765ffbc05ec4a394972c54446e1fa | |
parent | 7af27dc8d1254408713ada779ef873151585614f (diff) | |
parent | ab50f0a22c4a770bb914d18483a6cc1dd930182b (diff) | |
download | apksig-4ae12a139a194b22c03d8bd964632cd0b27b1bce.tar.gz |
Snap for 8730993 from ab50f0a22c4a770bb914d18483a6cc1dd930182b to mainline-tzdata3-releaseaml_tz3_314012070aml_tz3_314012050aml_tz3_314012010aml_tz3_313110000aml_tz3_312511020aml_tz3_312511010aml_tz3_312410020aml_tz3_312410010android12-mainline-tzdata3-releaseaml_tz3_314012010
Change-Id: I23d5d80dd9244ec01bf7bdfb5dbbf9439037232a
62 files changed, 239 insertions, 2530 deletions
@@ -39,11 +39,6 @@ java_library_host { "src/main/java/**/*.java", ], java_version: "1.8", - target: { - windows: { - enabled: true, - }, - }, } // apksigner command-line tool for signing APKs and verifying their signatures @@ -58,14 +53,6 @@ java_binary_host { "apksig", "conscrypt-unbundled", ], + jni_libs: ["libconscrypt_openjdk_jni"], java_version: "1.8", - target: { - not_windows: { - jni_libs: ["libconscrypt_openjdk_jni"], - }, - windows: { - enabled: true, - wrapper: "etc/apksigner.bat", - }, - }, } diff --git a/build.gradle b/build.gradle index fe75e9a..4c05a77 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,6 @@ dependencies { implementation 'com.google.protobuf:protobuf-javalite:3.8.0' testImplementation 'junit:junit:4.13' testImplementation 'org.bouncycastle:bcprov-jdk15on:1.68' - testImplementation 'org.conscrypt:conscrypt-openjdk-uber:2.5.1' } protobuf { diff --git a/src/apksigner/java/com/android/apksigner/ApkSignerTool.java b/src/apksigner/java/com/android/apksigner/ApkSignerTool.java index 742a966..9fd0c34 100644 --- a/src/apksigner/java/com/android/apksigner/ApkSignerTool.java +++ b/src/apksigner/java/com/android/apksigner/ApkSignerTool.java @@ -22,7 +22,6 @@ import com.android.apksig.SigningCertificateLineage; import com.android.apksig.SigningCertificateLineage.SignerCapabilities; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.MinSdkVersionException; -import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; @@ -49,7 +48,6 @@ import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; import java.util.ArrayList; import java.util.Arrays; -import java.util.Base64; import java.util.List; /** @@ -64,8 +62,6 @@ public class ApkSignerTool { private static final String HELP_PAGE_VERIFY = "help_verify.txt"; private static final String HELP_PAGE_ROTATE = "help_rotate.txt"; private static final String HELP_PAGE_LINEAGE = "help_lineage.txt"; - private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----"; - private static final String END_CERTIFICATE = "-----END CERTIFICATE-----"; private static MessageDigest sha256 = null; private static MessageDigest sha1 = null; @@ -151,8 +147,6 @@ public class ApkSignerTool { int minSdkVersion = 1; boolean minSdkVersionSpecified = false; int maxSdkVersion = Integer.MAX_VALUE; - int rotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; - boolean rotationTargetsDevRelease = false; List<SignerParams> signers = new ArrayList<>(1); SignerParams signerParams = new SignerParams(); SigningCertificateLineage lineage = null; @@ -181,13 +175,7 @@ public class ApkSignerTool { minSdkVersionSpecified = true; } else if ("max-sdk-version".equals(optionName)) { maxSdkVersion = optionsParser.getRequiredIntValue("Maximum API Level"); - } else if ("rotation-min-sdk-version".equals(optionName)) { - rotationMinSdkVersion = optionsParser.getRequiredIntValue( - "Minimum API Level for Rotation"); - } else if ("rotation-targets-dev-release".equals(optionName)) { - rotationTargetsDevRelease = optionsParser.getOptionalBooleanValue(true); - } - else if ("v1-signing-enabled".equals(optionName)) { + } else if ("v1-signing-enabled".equals(optionName)) { v1SigningEnabled = optionsParser.getOptionalBooleanValue(true); } else if ("v2-signing-enabled".equals(optionName)) { v2SigningEnabled = optionsParser.getOptionalBooleanValue(true); @@ -374,9 +362,7 @@ public class ApkSignerTool { .setVerityEnabled(verityEnabled) .setV4ErrorReportingEnabled(v4SigningEnabled && v4SigningFlagFound) .setDebuggableApkPermitted(debuggableApkPermitted) - .setSigningCertificateLineage(lineage) - .setMinSdkVersionForRotation(rotationMinSdkVersion) - .setRotationTargetsDevRelease(rotationTargetsDevRelease); + .setSigningCertificateLineage(lineage); if (minSdkVersionSpecified) { apkSignerBuilder.setMinSdkVersion(minSdkVersion); } @@ -464,7 +450,6 @@ public class ApkSignerTool { int maxSdkVersion = Integer.MAX_VALUE; boolean maxSdkVersionSpecified = false; boolean printCerts = false; - boolean printCertsPem = false; boolean verbose = false; boolean warningsTreatedAsErrors = false; boolean verifySourceStamp = false; @@ -483,13 +468,6 @@ public class ApkSignerTool { maxSdkVersionSpecified = true; } else if ("print-certs".equals(optionName)) { printCerts = optionsParser.getOptionalBooleanValue(true); - } else if ("print-certs-pem".equals(optionName)) { - printCertsPem = optionsParser.getOptionalBooleanValue(true); - // If the PEM output of the certs is requested, this implicitly implies the - // cert details should be printed. - if (printCertsPem && !printCerts) { - printCerts = true; - } } else if (("v".equals(optionName)) || ("verbose".equals(optionName))) { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("Werr".equals(optionName)) { @@ -590,9 +568,6 @@ public class ApkSignerTool { "Verified using v3 scheme (APK Signature Scheme v3): " + result.isVerifiedUsingV3Scheme()); System.out.println( - "Verified using v3.1 scheme (APK Signature Scheme v3.1): " - + result.isVerifiedUsingV31Scheme()); - System.out.println( "Verified using v4 scheme (APK Signature Scheme v4): " + result.isVerifiedUsingV4Scheme()); System.out.println("Verified for SourceStamp: " + result.isSourceStampVerified()); @@ -601,37 +576,14 @@ public class ApkSignerTool { } } if (printCerts) { - // The v3.1 signature scheme allows key rotation to target T+ while the original - // signing key can still be used with v3.0; if a v3.1 block is present then also - // include the target SDK versions for both rotation and the original signing key. - if (result.isVerifiedUsingV31Scheme()) { - for (ApkVerifier.Result.V3SchemeSignerInfo signer : - result.getV31SchemeSigners()) { - - printCertificate(signer.getCertificate(), - "Signer (minSdkVersion=" + signer.getMinSdkVersion() - + (signer.getRotationTargetsDevRelease() - ? " (dev release=true)" : "") - + ", maxSdkVersion=" + signer.getMaxSdkVersion() + ")", - verbose, printCertsPem); - } - for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { - printCertificate(signer.getCertificate(), - "Signer (minSdkVersion=" + signer.getMinSdkVersion() - + ", maxSdkVersion=" + signer.getMaxSdkVersion() + ")", - verbose, printCertsPem); - } - } else { - int signerNumber = 0; - for (X509Certificate signerCert : signerCerts) { - signerNumber++; - printCertificate(signerCert, "Signer #" + signerNumber, verbose, - printCertsPem); - } + int signerNumber = 0; + for (X509Certificate signerCert : signerCerts) { + signerNumber++; + printCertificate(signerCert, "Signer #" + signerNumber, verbose); } if (sourceStampInfo != null) { printCertificate(sourceStampInfo.getCertificate(), "Source Stamp Signer", - verbose, printCertsPem); + verbose); } } } else { @@ -682,20 +634,6 @@ public class ApkSignerTool { "WARNING: APK Signature Scheme v3 " + signerName + ": " + warning); } } - for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { - String signerName = "signer #" + (signer.getIndex() + 1) + "(minSdkVersion=" - + signer.getMinSdkVersion() + ", maxSdkVersion=" + signer.getMaxSdkVersion() - + ")"; - for (ApkVerifier.IssueWithParams error : signer.getErrors()) { - System.err.println( - "ERROR: APK Signature Scheme v3.1 " + signerName + ": " + error); - } - for (ApkVerifier.IssueWithParams warning : signer.getWarnings()) { - warningsEncountered = true; - warningsOut.println( - "WARNING: APK Signature Scheme v3.1 " + signerName + ": " + warning); - } - } if (sourceStampInfo != null) { for (ApkVerifier.IssueWithParams error : sourceStampInfo.getErrors()) { @@ -856,7 +794,6 @@ public class ApkSignerTool { boolean verbose = false; boolean printCerts = false; - boolean printCertsPem = false; boolean lineageUpdated = false; File inputKeyLineage = null; File outputKeyLineage = null; @@ -878,13 +815,6 @@ public class ApkSignerTool { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("print-certs".equals(optionName)) { printCerts = optionsParser.getOptionalBooleanValue(true); - } else if ("print-certs-pem".equals(optionName)) { - printCertsPem = optionsParser.getOptionalBooleanValue(true); - // If the PEM output of the certs is requested, this implicitly implies the - // cert details should be printed. - if (printCertsPem && !printCerts) { - printCerts = true; - } } else { throw new ParameterException( "Unsupported option: " + optionsParser.getOptionOriginalForm() @@ -945,8 +875,7 @@ public class ApkSignerTool { for (int i = 0; i < signingCerts.size(); i++) { X509Certificate signerCert = signingCerts.get(i); SignerCapabilities signerCapabilities = lineage.getSignerCapabilities(signerCert); - printCertificate(signerCert, "Signer #" + (i + 1) + " in lineage", verbose, - printCertsPem); + printCertificate(signerCert, "Signer #" + (i + 1) + " in lineage", verbose); printCapabilities(signerCapabilities); } } @@ -1078,29 +1007,19 @@ public class ApkSignerTool { } /** - * @see #printCertificate(X509Certificate, String, boolean, boolean) - */ - public static void printCertificate(X509Certificate cert, String name, boolean verbose) - throws NoSuchAlgorithmException, CertificateEncodingException { - printCertificate(cert, name, verbose, false); - } - - /** * Prints details from the provided certificate to stdout. * * @param cert the certificate to be displayed. * @param name the name to be used to identify the certificate. * @param verbose boolean indicating whether public key details from the certificate should be * displayed. - * @param pemOutput boolean indicating whether the PEM encoding of the certificate should be - * displayed. * @throws NoSuchAlgorithmException if an instance of MD5, SHA-1, or SHA-256 cannot be * obtained. * @throws CertificateEncodingException if an error is encountered when encoding the * certificate. */ - public static void printCertificate(X509Certificate cert, String name, boolean verbose, - boolean pemOutput) throws NoSuchAlgorithmException, CertificateEncodingException { + public static void printCertificate(X509Certificate cert, String name, boolean verbose) + throws NoSuchAlgorithmException, CertificateEncodingException { if (cert == null) { throw new NullPointerException("cert == null"); } @@ -1145,18 +1064,6 @@ public class ApkSignerTool { System.out.println( name + " public key MD5 digest: " + HexEncoding.encode(md5.digest(encodedKey))); } - - if (pemOutput) { - System.out.println(BEGIN_CERTIFICATE); - final int lineWidth = 64; - String pemEncodedCert = Base64.getEncoder().encodeToString(cert.getEncoded()); - for (int i = 0; i < pemEncodedCert.length(); i += lineWidth) { - System.out.println(pemEncodedCert.substring(i, i + lineWidth > pemEncodedCert.length() - ? pemEncodedCert.length() - : i + lineWidth)); - } - System.out.println(END_CERTIFICATE); - } } /** diff --git a/src/apksigner/java/com/android/apksigner/help_lineage.txt b/src/apksigner/java/com/android/apksigner/help_lineage.txt index 8fe410b..3f4922d 100644 --- a/src/apksigner/java/com/android/apksigner/help_lineage.txt +++ b/src/apksigner/java/com/android/apksigner/help_lineage.txt @@ -19,10 +19,6 @@ has been migrated to the new signing certificate. --print-certs Show information about the signing certificates and their capabilities in the SigningCertificateLineage. ---print-certs-pem Show information about the signing certificates and their capabilities - in the SigningCertificateLineage; prints the PEM encoding of each signing - certificate to stdout. - -v, --verbose Verbose output mode. -h, --help Show help about this command and exit. diff --git a/src/apksigner/java/com/android/apksigner/help_rotate.txt b/src/apksigner/java/com/android/apksigner/help_rotate.txt index d19136b..ff58372 100644 --- a/src/apksigner/java/com/android/apksigner/help_rotate.txt +++ b/src/apksigner/java/com/android/apksigner/help_rotate.txt @@ -43,17 +43,10 @@ used in some situations on the platform even though the APK is now being signed by a newer signing certificate. By default, the new signer will have all capabilities, but the capability options can be specified for the new signer during rotation to act as a default level of trust when moving to a newer -signing certificate. The capability options accept an optional boolean value of +signing certificate.The capability options accept an optional boolean value of true or false; if this value is not specified then the option will default to true. -Prior to Android 12, if multiple apps shared a common signer in their signing lineage -with distinct capabilities assigned, a bug in the platform would cause the capabilities -declared for this signer in one of the app's signing lineage to be assigned to this same -common signer in the lineage of the rest of the apps. Apps that use the default capabilities, -or that assign the same capabilities to a common signer in their lineage, are not impacted -by this bug. - --ks Load private key and certificate chain from the Java KeyStore initialized from the specified file. NONE means diff --git a/src/apksigner/java/com/android/apksigner/help_sign.txt b/src/apksigner/java/com/android/apksigner/help_sign.txt index 66aec29..d66b7a3 100644 --- a/src/apksigner/java/com/android/apksigner/help_sign.txt +++ b/src/apksigner/java/com/android/apksigner/help_sign.txt @@ -61,24 +61,6 @@ certificate. --max-sdk-version Highest API Level on which this APK's signatures will be verified. By default, the highest possible value is used. ---rotation-min-sdk-version Lowest API Level for which an APK's rotated signing - key should be used to produce the APK's signature. The - original signing key for the APK will be used for all - previous platform versions. Specifying a value <= 32 - (Android Sv2) will result in the original V3 signing block - being used without platform targeting. By default, - rotated signing keys will be used with the V3.1 signing - block which supports Android T+. - ---rotation-targets-dev-release The specified rotation-min-sdk-version is intended - for a platform release under development. During development - of a new platform, the API Level of the previously released - platform is used as the API Level of the development - platform until the SDK is finalized. This flag allows - targeting signing key rotation to a development platform - with API Level X while preventing the rotated key from being - used on the latest release platform with API Level X. - --debuggable-apk-permitted Whether to permit signing android:debuggable="true" APKs. Android disables some of its security protections for such apps. For example, anybody with ADB shell access diff --git a/src/apksigner/java/com/android/apksigner/help_verify.txt b/src/apksigner/java/com/android/apksigner/help_verify.txt index bc70924..c5cf663 100644 --- a/src/apksigner/java/com/android/apksigner/help_verify.txt +++ b/src/apksigner/java/com/android/apksigner/help_verify.txt @@ -11,9 +11,6 @@ range of API Levels. --print-certs Show information about the APK's signing certificates ---print-certs-pem Show information about the APK's signing certificates and prints the PEM - encoding of each signing certificate to stdout. - -v, --verbose Verbose output mode --min-sdk-version Lowest API Level on which this APK's signatures will be diff --git a/src/main/java/com/android/apksig/ApkSigner.java b/src/main/java/com/android/apksig/ApkSigner.java index 365fdbf..ca792c4 100644 --- a/src/main/java/com/android/apksig/ApkSigner.java +++ b/src/main/java/com/android/apksig/ApkSigner.java @@ -17,14 +17,11 @@ package com.android.apksig; import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; -import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; -import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkSigningBlockNotFoundException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.apk.MinSdkVersionException; -import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.util.ByteBufferDataSource; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.EocdRecord; @@ -92,8 +89,6 @@ public class ApkSigner { private final SigningCertificateLineage mSourceStampSigningCertificateLineage; private final boolean mForceSourceStampOverwrite; private final Integer mMinSdkVersion; - private final int mRotationMinSdkVersion; - private final boolean mRotationTargetsDevRelease; private final boolean mV1SigningEnabled; private final boolean mV2SigningEnabled; private final boolean mV3SigningEnabled; @@ -123,8 +118,6 @@ public class ApkSigner { SigningCertificateLineage sourceStampSigningCertificateLineage, boolean forceSourceStampOverwrite, Integer minSdkVersion, - int rotationMinSdkVersion, - boolean rotationTargetsDevRelease, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, @@ -148,8 +141,6 @@ public class ApkSigner { mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; mForceSourceStampOverwrite = forceSourceStampOverwrite; mMinSdkVersion = minSdkVersion; - mRotationMinSdkVersion = rotationMinSdkVersion; - mRotationTargetsDevRelease = rotationTargetsDevRelease; mV1SigningEnabled = v1SigningEnabled; mV2SigningEnabled = v2SigningEnabled; mV3SigningEnabled = v3SigningEnabled; @@ -305,9 +296,7 @@ public class ApkSigner { .setVerityEnabled(mVerityEnabled) .setDebuggableApkPermitted(mDebuggableApkPermitted) .setOtherSignersSignaturesPreserved(mOtherSignersSignaturesPreserved) - .setSigningCertificateLineage(mSigningCertificateLineage) - .setMinSdkVersionForRotation(mRotationMinSdkVersion) - .setRotationTargetsDevRelease(mRotationTargetsDevRelease); + .setSigningCertificateLineage(mSigningCertificateLineage); if (mCreatedBy != null) { signerEngineBuilder.setCreatedBy(mCreatedBy); } @@ -1104,8 +1093,6 @@ public class ApkSigner { private boolean mOtherSignersSignaturesPreserved; private String mCreatedBy; private Integer mMinSdkVersion; - private int mRotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; - private boolean mRotationTargetsDevRelease = false; private final ApkSignerEngine mSignerEngine; @@ -1314,58 +1301,6 @@ public class ApkSigner { } /** - * Sets the minimum Android platform version (API Level) for which an APK's rotated signing - * key should be used to produce the APK's signature. The original signing key for the APK - * will be used for all previous platform versions. If a rotated key with signing lineage is - * not provided then this method is a noop. This method is useful for overriding the - * default behavior where Android T is set as the minimum API level for rotation. - * - * <p><em>Note:</em>Specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result - * in the original V3 signing block being used without platform targeting. - * - * <p><em>Note:</em> This method may only be invoked when this builder is not initialized - * with an {@link ApkSignerEngine}. - * - * @throws IllegalStateException if this builder was initialized with an {@link - * ApkSignerEngine} - */ - public Builder setMinSdkVersionForRotation(int minSdkVersion) { - checkInitializedWithoutEngine(); - // If the provided SDK version does not support v3.1, then use the default SDK version - // with rotation support. - if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { - mRotationMinSdkVersion = MIN_SDK_WITH_V3_SUPPORT; - } else { - mRotationMinSdkVersion = minSdkVersion; - } - return this; - } - - /** - * Sets whether the rotation-min-sdk-version is intended to target a development release; - * this is primarily required after the T SDK is finalized, and an APK needs to target U - * during its development cycle for rotation. - * - * <p>This is only required after the T SDK is finalized since S and earlier releases do - * not know about the V3.1 block ID, but once T is released and work begins on U, U will - * use the SDK version of T during development. Specifying a rotation-min-sdk-version of T's - * SDK version along with setting {@code enabled} to true will allow an APK to use the - * rotated key on a device running U while causing this to be bypassed for T. - * - * <p><em>Note:</em>If the rotation-min-sdk-version is less than or equal to 32 (Android - * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call - * will be a noop. - * - * <p><em>Note:</em> This method may only be invoked when this builder is not initialized - * with an {@link ApkSignerEngine}. - */ - public Builder setRotationTargetsDevRelease(boolean enabled) { - checkInitializedWithoutEngine(); - mRotationTargetsDevRelease = enabled; - return this; - } - - /** * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme). * * <p>By default, whether APK is signed using JAR signing is determined by {@code @@ -1603,8 +1538,6 @@ public class ApkSigner { mSourceStampSigningCertificateLineage, mForceSourceStampOverwrite, mMinSdkVersion, - mRotationMinSdkVersion, - mRotationTargetsDevRelease, mV1SigningEnabled, mV2SigningEnabled, mV3SigningEnabled, diff --git a/src/main/java/com/android/apksig/ApkVerificationIssue.java b/src/main/java/com/android/apksig/ApkVerificationIssue.java index fa2b7aa..2aa9d0b 100644 --- a/src/main/java/com/android/apksig/ApkVerificationIssue.java +++ b/src/main/java/com/android/apksig/ApkVerificationIssue.java @@ -116,8 +116,6 @@ public class ApkVerificationIssue { public static final int JAR_SIG_NO_SIGNATURES = 36; /** An exception was encountered when parsing the V1 / jar signer in the signature block. */ public static final int JAR_SIG_PARSE_EXCEPTION = 37; - /** The source stamp timestamp attribute has an invalid value. */ - public static final int SOURCE_STAMP_INVALID_TIMESTAMP = 38; private final int mIssueId; private final String mFormat; diff --git a/src/main/java/com/android/apksig/ApkVerifier.java b/src/main/java/com/android/apksig/ApkVerifier.java index fc28864..354dfbd 100644 --- a/src/main/java/com/android/apksig/ApkVerifier.java +++ b/src/main/java/com/android/apksig/ApkVerifier.java @@ -24,7 +24,6 @@ import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_S 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 static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; @@ -202,58 +201,23 @@ public class ApkVerifier { Set<Integer> foundApkSigSchemeIds = new HashSet<>(2); if (maxSdkVersion >= AndroidSdkVersion.N) { RunnablesExecutor executor = RunnablesExecutor.SINGLE_THREADED; - // Android T and newer attempts to verify APKs using APK Signature Scheme V3.1. v3.0 - // also includes stripping protection for the minimum SDK version on which the rotated - // signing key should be used. - int rotationMinSdkVersion = 0; - if (maxSdkVersion >= MIN_SDK_WITH_V31_SUPPORT) { - try { - ApkSigningBlockUtils.Result v31Result = new V3SchemeVerifier.Builder(apk, - zipSections, Math.max(minSdkVersion, MIN_SDK_WITH_V31_SUPPORT), - maxSdkVersion) - .setRunnablesExecutor(executor) - .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) - .build() - .verify(); - foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31); - rotationMinSdkVersion = v31Result.signers.stream().mapToInt( - signer -> signer.minSdkVersion).min().orElse(0); - result.mergeFrom(v31Result); - signatureSchemeApkContentDigests.put( - ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31, - getApkContentDigestsFromSigningSchemeResult(v31Result)); - } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { - // v3.1 signature not required - } - if (result.containsErrors()) { - return result; - } - } // Android P and newer attempts to verify APKs using APK Signature Scheme v3 - if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT || foundApkSigSchemeIds.isEmpty()) { + if (maxSdkVersion >= AndroidSdkVersion.P) { try { - V3SchemeVerifier.Builder builder = new V3SchemeVerifier.Builder(apk, - zipSections, Math.max(minSdkVersion, AndroidSdkVersion.P), - maxSdkVersion) - .setRunnablesExecutor(executor) - .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID); - if (rotationMinSdkVersion > 0) { - builder.setRotationMinSdkVersion(rotationMinSdkVersion); - } - ApkSigningBlockUtils.Result v3Result = builder.build().verify(); + ApkSigningBlockUtils.Result v3Result = + V3SchemeVerifier.verify( + executor, + apk, + zipSections, + Math.max(minSdkVersion, AndroidSdkVersion.P), + maxSdkVersion); foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); result.mergeFrom(v3Result); signatureSchemeApkContentDigests.put( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3, getApkContentDigestsFromSigningSchemeResult(v3Result)); } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { - // v3 signature not required unless a v3.1 signature was found as a v3.1 - // signature is intended to support key rotation on T+ with the v3 signature - // containing the original signing key. - if (foundApkSigSchemeIds.contains( - ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31)) { - result.addError(Issue.V31_BLOCK_FOUND_WITHOUT_V3_BLOCK); - } + // v3 signature not required } if (result.containsErrors()) { return result; @@ -490,6 +454,9 @@ public class ApkVerifier { // The apkDigest field in the v4 signature should match the selected v2/v3. if (result.isVerifiedUsingV4Scheme()) { List<Result.V4SchemeSignerInfo> v4Signers = result.getV4SchemeSigners(); + if (v4Signers.size() != 1) { + result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); + } List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> digestsFromV4 = v4Signers.get(0).getContentDigests(); @@ -499,22 +466,21 @@ public class ApkVerifier { final byte[] digestFromV4 = digestsFromV4.get(0).getValue(); if (result.isVerifiedUsingV3Scheme()) { - int expectedSize = result.isVerifiedUsingV31Scheme() ? 2 : 1; - if (v4Signers.size() != expectedSize) { + List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners(); + if (v3Signers.size() != 1) { result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); } - checkV4Signer(result.getV3SchemeSigners(), v4Signers.get(0).mCerts, digestFromV4, - result); - if (result.isVerifiedUsingV31Scheme()) { - checkV4Signer(result.getV31SchemeSigners(), v4Signers.get(1).mCerts, - digestFromV4, result); + // Compare certificates. + checkV4Certificate(v4Signers.get(0).mCerts, v3Signers.get(0).mCerts, result); + + // Compare digests. + final byte[] digestFromV3 = pickBestDigestForV4( + v3Signers.get(0).getContentDigests()); + if (!Arrays.equals(digestFromV4, digestFromV3)) { + result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH); } } else if (result.isVerifiedUsingV2Scheme()) { - if (v4Signers.size() != 1) { - result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); - } - List<Result.V2SchemeSignerInfo> v2Signers = result.getV2SchemeSigners(); if (v2Signers.size() != 1) { result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); @@ -561,7 +527,7 @@ public class ApkVerifier { // Allow this case to fall through to the next as a signature satisfying a // later scheme version will also satisfy this requirement. case VERSION_APK_SIGNATURE_SCHEME_V3: - if (result.isVerifiedUsingV3Scheme() || result.isVerifiedUsingV31Scheme()) { + if (result.isVerifiedUsingV3Scheme()) { break; } result.addError(Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET, @@ -577,10 +543,7 @@ public class ApkVerifier { // Verified result.setVerified(); - if (result.isVerifiedUsingV31Scheme()) { - List<Result.V3SchemeSignerInfo> v31Signers = result.getV31SchemeSigners(); - result.addSignerCertificate(v31Signers.get(v31Signers.size() - 1).getCertificate()); - } else if (result.isVerifiedUsingV3Scheme()) { + if (result.isVerifiedUsingV3Scheme()) { List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners(); result.addSignerCertificate(v3Signers.get(v3Signers.size() - 1).getCertificate()); } else if (result.isVerifiedUsingV2Scheme()) { @@ -932,22 +895,6 @@ public class ApkVerifier { return result; } - private static void checkV4Signer(List<Result.V3SchemeSignerInfo> v3Signers, - List<X509Certificate> v4Certs, byte[] digestFromV4, Result result) { - if (v3Signers.size() != 1) { - result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); - } - - // Compare certificates. - checkV4Certificate(v4Certs, v3Signers.get(0).mCerts, result); - - // Compare digests. - final byte[] digestFromV3 = pickBestDigestForV4(v3Signers.get(0).getContentDigests()); - if (!Arrays.equals(digestFromV4, digestFromV3)) { - result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH); - } - } - private static void checkV4Certificate(List<X509Certificate> v4Certs, List<X509Certificate> v2v3Certs, Result result) { try { @@ -1058,7 +1005,6 @@ public class ApkVerifier { private final List<V1SchemeSignerInfo> mV1SchemeIgnoredSigners = new ArrayList<>(); private final List<V2SchemeSignerInfo> mV2SchemeSigners = new ArrayList<>(); private final List<V3SchemeSignerInfo> mV3SchemeSigners = new ArrayList<>(); - private final List<V3SchemeSignerInfo> mV31SchemeSigners = new ArrayList<>(); private final List<V4SchemeSignerInfo> mV4SchemeSigners = new ArrayList<>(); private SourceStampInfo mSourceStampInfo; @@ -1066,7 +1012,6 @@ public class ApkVerifier { private boolean mVerifiedUsingV1Scheme; private boolean mVerifiedUsingV2Scheme; private boolean mVerifiedUsingV3Scheme; - private boolean mVerifiedUsingV31Scheme; private boolean mVerifiedUsingV4Scheme; private boolean mSourceStampVerified; private boolean mWarningsAsErrors; @@ -1105,13 +1050,6 @@ public class ApkVerifier { } /** - * Returns {@code true} if the APK's APK Signature Scheme v3.1 signature verified. - */ - public boolean isVerifiedUsingV31Scheme() { - return mVerifiedUsingV31Scheme; - } - - /** * Returns {@code true} if the APK's APK Signature Scheme v4 signature verified. */ public boolean isVerifiedUsingV4Scheme() { @@ -1177,23 +1115,7 @@ public class ApkVerifier { return mV3SchemeSigners; } - /** - * Returns information about APK Signature Scheme v3.1 signers associated with the APK's - * signature. - * - * <note> Multiple signers represent different targeted platform versions, not - * a signing identity of multiple signers. APK Signature Scheme v3.1 only supports single - * signer identities.</note> - */ - public List<V3SchemeSignerInfo> getV31SchemeSigners() { - return mV31SchemeSigners; - } - - /** - * Returns information about APK Signature Scheme v4 signers associated with the APK's - * signature. - */ - public List<V4SchemeSignerInfo> getV4SchemeSigners() { + private List<V4SchemeSignerInfo> getV4SchemeSigners() { return mV4SchemeSigners; } @@ -1288,16 +1210,6 @@ public class ApkVerifier { for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) { mV3SchemeSigners.add(new V3SchemeSignerInfo(signer)); } - // Do not overwrite a previously set lineage from a v3.1 signing block. - if (mSigningCertificateLineage == null) { - mSigningCertificateLineage = source.signingCertificateLineage; - } - break; - case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31: - mVerifiedUsingV31Scheme = source.verified; - for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) { - mV31SchemeSigners.add(new V3SchemeSignerInfo(signer)); - } mSigningCertificateLineage = source.signingCertificateLineage; break; case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4: @@ -1585,9 +1497,6 @@ public class ApkVerifier { private final List<IssueWithParams> mWarnings; private final List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> mContentDigests; - private final int mMinSdkVersion; - private final int mMaxSdkVersion; - private final boolean mRotationTargetsDevRelease; private V3SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) { mIndex = result.index; @@ -1595,11 +1504,6 @@ public class ApkVerifier { mErrors = result.getErrors(); mWarnings = result.getWarnings(); mContentDigests = result.contentDigests; - mMinSdkVersion = result.minSdkVersion; - mMaxSdkVersion = result.maxSdkVersion; - mRotationTargetsDevRelease = result.additionalAttributes.stream().mapToInt( - attribute -> attribute.getId()).anyMatch( - attrId -> attrId == V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID); } /** @@ -1645,33 +1549,6 @@ public class ApkVerifier { public List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> getContentDigests() { return mContentDigests; } - - /** - * Returns the minimum SDK version on which this signer should be verified. - */ - public int getMinSdkVersion() { - return mMinSdkVersion; - } - - /** - * Returns the maximum SDK version on which this signer should be verified. - */ - public int getMaxSdkVersion() { - return mMaxSdkVersion; - } - - /** - * Returns whether rotation is targeting a development release. - * - * <p>A development release uses the SDK version of the previously released platform - * until the SDK of the development release is finalized. To allow rotation to target - * a development release after T, this attribute must be set to ensure rotation is - * used on the development release but ignored on the released platform with the same - * API level. - */ - public boolean getRotationTargetsDevRelease() { - return mRotationTargetsDevRelease; - } } /** @@ -1767,8 +1644,6 @@ public class ApkVerifier { private final SourceStampVerificationStatus mSourceStampVerificationStatus; - private final long mTimestamp; - private SourceStampInfo(ApkSignerInfo result) { mCertificates = result.certs; mCertificateLineage = result.certificateLineage; @@ -1782,7 +1657,6 @@ public class ApkVerifier { mSourceStampVerificationStatus = SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED; } - mTimestamp = result.timestamp; } SourceStampInfo(SourceStampVerificationStatus sourceStampVerificationStatus) { @@ -1791,7 +1665,6 @@ public class ApkVerifier { mErrors = Collections.emptyList(); mWarnings = Collections.emptyList(); mSourceStampVerificationStatus = sourceStampVerificationStatus; - mTimestamp = 0; } /** @@ -1831,14 +1704,6 @@ public class ApkVerifier { public SourceStampVerificationStatus getSourceStampVerificationStatus() { return mSourceStampVerificationStatus; } - - /** - * Returns the epoch timestamp in seconds representing the time this source stamp block - * was signed, or 0 if the timestamp is not available. - */ - public long getTimestampEpochSeconds() { - return mTimestamp; - } } } @@ -2662,61 +2527,6 @@ public class ApkVerifier { + " using APK Signature Scheme v3 are not all a part of the same overall lineage."), /** - * The v3 stripping protection attribute for rotation is present, but a v3.1 signing block - * was not found. - * - * <ul> - * <li>Parameter 1: min SDK version supporting rotation from attribute ({@code Integer}) - * </ul> - */ - V31_BLOCK_MISSING( - "The v3 signer indicates key rotation should be supported starting from SDK " - + "version %1$s, but a v3.1 block was not found"), - - /** - * The v3 stripping protection attribute for rotation does not match the minimum SDK version - * targeting rotation in the v3.1 signer block. - * - * <ul> - * <li>Parameter 1: min SDK version supporting rotation from attribute ({@code Integer}) - * <li>Parameter 2: min SDK version supporting rotation from v3.1 block ({@code Integer}) - * </ul> - */ - V31_ROTATION_MIN_SDK_MISMATCH( - "The v3 signer indicates key rotation should be supported starting from SDK " - + "version %1$s, but the v3.1 block targets %2$s for rotation"), - - /** - * The APK supports key rotation with SDK version targeting using v3.1, but the rotation min - * SDK version stripping protection attribute was not written to the v3 signer. - * - * <ul> - * <li>Parameter 1: min SDK version supporting rotation from v3.1 block ({@code Integer}) - * </ul> - */ - V31_ROTATION_MIN_SDK_ATTR_MISSING( - "APK supports key rotation starting from SDK version %1$s, but the v3 signer does" - + " not contain the attribute to detect if this signature is stripped"), - - /** - * The APK contains a v3.1 signing block without a v3.0 block. The v3.1 block should only - * be used for targeting rotation for a later SDK version; if an APK's minSdkVersion is the - * same as the SDK version for rotation then this should be written to a v3.0 block. - */ - V31_BLOCK_FOUND_WITHOUT_V3_BLOCK( - "The APK contains a v3.1 signing block without a v3.0 base block"), - - /** - * The APK contains a v3.0 signing block with a rotation-targets-dev-release attribute in - * the signer; this attribute is only intended for v3.1 signers to indicate they should be - * targeting the next development release that is using the SDK version of the previously - * released platform SDK version. - */ - V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER( - "The rotation-targets-dev-release attribute is only supported on v3.1 signers; " - + "this attribute will be ignored by the platform in a v3.0 signer"), - - /** * APK Signing Block contains an unknown entry. * * <ul> @@ -3012,16 +2822,6 @@ public class ApkVerifier { + "contains a proof-of-rotation record with signature(s) that did not verify."), /** - * The source stamp timestamp attribute has an invalid value (<= 0). - * <ul> - * <li>Parameter 1: The invalid timestamp value. - * </ul> - */ - SOURCE_STAMP_INVALID_TIMESTAMP( - "The source stamp" - + " timestamp attribute has an invalid value: %1$d"), - - /** * The APK could not be properly parsed due to a ZIP or APK format exception. * <ul> * <li>Parameter 1: The {@code Exception} caught when attempting to parse the APK. @@ -3317,8 +3117,6 @@ public class ApkVerifier { Issue.JAR_SIG_NO_SIGNATURES); sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION, Issue.JAR_SIG_PARSE_EXCEPTION); - sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP, - Issue.SOURCE_STAMP_INVALID_TIMESTAMP); } /** diff --git a/src/main/java/com/android/apksig/Constants.java b/src/main/java/com/android/apksig/Constants.java index f64064c..680c5c3 100644 --- a/src/main/java/com/android/apksig/Constants.java +++ b/src/main/java/com/android/apksig/Constants.java @@ -32,7 +32,6 @@ public class Constants { public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; - public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31; public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4; public static final String MANIFEST_ENTRY_NAME = V1SchemeConstants.MANIFEST_ENTRY_NAME; @@ -42,14 +41,10 @@ public class Constants { public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; - public static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = - V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID; public static final int PROOF_OF_ROTATION_ATTR_ID = V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID; public static final int V1_SOURCE_STAMP_BLOCK_ID = SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID; public static final int V2_SOURCE_STAMP_BLOCK_ID = SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID; - - public static final String OID_RSA_ENCRYPTION = "1.2.840.113549.1.1.1"; } diff --git a/src/main/java/com/android/apksig/DefaultApkSignerEngine.java b/src/main/java/com/android/apksig/DefaultApkSignerEngine.java index f25bc59..e2256da 100644 --- a/src/main/java/com/android/apksig/DefaultApkSignerEngine.java +++ b/src/main/java/com/android/apksig/DefaultApkSignerEngine.java @@ -22,8 +22,6 @@ import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERITY_PADDIN 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.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; -import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; @@ -36,7 +34,6 @@ import com.android.apksig.internal.apk.v1.V1SchemeConstants; import com.android.apksig.internal.apk.v1.V1SchemeSigner; import com.android.apksig.internal.apk.v1.V1SchemeVerifier; import com.android.apksig.internal.apk.v2.V2SchemeSigner; -import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.apk.v4.V4SchemeSigner; import com.android.apksig.internal.apk.v4.V4Signature; @@ -69,7 +66,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -106,8 +102,6 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { private final List<SignerConfig> mSignerConfigs; private final SignerConfig mSourceStampSignerConfig; private final SigningCertificateLineage mSourceStampSigningCertificateLineage; - private final int mRotationMinSdkVersion; - private final boolean mRotationTargetsDevRelease; private final int mMinSdkVersion; private final SigningCertificateLineage mSigningCertificateLineage; @@ -190,8 +184,6 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, int minSdkVersion, - int rotationMinSdkVersion, - boolean rotationTargetsDevRelease, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, @@ -219,8 +211,6 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { mSourceStampSignerConfig = sourceStampSignerConfig; mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; mMinSdkVersion = minSdkVersion; - mRotationMinSdkVersion = rotationMinSdkVersion; - mRotationTargetsDevRelease = rotationTargetsDevRelease; mSigningCertificateLineage = signingCertificateLineage; if (v1SigningEnabled) { @@ -338,29 +328,8 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { } } - private boolean signingLineageHas31Support() { - return mSigningCertificateLineage != null - && mRotationMinSdkVersion >= MIN_SDK_WITH_V31_SUPPORT - && mMinSdkVersion < mRotationMinSdkVersion; - } - private List<ApkSigningBlockUtils.SignerConfig> processV3Configs( List<ApkSigningBlockUtils.SignerConfig> rawConfigs) throws InvalidKeyException { - // While the V3 signature scheme supports rotation, it is possible for a caller to specify - // a minimum SDK version for rotation that is >= the first SDK version that supports V3.1; - // in this case the V3.1 signing block will contain the rotated key, and the V3.0 block - // will use the original signing key. - if (signingLineageHas31Support()) { - SigningCertificateLineage subLineage = mSigningCertificateLineage - .getSubLineage(mSignerConfigs.get(0).mCertificates.get(0)); - if (subLineage.size() != 1) { - throw new IllegalArgumentException( - "v3.1 signing enabled but the oldest signer in the SigningCertificateLineage" - + " for the v3.0 signing block is missing. Please provide" - + " the oldest signer to enable v3.1 signing."); - } - } - List<ApkSigningBlockUtils.SignerConfig> processedConfigs = new ArrayList<>(); // we have our configs, now touch them up to appropriately cover all SDK levels since APK @@ -384,43 +353,20 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { // this needs to change config.maxSdkVersion = Integer.MAX_VALUE; } else { - if (mRotationTargetsDevRelease && currentMinSdk == mRotationMinSdkVersion) { - // The currentMinSdk is both the SDK version for the active development release - // as well as the most recent released platform. To ensure the v3.0 signer will - // target the released platform, overlap the maxSdkVersion for the v3.0 signer - // with the minSdkVersion of the rotated signer in the v3.1 block - config.maxSdkVersion = currentMinSdk; - } else { - // otherwise, we only want to use this signer up to the minimum platform version - // on which a newer one is acceptable - config.maxSdkVersion = currentMinSdk - 1; - } + // otherwise, we only want to use this signer up to the minimum platform version + // on which a newer one is acceptable + config.maxSdkVersion = currentMinSdk - 1; } config.minSdkVersion = getMinSdkFromV3SignatureAlgorithms(config.signatureAlgorithms); - // Only use a rotated key and signing lineage if the config's max SDK version is greater - // than that requested to support rotation. - if (mSigningCertificateLineage != null - && ((mRotationTargetsDevRelease - ? config.maxSdkVersion > mRotationMinSdkVersion - : config.maxSdkVersion >= mRotationMinSdkVersion))) { + if (mSigningCertificateLineage != null) { config.mSigningCertificateLineage = mSigningCertificateLineage.getSubLineage(config.certificates.get(0)); - if (config.minSdkVersion < mRotationMinSdkVersion) { - config.minSdkVersion = mRotationMinSdkVersion; - } } // we know that this config will be used, so add it to our result, order doesn't matter // at this point (and likely only one will be needed processedConfigs.add(config); currentMinSdk = config.minSdkVersion; - // If the rotation is targeting a development release and this is the v3.1 signer, then - // the minSdkVersion of this signer should equal the maxSdkVersion of the next signer; - // this ensures a package with the minSdkVersion set to the mRotationMinSdkVersion has - // a v3.0 block with the min / max SDK version set to this same minSdkVersion from the - // v3.1 block. - if ((mRotationTargetsDevRelease && currentMinSdk < mMinSdkVersion) - || (!mRotationTargetsDevRelease && currentMinSdk <= mMinSdkVersion) - || currentMinSdk <= AndroidSdkVersion.P) { + if (currentMinSdk <= mMinSdkVersion || currentMinSdk <= AndroidSdkVersion.P) { // this satisfies all we need, stop here break; } @@ -441,42 +387,17 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3)); } - private List<ApkSigningBlockUtils.SignerConfig> processV31SignerConfigs( - List<ApkSigningBlockUtils.SignerConfig> v3SignerConfigs) { - // If the signing key has been rotated, the caller has requested to use the rotated - // signing key starting from an SDK version where v3.1 is supported, and the minimum - // SDK version for the APK is less than the requested rotation minimum, then the APK - // should be signed with both the v3.1 signing scheme with the rotated key, and the v3.0 - // scheme with the original signing key. If the APK's minSdkVersion is >= the requested - // SDK version for rotation then just use the v3.0 signing block for this. - if (!signingLineageHas31Support()) { - return null; - } - - List<ApkSigningBlockUtils.SignerConfig> v31SignerConfigs = new ArrayList<>(); - Iterator<ApkSigningBlockUtils.SignerConfig> v3SignerIterator = - v3SignerConfigs.iterator(); - while (v3SignerIterator.hasNext()) { - ApkSigningBlockUtils.SignerConfig signerConfig = v3SignerIterator.next(); - // All signing configs with a min SDK version that supports v3.1 should be used - // in the v3.1 signing block and removed from the v3.0 block. - if (signerConfig.minSdkVersion >= mRotationMinSdkVersion) { - v31SignerConfigs.add(signerConfig); - v3SignerIterator.remove(); - } - } - return v31SignerConfigs; - } - - private V4SchemeSigner.SignerConfig createV4SignerConfig() throws InvalidKeyException { - List<ApkSigningBlockUtils.SignerConfig> v4Configs = createSigningBlockSignerConfigs(true, + private ApkSigningBlockUtils.SignerConfig createV4SignerConfig() throws InvalidKeyException { + List<ApkSigningBlockUtils.SignerConfig> configs = createSigningBlockSignerConfigs(true, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4); - if (v4Configs.size() != 1) { - // V4 uses signer config to connect back to v3. Use the same filtering logic. - v4Configs = processV3Configs(v4Configs); + if (configs.size() != 1) { + // V4 only uses signer config to connect back to v3. Use the same filtering logic. + configs = processV3Configs(configs); + } + if (configs.size() != 1) { + throw new InvalidKeyException("Only accepting one signer config for V4 Signature."); } - List<ApkSigningBlockUtils.SignerConfig> v41configs = processV31SignerConfigs(v4Configs); - return new V4SchemeSigner.SignerConfig(v4Configs, v41configs); + return configs.get(0); } private ApkSigningBlockUtils.SignerConfig createSourceStampSignerConfig() @@ -1072,29 +993,13 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { invalidateV3Signature(); List<ApkSigningBlockUtils.SignerConfig> v3SignerConfigs = createV3SignerConfigs(apkSigningBlockPaddingSupported); - List<ApkSigningBlockUtils.SignerConfig> v31SignerConfigs = processV31SignerConfigs( - v3SignerConfigs); - if (v31SignerConfigs != null && v31SignerConfigs.size() > 0) { - ApkSigningBlockUtils.SigningSchemeBlockAndDigests - v31SigningSchemeBlockAndDigests = - new V3SchemeSigner.Builder(beforeCentralDir, zipCentralDirectory, eocd, - v31SignerConfigs) - .setRunnablesExecutor(mExecutor) - .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) - .setRotationTargetsDevRelease(mRotationTargetsDevRelease) - .build() - .generateApkSignatureSchemeV3BlockAndDigests(); - signingSchemeBlocks.add(v31SigningSchemeBlockAndDigests.signingSchemeBlock); - } - V3SchemeSigner.Builder builder = new V3SchemeSigner.Builder(beforeCentralDir, - zipCentralDirectory, eocd, v3SignerConfigs) - .setRunnablesExecutor(mExecutor) - .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID); - if (signingLineageHas31Support()) { - builder.setRotationMinSdkVersion(mRotationMinSdkVersion); - } v3SigningSchemeBlockAndDigests = - builder.build().generateApkSignatureSchemeV3BlockAndDigests(); + V3SchemeSigner.generateApkSignatureSchemeV3Block( + mExecutor, + beforeCentralDir, + zipCentralDirectory, + eocd, + v3SignerConfigs); signingSchemeBlocks.add(v3SigningSchemeBlockAndDigests.signingSchemeBlock); } if (isEligibleForSourceStamp()) { @@ -1166,7 +1071,7 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { throw new SignatureException("Missing V4 output file."); } try { - V4SchemeSigner.SignerConfig v4SignerConfig = createV4SignerConfig(); + ApkSigningBlockUtils.SignerConfig v4SignerConfig = createV4SignerConfig(); V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig, outputFile); } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) { if (ignoreFailures) { @@ -1183,7 +1088,7 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { throw new SignatureException("Missing V4 output streams."); } try { - V4SchemeSigner.SignerConfig v4SignerConfig = createV4SignerConfig(); + ApkSigningBlockUtils.SignerConfig v4SignerConfig = createV4SignerConfig(); Pair<V4Signature, byte[]> pair = V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig); pair.getFirst().writeTo(sigOutput); @@ -1725,8 +1630,6 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { private boolean mV1SigningEnabled = true; private boolean mV2SigningEnabled = true; private boolean mV3SigningEnabled = true; - private int mRotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; - private boolean mRotationTargetsDevRelease = false; private boolean mVerityEnabled = false; private boolean mDebuggableApkPermitted = true; private boolean mOtherSignersSignaturesPreserved; @@ -1815,8 +1718,6 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { mStampSignerConfig, mSourceStampSigningCertificateLineage, mMinSdkVersion, - mRotationMinSdkVersion, - mRotationTargetsDevRelease, mV1SigningEnabled, mV2SigningEnabled, mV3SigningEnabled, @@ -1939,49 +1840,5 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { } return this; } - - /** - * Sets the minimum Android platform version (API Level) for which an APK's rotated signing - * key should be used to produce the APK's signature. The original signing key for the APK - * will be used for all previous platform versions. If a rotated key with signing lineage is - * not provided then this method is a noop. - * - * <p>By default, if a signing lineage is specified with {@link - * #setSigningCertificateLineage(SigningCertificateLineage)}, then the APK Signature Scheme - * V3.1 will be used to only apply the rotation on devices running Android T+. - * - * <p><em>Note:</em>Specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result - * in the original V3 signing block being used without platform targeting. - */ - public Builder setMinSdkVersionForRotation(int minSdkVersion) { - // If the provided SDK version does not support v3.1, then use the default SDK version - // with rotation support. - if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { - mRotationMinSdkVersion = MIN_SDK_WITH_V3_SUPPORT; - } else { - mRotationMinSdkVersion = minSdkVersion; - } - return this; - } - - /** - * Sets whether the rotation-min-sdk-version is intended to target a development release; - * this is primarily required after the T SDK is finalized, and an APK needs to target U - * during its development cycle for rotation. - * - * <p>This is only required after the T SDK is finalized since S and earlier releases do - * not know about the V3.1 block ID, but once T is released and work begins on U, U will - * use the SDK version of T during development. Specifying a rotation-min-sdk-version of T's - * SDK version along with setting {@code enabled} to true will allow an APK to use the - * rotated key on a device running U while causing this to be bypassed for T. - * - * <p><em>Note:</em>If the rotation-min-sdk-version is less than or equal to 32 (Android - * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call - * will be a noop. - */ - public Builder setRotationTargetsDevRelease(boolean enabled) { - mRotationTargetsDevRelease = enabled; - return this; - } } } diff --git a/src/main/java/com/android/apksig/SigningCertificateLineage.java b/src/main/java/com/android/apksig/SigningCertificateLineage.java index 43b7f5e..6c505be 100644 --- a/src/main/java/com/android/apksig/SigningCertificateLineage.java +++ b/src/main/java/com/android/apksig/SigningCertificateLineage.java @@ -191,62 +191,41 @@ public class SigningCertificateLineage { */ public static SigningCertificateLineage readFromApkDataSource(DataSource apk) throws IOException, ApkFormatException { - ApkUtils.ZipSections zipSections; - try { - zipSections = ApkUtils.findZipSections(apk); - } catch (ZipFormatException e) { - throw new ApkFormatException(e.getMessage()); - } - - List<SignatureInfo> signatureInfoList = new ArrayList<>(); - try { - ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( - ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31); - signatureInfoList.add( - ApkSigningBlockUtils.findSignature(apk, zipSections, - V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID, result)); - } - catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { - // This could be expected if there's only a V3 signature block. - } + SignatureInfo signatureInfo; try { + ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); - signatureInfoList.add( + signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, - V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID, result)); - } - catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { - // This could be expected if the provided APK is not signed with the v3 signature scheme - } - if (signatureInfoList.isEmpty()) { + V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID, result); + } catch (ZipFormatException e) { + throw new ApkFormatException(e.getMessage()); + } catch (ApkSigningBlockUtils.SignatureNotFoundException e) { throw new IllegalArgumentException( "The provided APK does not contain a valid V3 signature block."); } + // FORMAT: + // * length-prefixed sequence of length-prefixed signers: + // * length-prefixed signed data + // * minSDK + // * maxSDK + // * length-prefixed sequence of length-prefixed signatures + // * length-prefixed public key + ByteBuffer signers = getLengthPrefixedSlice(signatureInfo.signatureBlock); List<SigningCertificateLineage> lineages = new ArrayList<>(1); - for (SignatureInfo signatureInfo : signatureInfoList) { - // FORMAT: - // * length-prefixed sequence of length-prefixed signers: - // * length-prefixed signed data - // * minSDK - // * maxSDK - // * length-prefixed sequence of length-prefixed signatures - // * length-prefixed public key - ByteBuffer signers = getLengthPrefixedSlice(signatureInfo.signatureBlock); - while (signers.hasRemaining()) { - ByteBuffer signer = getLengthPrefixedSlice(signers); - ByteBuffer signedData = getLengthPrefixedSlice(signer); - try { - SigningCertificateLineage lineage = readFromSignedData(signedData); - lineages.add(lineage); - } catch (IllegalArgumentException ignored) { - // The current signer block does not contain a valid lineage, but it is possible - // another block will. - } + while (signers.hasRemaining()) { + ByteBuffer signer = getLengthPrefixedSlice(signers); + ByteBuffer signedData = getLengthPrefixedSlice(signer); + try { + SigningCertificateLineage lineage = readFromSignedData(signedData); + lineages.add(lineage); + } catch (IllegalArgumentException ignored) { + // The current signer block does not contain a valid lineage, but it is possible + // another block will. } } - SigningCertificateLineage result; if (lineages.isEmpty()) { throw new IllegalArgumentException( diff --git a/src/main/java/com/android/apksig/SourceStampVerifier.java b/src/main/java/com/android/apksig/SourceStampVerifier.java index b155341..587cbd3 100644 --- a/src/main/java/com/android/apksig/SourceStampVerifier.java +++ b/src/main/java/com/android/apksig/SourceStampVerifier.java @@ -730,8 +730,6 @@ public class SourceStampVerifier { private final List<ApkVerificationIssue> mErrors = new ArrayList<>(); private final List<ApkVerificationIssue> mWarnings = new ArrayList<>(); - private final long mTimestamp; - /* * Since this utility is intended just to verify the source stamp, and the source stamp * currently only logs warnings to prevent failing the APK signature verification, treat @@ -746,7 +744,6 @@ public class SourceStampVerifier { mCertificateLineage = result.certificateLineage; mErrors.addAll(result.getErrors()); mWarnings.addAll(result.getWarnings()); - mTimestamp = result.timestamp; } /** @@ -797,14 +794,6 @@ public class SourceStampVerifier { public List<ApkVerificationIssue> getWarnings() { return mWarnings; } - - /** - * Returns the epoch timestamp in seconds representing the time this source stamp block - * was signed, or 0 if the timestamp is not available. - */ - public long getTimestampEpochSeconds() { - return mTimestamp; - } } } diff --git a/src/main/java/com/android/apksig/apk/ApkUtils.java b/src/main/java/com/android/apksig/apk/ApkUtils.java index 156ea17..426f0be 100644 --- a/src/main/java/com/android/apksig/apk/ApkUtils.java +++ b/src/main/java/com/android/apksig/apk/ApkUtils.java @@ -97,17 +97,6 @@ public abstract class ApkUtils { } /** - * Updates the length of EOCD comment. - * - * @param zipEndOfCentralDirectory APK's ZIP End of Central Directory record - */ - public static void updateZipEocdCommentLen(ByteBuffer zipEndOfCentralDirectory) { - ByteBuffer eocd = zipEndOfCentralDirectory.slice(); - eocd.order(ByteOrder.LITTLE_ENDIAN); - ZipUtils.updateZipEocdCommentLen(eocd); - } - - /** * Returns the APK Signing Block of the provided {@code apk}. * * @throws ApkFormatException if the APK is not a valid ZIP archive diff --git a/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java b/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java index 12e54d0..e0ea365 100644 --- a/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java +++ b/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java @@ -27,7 +27,6 @@ import java.util.List; */ public class ApkSignerInfo { public int index; - public long timestamp; public List<X509Certificate> certs = new ArrayList<>(); public List<X509Certificate> certificateLineage = new ArrayList<>(); diff --git a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java index 44dcc79..61b7b00 100644 --- a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java +++ b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java @@ -16,7 +16,6 @@ package com.android.apksig.internal.apk; -import static com.android.apksig.Constants.OID_RSA_ENCRYPTION; import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA256; import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA512; import static com.android.apksig.internal.apk.ContentDigestAlgorithm.VERITY_CHUNKED_SHA256; @@ -104,7 +103,6 @@ public class ApkSigningBlockUtils { public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; - public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31; public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4; /** @@ -668,8 +666,7 @@ public class ApkSigningBlockUtils { if ("X.509".equals(publicKey.getFormat())) { encodedPublicKey = publicKey.getEncoded(); // if the key is an RSA key check for a negative modulus - String keyAlgorithm = publicKey.getAlgorithm(); - if ("RSA".equals(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) { + if ("RSA".equals(publicKey.getAlgorithm())) { try { // Parse the encoded public key into the separate elements of the // SubjectPublicKeyInfo to obtain the SubjectPublicKey. diff --git a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java index 2a949ad..465fbb0 100644 --- a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java +++ b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java @@ -24,11 +24,4 @@ public class SourceStampConstants { public static final int V2_SOURCE_STAMP_BLOCK_ID = 0x6dff800d; public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256"; public static final int PROOF_OF_ROTATION_ATTR_ID = 0x9d6303f7; - /** - * The source stamp timestamp attribute value is an 8-byte little-endian encoded long - * representing the epoch time in seconds when the stamp block was signed. The first 8 bytes - * of the attribute value buffer will be used to read the timestamp, and any additional buffer - * space will be ignored. - */ - public static final int STAMP_TIME_ATTR_ID = 0xe43c5946; } diff --git a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java index aace413..9cd7b1f 100644 --- a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java +++ b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java @@ -21,7 +21,6 @@ import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.readLengt import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.toHex; import com.android.apksig.ApkVerificationIssue; -import com.android.apksig.Constants; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSignerInfo; import com.android.apksig.internal.apk.ApkSupportedSignature; @@ -138,13 +137,6 @@ class SourceStampVerifier { for (Map.Entry<Integer, byte[]> signatureSchemeApkDigest : signatureSchemeApkDigests.entrySet()) { - // TODO(b/192301300): Should the new v3.1 be included in the source stamp, or since a - // v3.0 block must always be present with a v3.1 block is it sufficient to just use the - // v3.0 block? - if (signatureSchemeApkDigest.getKey() - == Constants.VERSION_APK_SIGNATURE_SCHEME_V31) { - continue; - } if (!signedSignatureSchemeData.containsKey(signatureSchemeApkDigest.getKey())) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE); return; @@ -318,15 +310,6 @@ class SourceStampVerifier { byte[] value = ByteBufferUtils.toByteArray(attribute); if (id == SourceStampConstants.PROOF_OF_ROTATION_ATTR_ID) { readStampCertificateLineage(value, sourceStampCertificate, result); - } else if (id == SourceStampConstants.STAMP_TIME_ATTR_ID) { - long timestamp = ByteBuffer.wrap(value).order( - ByteOrder.LITTLE_ENDIAN).getLong(); - if (timestamp > 0) { - result.timestamp = timestamp; - } else { - result.addWarning(ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP, - timestamp); - } } else { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE, id); } diff --git a/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java b/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java index 9c00a88..1c1570a 100644 --- a/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java +++ b/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java @@ -35,7 +35,6 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; -import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -203,22 +202,6 @@ public abstract class V2SourceStampSigner { private static Map<Integer, byte[]> generateStampAttributes(SigningCertificateLineage lineage) { HashMap<Integer, byte[]> stampAttributes = new HashMap<>(); - - // Write the current epoch time as the timestamp for the source stamp. - long timestamp = Instant.now().getEpochSecond(); - if (timestamp > 0) { - ByteBuffer attributeBuffer = ByteBuffer.allocate(8); - attributeBuffer.order(ByteOrder.LITTLE_ENDIAN); - attributeBuffer.putLong(timestamp); - stampAttributes.put(SourceStampConstants.STAMP_TIME_ATTR_ID, attributeBuffer.array()); - } else { - // The epoch time should never be <= 0, and since security decisions can potentially - // be made based on the value in the timestamp, throw an Exception to ensure the issues - // with the environment are resolved before allowing the signing. - throw new IllegalStateException( - "Received an invalid value from Instant#getTimestamp: " + timestamp); - } - if (lineage != null) { stampAttributes.put(SourceStampConstants.PROOF_OF_ROTATION_ATTR_ID, lineage.encodeSigningCertificateLineage()); diff --git a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java index ee3c9d8..85301ca 100644 --- a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java +++ b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java @@ -16,7 +16,6 @@ package com.android.apksig.internal.apk.v1; -import static com.android.apksig.Constants.OID_RSA_ENCRYPTION; import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoDigestAlgorithmOid; import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoSignatureAlgorithm; @@ -112,7 +111,7 @@ public abstract class V1SchemeSigner { public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm( PublicKey signingKey, int minSdkVersion) throws InvalidKeyException { String keyAlgorithm = signingKey.getAlgorithm(); - if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals((keyAlgorithm))) { + if ("RSA".equalsIgnoreCase(keyAlgorithm)) { // Prior to API Level 18, only SHA-1 can be used with RSA. if (minSdkVersion < 18) { return DigestAlgorithm.SHA1; diff --git a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java index 0ebef0e..6d7e997 100644 --- a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java +++ b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java @@ -26,7 +26,6 @@ import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.ApkVerifier.IssueWithParams; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; -import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.asn1.Asn1BerParser; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1DecodingException; @@ -55,17 +54,13 @@ import com.android.apksig.zip.ZipFormatException; import java.io.IOException; import java.nio.ByteBuffer; import java.security.InvalidKeyException; -import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Principal; -import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -682,27 +677,7 @@ public abstract class V1SchemeVerifier { String jcaSignatureAlgorithm = getJcaSignatureAlgorithm(digestAlgorithmOid, signatureAlgorithmOid); Signature s = Signature.getInstance(jcaSignatureAlgorithm); - PublicKey publicKey = signingCertificate.getPublicKey(); - try { - s.initVerify(publicKey); - } catch (InvalidKeyException e) { - // An InvalidKeyException could be caught if the PublicKey in the certificate is not - // properly encoded; attempt to resolve any encoding errors, generate a new public - // key, and reattempt the initVerify with the newly encoded key. - try { - byte[] encodedPublicKey = ApkSigningBlockUtils.encodePublicKey(publicKey); - publicKey = KeyFactory.getInstance(publicKey.getAlgorithm()).generatePublic( - new X509EncodedKeySpec(encodedPublicKey)); - } catch (InvalidKeySpecException ikse) { - // If an InvalidKeySpecException is caught then throw the original Exception - // since the key couldn't be properly re-encoded, and the original Exception - // will have more useful debugging info. - throw e; - } - s = Signature.getInstance(jcaSignatureAlgorithm); - s.initVerify(publicKey); - } - + s.initVerify(signingCertificate.getPublicKey()); if (signerInfo.signedAttrs != null) { // Signed attributes present -- verify signature against the ASN.1 DER encoded form // of signed attributes. This verifies integrity of the signature file because diff --git a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java index 6963dd3..3b70aa0 100644 --- a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java +++ b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java @@ -16,40 +16,10 @@ package com.android.apksig.internal.apk.v3; -import com.android.apksig.internal.util.AndroidSdkVersion; - /** Constants used by the V3 Signature Scheme signing and verification. */ public class V3SchemeConstants { private V3SchemeConstants() {} public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; - public static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = 0x1b93ad61; public static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; - - public static final int MIN_SDK_WITH_V3_SUPPORT = AndroidSdkVersion.P; - public static final int MIN_SDK_WITH_V31_SUPPORT = AndroidSdkVersion.T; - /** - * By default, APK signing key rotation will target T, but packages that have previously - * rotated can continue rotating on pre-T by specifying an SDK version <= 32 as the - * --rotation-min-sdk-version parameter when using apksigner or when invoking - * {@link com.android.apksig.ApkSigner.Builder#setMinSdkVersionForRotation(int)}. - */ - public static final int DEFAULT_ROTATION_MIN_SDK_VERSION = AndroidSdkVersion.T; - - /** - * This attribute is intended to be written to the V3.0 signer block as an additional attribute - * whose value is the minimum SDK version supported for rotation by the V3.1 signing block. If - * this value is set to X and a v3.1 signing block does not exist, or the minimum SDK version - * for rotation in the v3.1 signing block is not X, then the APK should be rejected. - */ - public static final int ROTATION_MIN_SDK_VERSION_ATTR_ID = 0x559f8b02; - - /** - * This attribute is written to the V3.1 signer block as an additional attribute to signify that - * the rotation-min-sdk-version is targeting a development release. This is required to support - * testing rotation on new development releases as the previous platform release SDK version - * is used as the development release SDK version until the development release SDK is - * finalized. - */ - public static final int ROTATION_ON_DEV_RELEASE_ATTR_ID = 0xc2a6b3ba; } diff --git a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeSigner.java index ee5d3b4..04260d5 100644 --- a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeSigner.java +++ b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeSigner.java @@ -24,14 +24,12 @@ import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodePublicK import com.android.apksig.SigningCertificateLineage; import com.android.apksig.internal.apk.ApkSigningBlockUtils; -import com.android.apksig.internal.apk.ApkSigningBlockUtils.SigningSchemeBlockAndDigests; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; - import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -46,7 +44,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.OptionalInt; /** * APK Signature Scheme v3 signer. @@ -59,37 +56,13 @@ import java.util.OptionalInt; * SigningCertificateLineage}, which enables an APK to change its signing certificate as long as * it can prove the new siging certificate was signed by the old. */ -public class V3SchemeSigner { +public abstract class V3SchemeSigner { public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; public static final int PROOF_OF_ROTATION_ATTR_ID = V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID; - private final RunnablesExecutor mExecutor; - private final DataSource mBeforeCentralDir; - private final DataSource mCentralDir; - private final DataSource mEocd; - private final List<SignerConfig> mSignerConfigs; - private final int mBlockId; - private final OptionalInt mOptionalRotationMinSdkVersion; - private final boolean mRotationTargetsDevRelease; - - private V3SchemeSigner(DataSource beforeCentralDir, - DataSource centralDir, - DataSource eocd, - List<SignerConfig> signerConfigs, - RunnablesExecutor executor, - int blockId, - OptionalInt optionalRotationMinSdkVersion, - boolean rotationTargetsDevRelease) { - mBeforeCentralDir = beforeCentralDir; - mCentralDir = centralDir; - mEocd = eocd; - mSignerConfigs = signerConfigs; - mExecutor = executor; - mBlockId = blockId; - mOptionalRotationMinSdkVersion = optionalRotationMinSdkVersion; - mRotationTargetsDevRelease = rotationTargetsDevRelease; - } + /** Hidden constructor to prevent instantiation. */ + private V3SchemeSigner() {} /** * Gets the APK Signature Scheme v3 signature algorithms to be used for signing an APK using the @@ -156,18 +129,21 @@ public class V3SchemeSigner { } } - public static SigningSchemeBlockAndDigests generateApkSignatureSchemeV3Block( - RunnablesExecutor executor, - DataSource beforeCentralDir, - DataSource centralDir, - DataSource eocd, - List<SignerConfig> signerConfigs) - throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { - return new V3SchemeSigner.Builder(beforeCentralDir, centralDir, eocd, signerConfigs) - .setRunnablesExecutor(executor) - .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) - .build() - .generateApkSignatureSchemeV3BlockAndDigests(); + public static ApkSigningBlockUtils.SigningSchemeBlockAndDigests + generateApkSignatureSchemeV3Block( + RunnablesExecutor executor, + DataSource beforeCentralDir, + DataSource centralDir, + DataSource eocd, + List<SignerConfig> signerConfigs) + throws IOException, InvalidKeyException, NoSuchAlgorithmException, + SignatureException { + Pair<List<SignerConfig>, Map<ContentDigestAlgorithm, byte[]>> digestInfo = + ApkSigningBlockUtils.computeContentDigests( + executor, beforeCentralDir, centralDir, eocd, signerConfigs); + return new ApkSigningBlockUtils.SigningSchemeBlockAndDigests( + generateApkSignatureSchemeV3Block(digestInfo.getFirst(), digestInfo.getSecond()), + digestInfo.getSecond()); } public static byte[] generateV3SignerAttribute( @@ -186,62 +162,14 @@ public class V3SchemeSigner { return result.array(); } - private static byte[] generateV3RotationMinSdkVersionStrippingProtectionAttribute( - int rotationMinSdkVersion) { - // FORMAT (little endian): - // * length-prefixed bytes: attribute pair - // * uint32: ID - // * bytes: value - int32 representing minimum SDK version for rotation - int payloadSize = 4 + 4 + 4; - ByteBuffer result = ByteBuffer.allocate(payloadSize); - result.order(ByteOrder.LITTLE_ENDIAN); - result.putInt(payloadSize - 4); - result.putInt(V3SchemeConstants.ROTATION_MIN_SDK_VERSION_ATTR_ID); - result.putInt(rotationMinSdkVersion); - return result.array(); - } - - private static byte[] generateV31RotationTargetsDevReleaseAttribute() { - // FORMAT (little endian): - // * length-prefixed bytes: attribute pair - // * uint32: ID - // * bytes: value - No value is used for this attribute - int payloadSize = 4 + 4; - ByteBuffer result = ByteBuffer.allocate(payloadSize); - result.order(ByteOrder.LITTLE_ENDIAN); - result.putInt(payloadSize - 4); - result.putInt(V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID); - return result.array(); - } - - /** - * Generates and returns a new {@link SigningSchemeBlockAndDigests} containing the V3.x - * signing scheme block and digests based on the parameters provided to the {@link Builder}. - * - * @throws IOException if an I/O error occurs - * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is - * missing - * @throws InvalidKeyException if the X.509 encoded form of the public key cannot be obtained - * @throws SignatureException if an error occurs when computing digests or generating - * signatures - */ - public SigningSchemeBlockAndDigests generateApkSignatureSchemeV3BlockAndDigests() - throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { - Pair<List<SignerConfig>, Map<ContentDigestAlgorithm, byte[]>> digestInfo = - ApkSigningBlockUtils.computeContentDigests( - mExecutor, mBeforeCentralDir, mCentralDir, mEocd, mSignerConfigs); - return new SigningSchemeBlockAndDigests( - generateApkSignatureSchemeV3Block(digestInfo.getSecond()), digestInfo.getSecond()); - } - - private Pair<byte[], Integer> generateApkSignatureSchemeV3Block( - Map<ContentDigestAlgorithm, byte[]> contentDigests) + private static Pair<byte[], Integer> generateApkSignatureSchemeV3Block( + List<SignerConfig> signerConfigs, Map<ContentDigestAlgorithm, byte[]> contentDigests) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { // FORMAT: // * length-prefixed sequence of length-prefixed signer blocks. - List<byte[]> signerBlocks = new ArrayList<>(mSignerConfigs.size()); + List<byte[]> signerBlocks = new ArrayList<>(signerConfigs.size()); int signerNumber = 0; - for (SignerConfig signerConfig : mSignerConfigs) { + for (SignerConfig signerConfig : signerConfigs) { signerNumber++; byte[] signerBlock; try { @@ -259,10 +187,10 @@ public class V3SchemeSigner { new byte[][] { encodeAsSequenceOfLengthPrefixedElements(signerBlocks), }), - mBlockId); + V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID); } - private byte[] generateSignerBlock( + private static byte[] generateSignerBlock( SignerConfig signerConfig, Map<ContentDigestAlgorithm, byte[]> contentDigests) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { if (signerConfig.certificates.isEmpty()) { @@ -312,7 +240,7 @@ public class V3SchemeSigner { return encodeSigner(signer); } - private byte[] encodeSigner(V3SignatureSchemeBlock.Signer signer) { + private static byte[] encodeSigner(V3SignatureSchemeBlock.Signer signer) { byte[] signedData = encodeAsLengthPrefixedElement(signer.signedData); byte[] signatures = encodeAsLengthPrefixedElement( @@ -341,7 +269,7 @@ public class V3SchemeSigner { return result.array(); } - private byte[] encodeSignedData(V3SignatureSchemeBlock.SignedData signedData) { + private static byte[] encodeSignedData(V3SignatureSchemeBlock.SignedData signedData) { byte[] digests = encodeAsLengthPrefixedElement( encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( @@ -377,26 +305,11 @@ public class V3SchemeSigner { return result.array(); } - private byte[] generateAdditionalAttributes(SignerConfig signerConfig) { - if (signerConfig.mSigningCertificateLineage != null) { - byte[] lineageAttr = generateV3SignerAttribute(signerConfig.mSigningCertificateLineage); - // If this rotation is not targeting a development release, or if this is not a v3.1 - // signer block then just return the lineage attribute. - if (!mRotationTargetsDevRelease - || mBlockId != V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) { - return lineageAttr; - } - byte[] devReleaseRotationAttr = generateV31RotationTargetsDevReleaseAttribute(); - byte[] attributes = new byte[lineageAttr.length + devReleaseRotationAttr.length]; - System.arraycopy(lineageAttr, 0, attributes, 0, lineageAttr.length); - System.arraycopy(devReleaseRotationAttr, 0, attributes, lineageAttr.length, - devReleaseRotationAttr.length); - return attributes; - } else if (mOptionalRotationMinSdkVersion.isPresent()) { - return generateV3RotationMinSdkVersionStrippingProtectionAttribute( - mOptionalRotationMinSdkVersion.getAsInt()); + private static byte[] generateAdditionalAttributes(SignerConfig signerConfig) { + if (signerConfig.mSigningCertificateLineage == null) { + return new byte[0]; } - return new byte[0]; + return generateV3SignerAttribute(signerConfig.mSigningCertificateLineage); } private static final class V3SignatureSchemeBlock { @@ -416,97 +329,4 @@ public class V3SchemeSigner { public byte[] additionalAttributes; } } - - /** Builder of {@link V3SchemeSigner} instances. */ - public static class Builder { - private final DataSource mBeforeCentralDir; - private final DataSource mCentralDir; - private final DataSource mEocd; - private final List<SignerConfig> mSignerConfigs; - - private RunnablesExecutor mExecutor = RunnablesExecutor.MULTI_THREADED; - private int mBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; - private OptionalInt mOptionalRotationMinSdkVersion = OptionalInt.empty(); - private boolean mRotationTargetsDevRelease = false; - - /** - * Instantiates a new {@code Builder} with an APK's {@code beforeCentralDir}, {@code - * centralDir}, and {@code eocd}, along with a {@link List} of {@code signerConfigs} to - * be used to sign the APK. - */ - public Builder(DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, - List<SignerConfig> signerConfigs) { - mBeforeCentralDir = beforeCentralDir; - mCentralDir = centralDir; - mEocd = eocd; - mSignerConfigs = signerConfigs; - } - - /** - * Sets the {@link RunnablesExecutor} to be used when computing the APK's content digests. - */ - public Builder setRunnablesExecutor(RunnablesExecutor executor) { - mExecutor = executor; - return this; - } - - /** - * Sets the {@code blockId} to be used for the V3 signature block. - * - * <p>This {@code V3SchemeSigner} currently supports the block IDs for the {@link - * V3SchemeConstants#APK_SIGNATURE_SCHEME_V3_BLOCK_ID v3.0} and {@link - * V3SchemeConstants#APK_SIGNATURE_SCHEME_V31_BLOCK_ID v3.1} signature schemes. - */ - public Builder setBlockId(int blockId) { - mBlockId = blockId; - return this; - } - - /** - * Sets the {@code rotationMinSdkVersion} to be written as an additional attribute in each - * signer's block. - * - * <p>This value provides stripping protection to ensure a v3.1 signing block with rotation - * is not modified or removed from the APK's signature block. - */ - public Builder setRotationMinSdkVersion(int rotationMinSdkVersion) { - mOptionalRotationMinSdkVersion = OptionalInt.of(rotationMinSdkVersion); - return this; - } - - /** - * Sets whether the minimum SDK version of a signer is intended to target a development - * release; this is primarily required after the T SDK is finalized, and an APK needs to - * target U during its development cycle for rotation. - * - * <p>This is only required after the T SDK is finalized since S and earlier releases do - * not know about the V3.1 block ID, but once T is released and work begins on U, U will - * use the SDK version of T during development. A signer with a minimum SDK version of T's - * SDK version along with setting {@code enabled} to true will allow an APK to use the - * rotated key on a device running U while causing this to be bypassed for T. - * - * <p><em>Note:</em>If the rotation-min-sdk-version is less than or equal to 32 (Android - * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call - * will be a noop. - */ - public Builder setRotationTargetsDevRelease(boolean enabled) { - mRotationTargetsDevRelease = enabled; - return this; - } - - /** - * Returns a new {@link V3SchemeSigner} built with the configuration provided to this - * {@code Builder}. - */ - public V3SchemeSigner build() { - return new V3SchemeSigner(mBeforeCentralDir, - mCentralDir, - mEocd, - mSignerConfigs, - mExecutor, - mBlockId, - mOptionalRotationMinSdkVersion, - mRotationTargetsDevRelease); - } - } } diff --git a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java index 956027f..ea93194 100644 --- a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java +++ b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java @@ -28,6 +28,7 @@ import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignatureNotFoundExc 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.util.AndroidSdkVersion; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.util.X509CertificateUtils; @@ -37,7 +38,6 @@ import com.android.apksig.util.RunnablesExecutor; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -54,7 +54,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.OptionalInt; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -68,42 +67,9 @@ import java.util.TreeMap; * * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature Scheme v2</a> */ -public class V3SchemeVerifier { - private final RunnablesExecutor mExecutor; - private final DataSource mApk; - private final ApkUtils.ZipSections mZipSections; - private final ApkSigningBlockUtils.Result mResult; - private final Set<ContentDigestAlgorithm> mContentDigestsToVerify; - private final int mMinSdkVersion; - private final int mMaxSdkVersion; - private final int mBlockId; - private final OptionalInt mOptionalRotationMinSdkVersion; - private final boolean mFullVerification; - - private ByteBuffer mApkSignatureSchemeV3Block; - - private V3SchemeVerifier( - RunnablesExecutor executor, - DataSource apk, - ApkUtils.ZipSections zipSections, - Set<ContentDigestAlgorithm> contentDigestsToVerify, - ApkSigningBlockUtils.Result result, - int minSdkVersion, - int maxSdkVersion, - int blockId, - OptionalInt optionalRotationMinSdkVersion, - boolean fullVerification) { - mExecutor = executor; - mApk = apk; - mZipSections = zipSections; - mContentDigestsToVerify = contentDigestsToVerify; - mResult = result; - mMinSdkVersion = minSdkVersion; - mMaxSdkVersion = maxSdkVersion; - mBlockId = blockId; - mOptionalRotationMinSdkVersion = optionalRotationMinSdkVersion; - mFullVerification = fullVerification; - } +public abstract class V3SchemeVerifier { + /** Hidden constructor to prevent instantiation. */ + private V3SchemeVerifier() {} /** * Verifies the provided APK's APK Signature Scheme v3 signatures and returns the result of @@ -118,10 +84,7 @@ public class V3SchemeVerifier { * this method returns a result with one or more errors and whose * {@code Result.verified == false}, or this method throws an exception. * - * <p>This method only verifies the v3.0 signing block without platform targeted rotation from - * a v3.1 signing block. To verify a v3.1 signing block, or a v3.0 signing block in the presence - * of a v3.1 block, configure a new {@link V3SchemeVerifier} using the {@code Builder}. - * + * @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 SignatureNotFoundException if no APK Signature Scheme v3 @@ -135,11 +98,34 @@ public class V3SchemeVerifier { int minSdkVersion, int maxSdkVersion) throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { - return new V3SchemeVerifier.Builder(apk, zipSections, minSdkVersion, maxSdkVersion) - .setRunnablesExecutor(executor) - .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) - .build() - .verify(); + ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( + ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); + SignatureInfo signatureInfo = + ApkSigningBlockUtils.findSignature(apk, zipSections, + V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID, result); + + DataSource beforeApkSigningBlock = apk.slice(0, signatureInfo.apkSigningBlockOffset); + DataSource centralDir = + apk.slice( + signatureInfo.centralDirOffset, + signatureInfo.eocdOffset - signatureInfo.centralDirOffset); + ByteBuffer eocd = signatureInfo.eocd; + + // v3 didn't exist prior to P, so make sure that we're only judging v3 on its supported + // platforms + if (minSdkVersion < AndroidSdkVersion.P) { + minSdkVersion = AndroidSdkVersion.P; + } + + verify(executor, + beforeApkSigningBlock, + signatureInfo.signatureBlock, + centralDir, + eocd, + minSdkVersion, + maxSdkVersion, + result); + return result; } /** @@ -148,40 +134,33 @@ public class V3SchemeVerifier { * {@code result}. See {@link #verify(RunnablesExecutor, DataSource, ApkUtils.ZipSections, int, * int)} for more information about the contract of this method. * - * @return {@link ApkSigningBlockUtils.Result} populated with interesting information about the - * APK, such as information about signers, and verification errors and warnings + * @param result result populated by this method with interesting information about the APK, + * such as information about signers, and verification errors and warnings. */ - public ApkSigningBlockUtils.Result verify() - throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { - if (mApk == null || mZipSections == null) { - throw new IllegalStateException( - "A non-null apk and zip sections must be specified to verify an APK's v3 " - + "signatures"); - } - SignatureInfo signatureInfo = - ApkSigningBlockUtils.findSignature(mApk, mZipSections, mBlockId, mResult); - mApkSignatureSchemeV3Block = signatureInfo.signatureBlock; - - DataSource beforeApkSigningBlock = mApk.slice(0, signatureInfo.apkSigningBlockOffset); - DataSource centralDir = - mApk.slice( - signatureInfo.centralDirOffset, - signatureInfo.eocdOffset - signatureInfo.centralDirOffset); - ByteBuffer eocd = signatureInfo.eocd; - - parseSigners(); + private static void verify( + RunnablesExecutor executor, + DataSource beforeApkSigningBlock, + ByteBuffer apkSignatureSchemeV3Block, + DataSource centralDir, + ByteBuffer eocd, + int minSdkVersion, + int maxSdkVersion, + ApkSigningBlockUtils.Result result) + throws IOException, NoSuchAlgorithmException { + Set<ContentDigestAlgorithm> contentDigestsToVerify = new HashSet<>(1); + parseSigners(apkSignatureSchemeV3Block, contentDigestsToVerify, result); - if (mResult.containsErrors()) { - return mResult; + if (result.containsErrors()) { + return; } - ApkSigningBlockUtils.verifyIntegrity(mExecutor, beforeApkSigningBlock, centralDir, eocd, - mContentDigestsToVerify, mResult); + ApkSigningBlockUtils.verifyIntegrity( + executor, beforeApkSigningBlock, centralDir, eocd, contentDigestsToVerify, result); // make sure that the v3 signers cover the entire targeted sdk version ranges and that the // longest SigningCertificateHistory, if present, corresponds to the newest platform // versions SortedMap<Integer, ApkSigningBlockUtils.Result.SignerInfo> sortedSigners = new TreeMap<>(); - for (ApkSigningBlockUtils.Result.SignerInfo signer : mResult.signers) { + for (ApkSigningBlockUtils.Result.SignerInfo signer : result.signers) { sortedSigners.put(signer.minSdkVersion, signer); } @@ -191,7 +170,7 @@ public class V3SchemeVerifier { int lastLineageSize = 0; // while we're iterating through the signers, build up the list of lineages - List<SigningCertificateLineage> lineages = new ArrayList<>(mResult.signers.size()); + List<SigningCertificateLineage> lineages = new ArrayList<>(result.signers.size()); for (ApkSigningBlockUtils.Result.SignerInfo signer : sortedSigners.values()) { int currentMin = signer.minSdkVersion; @@ -201,7 +180,7 @@ public class V3SchemeVerifier { firstMin = currentMin; } else { if (currentMin != lastMax + 1) { - mResult.addError(Issue.V3_INCONSISTENT_SDK_VERSIONS); + result.addError(Issue.V3_INCONSISTENT_SDK_VERSIONS); break; } } @@ -211,7 +190,7 @@ public class V3SchemeVerifier { if (signer.signingCertificateLineage != null) { int currLineageSize = signer.signingCertificateLineage.size(); if (currLineageSize < lastLineageSize) { - mResult.addError(Issue.V3_INCONSISTENT_LINEAGES); + result.addError(Issue.V3_INCONSISTENT_LINEAGES); break; } lastLineageSize = currLineageSize; @@ -219,24 +198,20 @@ public class V3SchemeVerifier { } } - // make sure we support our desired sdk ranges; if rotation is present in a v3.1 block - // then the max level only needs to support up to that sdk version for rotation. - if (firstMin > mMinSdkVersion - || lastMax < (mOptionalRotationMinSdkVersion.isPresent() - ? mOptionalRotationMinSdkVersion.getAsInt() - 1 : mMaxSdkVersion)) { - mResult.addError(Issue.V3_MISSING_SDK_VERSIONS, firstMin, lastMax); + // make sure we support our desired sdk ranges + if (firstMin > minSdkVersion || lastMax < maxSdkVersion) { + result.addError(Issue.V3_MISSING_SDK_VERSIONS, firstMin, lastMax); } try { - mResult.signingCertificateLineage = + result.signingCertificateLineage = SigningCertificateLineage.consolidateLineages(lineages); } catch (IllegalArgumentException e) { - mResult.addError(Issue.V3_INCONSISTENT_LINEAGES); + result.addError(Issue.V3_INCONSISTENT_LINEAGES); } - if (!mResult.containsErrors()) { - mResult.verified = true; + if (!result.containsErrors()) { + result.verified = true; } - return mResult; } /** @@ -255,49 +230,16 @@ public class V3SchemeVerifier { ByteBuffer apkSignatureSchemeV3Block, Set<ContentDigestAlgorithm> contentDigestsToVerify, ApkSigningBlockUtils.Result result) throws NoSuchAlgorithmException { - try { - new V3SchemeVerifier.Builder(apkSignatureSchemeV3Block) - .setResult(result) - .setContentDigestsToVerify(contentDigestsToVerify) - .setFullVerification(false) - .build() - .parseSigners(); - } catch (IOException | SignatureNotFoundException e) { - // This should never occur since the apkSignatureSchemeV3Block was already provided. - throw new IllegalStateException("An exception was encountered when attempting to parse" - + " the signers from the provided APK Signature Scheme v3 block", e); - } - } - - /** - * Parses each signer in the APK Signature Scheme v3 block and populates corresponding - * {@link ApkSigningBlockUtils.Result.SignerInfo} instances in the - * returned {@link ApkSigningBlockUtils.Result}. - * - * <p>This verifies signatures over {@code signed-data} block contained in each signer block. - * However, this does not verify the integrity of the rest of the APK but rather simply reports - * the expected digests of the rest of the APK (see {@link Builder#setContentDigestsToVerify}). - * - * <p>This method adds one or more errors to the returned {@code Result} if a verification error - * is encountered when parsing the signers. - */ - public ApkSigningBlockUtils.Result parseSigners() - throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { ByteBuffer signers; try { - if (mApkSignatureSchemeV3Block == null) { - SignatureInfo signatureInfo = - ApkSigningBlockUtils.findSignature(mApk, mZipSections, mBlockId, mResult); - mApkSignatureSchemeV3Block = signatureInfo.signatureBlock; - } - signers = getLengthPrefixedSlice(mApkSignatureSchemeV3Block); + signers = getLengthPrefixedSlice(apkSignatureSchemeV3Block); } catch (ApkFormatException e) { - mResult.addError(Issue.V3_SIG_MALFORMED_SIGNERS); - return mResult; + result.addError(Issue.V3_SIG_MALFORMED_SIGNERS); + return; } if (!signers.hasRemaining()) { - mResult.addError(Issue.V3_SIG_NO_SIGNERS); - return mResult; + result.addError(Issue.V3_SIG_NO_SIGNERS); + return; } CertificateFactory certFactory; @@ -313,16 +255,15 @@ public class V3SchemeVerifier { ApkSigningBlockUtils.Result.SignerInfo signerInfo = new ApkSigningBlockUtils.Result.SignerInfo(); signerInfo.index = signerIndex; - mResult.signers.add(signerInfo); + result.signers.add(signerInfo); try { ByteBuffer signer = getLengthPrefixedSlice(signers); - parseSigner(signer, certFactory, signerInfo); + parseSigner(signer, certFactory, signerInfo, contentDigestsToVerify); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addError(Issue.V3_SIG_MALFORMED_SIGNER); - return mResult; + return; } } - return mResult; } /** @@ -337,9 +278,12 @@ public class V3SchemeVerifier { * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ - private void parseSigner(ByteBuffer signerBlock, CertificateFactory certFactory, - ApkSigningBlockUtils.Result.SignerInfo result) - throws ApkFormatException, NoSuchAlgorithmException { + private static void parseSigner( + ByteBuffer signerBlock, + CertificateFactory certFactory, + ApkSigningBlockUtils.Result.SignerInfo result, + Set<ContentDigestAlgorithm> contentDigestsToVerify) + throws ApkFormatException, NoSuchAlgorithmException { ByteBuffer signedData = getLengthPrefixedSlice(signerBlock); byte[] signedDataBytes = new byte[signedData.remaining()]; signedData.get(signedDataBytes); @@ -428,7 +372,7 @@ public class V3SchemeVerifier { return; } result.verifiedSignatures.put(signatureAlgorithm, sigBytes); - mContentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm()); + contentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm()); } catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) { result.addError(Issue.V3_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e); @@ -538,7 +482,6 @@ public class V3SchemeVerifier { // Parse the additional attributes block. int additionalAttributeCount = 0; - boolean rotationAttrFound = false; while (additionalAttributes.hasRemaining()) { additionalAttributeCount++; try { @@ -566,31 +509,6 @@ public class V3SchemeVerifier { } catch (Exception e) { result.addError(Issue.V3_SIG_MALFORMED_LINEAGE); } - } else if (id == V3SchemeConstants.ROTATION_MIN_SDK_VERSION_ATTR_ID) { - rotationAttrFound = true; - // API targeting for rotation was added with V3.1; if the maxSdkVersion - // does not support v3.1 then ignore this attribute. - if (mMaxSdkVersion >= V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - && mFullVerification) { - int attrRotationMinSdkVersion = ByteBuffer.wrap(value) - .order(ByteOrder.LITTLE_ENDIAN).getInt(); - if (mOptionalRotationMinSdkVersion.isPresent()) { - int rotationMinSdkVersion = mOptionalRotationMinSdkVersion.getAsInt(); - if (attrRotationMinSdkVersion != rotationMinSdkVersion) { - result.addError(Issue.V31_ROTATION_MIN_SDK_MISMATCH, - attrRotationMinSdkVersion, rotationMinSdkVersion); - } - } else { - result.addError(Issue.V31_BLOCK_MISSING, attrRotationMinSdkVersion); - } - } - } else if (id == V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID) { - // This attribute should only be used by a v3.1 signer to indicate rotation - // is targeting the development release that is using the SDK version of the - // previously released platform version. - if (mBlockId != V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) { - result.addWarning(Issue.V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER); - } } else { result.addWarning(Issue.V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE, id); } @@ -600,168 +518,5 @@ public class V3SchemeVerifier { return; } } - if (mFullVerification && mOptionalRotationMinSdkVersion.isPresent() && !rotationAttrFound) { - result.addWarning(Issue.V31_ROTATION_MIN_SDK_ATTR_MISSING, - mOptionalRotationMinSdkVersion.getAsInt()); - } - } - - /** Builder of {@link V3SchemeVerifier} instances. */ - public static class Builder { - private RunnablesExecutor mExecutor = RunnablesExecutor.SINGLE_THREADED; - private DataSource mApk; - private ApkUtils.ZipSections mZipSections; - private ByteBuffer mApkSignatureSchemeV3Block; - private Set<ContentDigestAlgorithm> mContentDigestsToVerify; - private ApkSigningBlockUtils.Result mResult; - private int mMinSdkVersion; - private int mMaxSdkVersion; - private int mBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; - private boolean mFullVerification = true; - private OptionalInt mOptionalRotationMinSdkVersion = OptionalInt.empty(); - - /** - * Instantiates a new {@code Builder} for a {@code V3SchemeVerifier} that can be used to - * verify the V3 signing block of the provided {@code apk} with the specified {@code - * zipSections} over the range from {@code minSdkVersion} to {@code maxSdkVersion}. - */ - public Builder(DataSource apk, ApkUtils.ZipSections zipSections, int minSdkVersion, - int maxSdkVersion) { - mApk = apk; - mZipSections = zipSections; - mMinSdkVersion = minSdkVersion; - mMaxSdkVersion = maxSdkVersion; - } - - /** - * Instantiates a new {@code Builder} for a {@code V3SchemeVerifier} that can be used to - * parse the {@link ApkSigningBlockUtils.Result.SignerInfo} instances from the {@code - * apkSignatureSchemeV3Block}. - * - * <note>Full verification of the v3 signature is not possible when instantiating a new - * {@code V3SchemeVerifier} with this method.</note> - */ - public Builder(ByteBuffer apkSignatureSchemeV3Block) { - mApkSignatureSchemeV3Block = apkSignatureSchemeV3Block; - } - - /** - * Sets the {@link RunnablesExecutor} to be used when verifying the APK's content digests. - */ - public Builder setRunnablesExecutor(RunnablesExecutor executor) { - mExecutor = executor; - return this; - } - - /** - * Sets the V3 {code blockId} to be verified in the provided APK. - * - * <p>This {@code V3SchemeVerifier} currently supports the block IDs for the {@link - * V3SchemeConstants#APK_SIGNATURE_SCHEME_V3_BLOCK_ID v3.0} and {@link - * V3SchemeConstants#APK_SIGNATURE_SCHEME_V31_BLOCK_ID v3.1} signature schemes. - */ - public Builder setBlockId(int blockId) { - mBlockId = blockId; - return this; - } - - /** - * Sets the {@code rotationMinSdkVersion} to be verified in the v3.0 signer's additional - * attribute. - * - * <p>This value can be obtained from the signers returned when verifying the v3.1 signing - * block of an APK; in the case of multiple signers targeting different SDK versions in the - * v3.1 signing block, the minimum SDK version from all the signers should be used. - */ - public Builder setRotationMinSdkVersion(int rotationMinSdkVersion) { - mOptionalRotationMinSdkVersion = OptionalInt.of(rotationMinSdkVersion); - return this; - } - - /** - * Sets the {@code result} instance to be used when returning verification results. - * - * <p>This method can be used when the caller already has a {@link - * ApkSigningBlockUtils.Result} and wants to store the verification results in this - * instance. - */ - public Builder setResult(ApkSigningBlockUtils.Result result) { - mResult = result; - return this; - } - - /** - * Sets the instance to be used to store the {@code contentDigestsToVerify}. - * - * <p>This method can be used when the caller needs access to the {@code - * contentDigestsToVerify} computed by this {@code V3SchemeVerifier}. - */ - public Builder setContentDigestsToVerify( - Set<ContentDigestAlgorithm> contentDigestsToVerify) { - mContentDigestsToVerify = contentDigestsToVerify; - return this; - } - - /** - * Sets whether full verification should be performed by the {@code V3SchemeVerifier} built - * from this instance. - * - * <note>{@link #verify()} will always verify the content digests for the APK, but this - * allows verification of the rotation minimum SDK version stripping attribute to be skipped - * for scenarios where this value may not have been parsed from a V3.1 signing block (such - * as when only {@link #parseSigners()} will be invoked.</note> - */ - public Builder setFullVerification(boolean fullVerification) { - mFullVerification = fullVerification; - return this; - } - - /** - * Returns a new {@link V3SchemeVerifier} built with the configuration provided to this - * {@code Builder}. - */ - public V3SchemeVerifier build() { - int sigSchemeVersion; - switch (mBlockId) { - case V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID: - sigSchemeVersion = ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; - mMinSdkVersion = Math.max(mMinSdkVersion, - V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT); - break; - case V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID: - sigSchemeVersion = ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31; - // V3.1 supports targeting an SDK version later than that of the initial release - // in which it is supported; allow any range for V3.1 as long as V3.0 covers the - // rest of the range. - mMinSdkVersion = mMaxSdkVersion; - break; - default: - throw new IllegalArgumentException( - String.format("Unsupported APK Signature Scheme V3 block ID: 0x%08x", - mBlockId)); - } - if (mResult == null) { - mResult = new ApkSigningBlockUtils.Result(sigSchemeVersion); - } - if (mContentDigestsToVerify == null) { - mContentDigestsToVerify = new HashSet<>(1); - } - - V3SchemeVerifier verifier = new V3SchemeVerifier( - mExecutor, - mApk, - mZipSections, - mContentDigestsToVerify, - mResult, - mMinSdkVersion, - mMaxSdkVersion, - mBlockId, - mOptionalRotationMinSdkVersion, - mFullVerification); - if (mApkSignatureSchemeV3Block != null) { - verifier.mApkSignatureSchemeV3Block = mApkSignatureSchemeV3Block; - } - return verifier; - } } } diff --git a/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeSigner.java index 2f9ecb3..74aa629 100644 --- a/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeSigner.java +++ b/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeSigner.java @@ -22,11 +22,11 @@ import static com.android.apksig.internal.apk.v3.V3SchemeConstants.APK_SIGNATURE import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; +import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; 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.v2.V2SchemeVerifier; -import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.apk.v3.V3SchemeVerifier; import com.android.apksig.internal.util.Pair; @@ -43,7 +43,6 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -62,6 +61,7 @@ import java.util.Set; * </p> * (optional) verityTree: integer size prepended bytes of the verity hash tree. * <p> + * TODO(schfan): Add v4 unit tests */ public abstract class V4SchemeSigner { /** @@ -70,23 +70,6 @@ public abstract class V4SchemeSigner { private V4SchemeSigner() { } - public static class SignerConfig { - final public ApkSigningBlockUtils.SignerConfig v4Config; - final public ApkSigningBlockUtils.SignerConfig v41Config; - - public SignerConfig(List<ApkSigningBlockUtils.SignerConfig> v4Configs, - List<ApkSigningBlockUtils.SignerConfig> v41Configs) throws InvalidKeyException { - if (v4Configs == null || v4Configs.size() != 1) { - throw new InvalidKeyException("Only accepting one signer config for V4 Signature."); - } - if (v41Configs != null && v41Configs.size() != 1) { - throw new InvalidKeyException("Only accepting one signer config for V4.1 Signature."); - } - this.v4Config = v4Configs.get(0); - this.v41Config = v41Configs != null ? v41Configs.get(0) : null; - } - } - /** * Based on a public key, return a signing algorithm that supports verity. */ @@ -166,10 +149,10 @@ public abstract class V4SchemeSigner { return Pair.of(signature, tree); } - private static V4Signature.SigningInfo generateSigningInfo( - ApkSigningBlockUtils.SignerConfig signerConfig, + private static V4Signature generateSignature( + SignerConfig signerConfig, V4Signature.HashingInfo hashingInfo, - byte[] apkDigest, byte[] additionalData, long fileSize) + byte[] apkDigest, byte[] additionaData, long fileSize) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, CertificateEncodingException { if (signerConfig.certificates.isEmpty()) { @@ -186,7 +169,7 @@ public abstract class V4SchemeSigner { final byte[] encodedCertificate = encodedCertificates.get(0); final V4Signature.SigningInfo signingInfoNoSignature = new V4Signature.SigningInfo(apkDigest, - encodedCertificate, additionalData, publicKey.getEncoded(), -1, null); + encodedCertificate, additionaData, publicKey.getEncoded(), -1, null); final byte[] data = V4Signature.getSignedData(fileSize, hashingInfo, signingInfoNoSignature); @@ -201,33 +184,12 @@ public abstract class V4SchemeSigner { final int signatureAlgorithmId = signatures.get(0).getFirst(); final byte[] signature = signatures.get(0).getSecond(); - return new V4Signature.SigningInfo(apkDigest, - encodedCertificate, additionalData, publicKey.getEncoded(), signatureAlgorithmId, + final V4Signature.SigningInfo signingInfo = new V4Signature.SigningInfo(apkDigest, + encodedCertificate, additionaData, publicKey.getEncoded(), signatureAlgorithmId, signature); - } - - private static V4Signature generateSignature( - SignerConfig signerConfig, - V4Signature.HashingInfo hashingInfo, - byte[] apkDigest, byte[] additionalData, long fileSize) - throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, - CertificateEncodingException { - final V4Signature.SigningInfo signingInfo = generateSigningInfo(signerConfig.v4Config, - hashingInfo, apkDigest, additionalData, fileSize); - - final V4Signature.SigningInfos signingInfos; - if (signerConfig.v41Config != null) { - final V4Signature.SigningInfoBlock extSigningBlock = new V4Signature.SigningInfoBlock( - V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID, - generateSigningInfo(signerConfig.v41Config, hashingInfo, apkDigest, - additionalData, fileSize).toByteArray()); - signingInfos = new V4Signature.SigningInfos(signingInfo, extSigningBlock); - } else { - signingInfos = new V4Signature.SigningInfos(signingInfo); - } return new V4Signature(V4Signature.CURRENT_VERSION, hashingInfo.toByteArray(), - signingInfos.toByteArray()); + signingInfo.toByteArray()); } // Get digest by parsing the V2/V3-signed apk and choosing the first digest of supported type. diff --git a/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeVerifier.java index c0a9013..a6cd9db 100644 --- a/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeVerifier.java +++ b/src/main/java/com/android/apksig/internal/apk/v4/V4SchemeVerifier.java @@ -90,37 +90,20 @@ public abstract class V4SchemeVerifier { V4Signature.HashingInfo hashingInfo = V4Signature.HashingInfo.fromByteArray( signature.hashingInfo); + V4Signature.SigningInfo signingInfo = V4Signature.SigningInfo.fromByteArray( + signature.signingInfo); - V4Signature.SigningInfos signingInfos = V4Signature.SigningInfos.fromByteArray( - signature.signingInfos); + final byte[] signedData = V4Signature.getSignedData(apk.size(), hashingInfo, signingInfo); - final ApkSigningBlockUtils.Result.SignerInfo signerInfo; - - // Verify the primary signature over signedData. - { - V4Signature.SigningInfo signingInfo = signingInfos.signingInfo; - final byte[] signedData = V4Signature.getSignedData(apk.size(), hashingInfo, - signingInfo); - signerInfo = parseAndVerifySignatureBlock(signingInfo, signedData); - result.signers.add(signerInfo); - if (result.containsErrors()) { - return result; - } - } - - // Verify all subsequent signatures. - for (V4Signature.SigningInfoBlock signingInfoBlock : signingInfos.signingInfoBlocks) { - V4Signature.SigningInfo signingInfo = V4Signature.SigningInfo.fromByteArray( - signingInfoBlock.signingInfo); - final byte[] signedData = V4Signature.getSignedData(apk.size(), hashingInfo, - signingInfo); - result.signers.add(parseAndVerifySignatureBlock(signingInfo, signedData)); - if (result.containsErrors()) { - return result; - } + // First, verify the signature over signedData. + ApkSigningBlockUtils.Result.SignerInfo signerInfo = parseAndVerifySignatureBlock( + signingInfo, signedData); + result.signers.add(signerInfo); + if (result.containsErrors()) { + return result; } - // Check if the root hash and the tree are correct. + // Second, check if the root hash and the tree are correct. verifyRootHashAndTree(apk, signerInfo, hashingInfo.rawRootHash, tree); if (!result.containsErrors()) { result.verified = true; diff --git a/src/main/java/com/android/apksig/internal/apk/v4/V4Signature.java b/src/main/java/com/android/apksig/internal/apk/v4/V4Signature.java index 1eac5a2..deabe12 100644 --- a/src/main/java/com/android/apksig/internal/apk/v4/V4Signature.java +++ b/src/main/java/com/android/apksig/internal/apk/v4/V4Signature.java @@ -22,8 +22,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; public class V4Signature { public static final int CURRENT_VERSION = 2; @@ -31,8 +29,6 @@ public class V4Signature { public static final int HASHING_ALGORITHM_SHA256 = 1; public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12; - public static final int MAX_SIGNING_INFOS_SIZE = 7168; - public static class HashingInfo { public final int hashAlgorithm; // only 1 == SHA256 supported public final byte log2BlockSize; // only 12 (block size 4096) supported now @@ -86,10 +82,7 @@ public class V4Signature { } static SigningInfo fromByteArray(byte[] bytes) throws IOException { - return fromByteBuffer(ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)); - } - - static SigningInfo fromByteBuffer(ByteBuffer buffer) throws IOException { + ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); byte[] apkDigest = readBytes(buffer); byte[] certificate = readBytes(buffer); byte[] additionalData = readBytes(buffer); @@ -115,93 +108,14 @@ public class V4Signature { } } - public static class SigningInfoBlock { - public final int blockId; - public final byte[] signingInfo; - - public SigningInfoBlock(int blockId, byte[] signingInfo) { - this.blockId = blockId; - this.signingInfo = signingInfo; - } - - static SigningInfoBlock fromByteBuffer(ByteBuffer buffer) throws IOException { - int blockId = buffer.getInt(); - byte[] signingInfo = readBytes(buffer); - return new SigningInfoBlock(blockId, signingInfo); - } - - byte[] toByteArray() { - final int size = 4/*blockId*/ + bytesSize(this.signingInfo); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer.putInt(this.blockId); - writeBytes(buffer, this.signingInfo); - return buffer.array(); - } - } - - public static class SigningInfos { - public final SigningInfo signingInfo; - public final SigningInfoBlock[] signingInfoBlocks; - - public SigningInfos(SigningInfo signingInfo) { - this.signingInfo = signingInfo; - this.signingInfoBlocks = new SigningInfoBlock[0]; - } - - public SigningInfos(SigningInfo signingInfo, SigningInfoBlock... signingInfoBlocks) { - this.signingInfo = signingInfo; - this.signingInfoBlocks = signingInfoBlocks; - } - - public static SigningInfos fromByteArray(byte[] bytes) throws IOException { - ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); - SigningInfo signingInfo = SigningInfo.fromByteBuffer(buffer); - if (!buffer.hasRemaining()) { - return new SigningInfos(signingInfo); - } - ArrayList<SigningInfoBlock> signingInfoBlocks = new ArrayList<>(1); - while (buffer.hasRemaining()) { - signingInfoBlocks.add(SigningInfoBlock.fromByteBuffer(buffer)); - } - return new SigningInfos(signingInfo, - signingInfoBlocks.toArray(new SigningInfoBlock[signingInfoBlocks.size()])); - } - - byte[] toByteArray() { - byte[][] arrays = new byte[1 + this.signingInfoBlocks.length][]; - arrays[0] = this.signingInfo.toByteArray(); - int size = arrays[0].length; - for (int i = 0, isize = this.signingInfoBlocks.length; i < isize; ++i) { - arrays[i + 1] = this.signingInfoBlocks[i].toByteArray(); - size += arrays[i + 1].length; - } - if (size > MAX_SIGNING_INFOS_SIZE) { - throw new IllegalArgumentException( - "Combined SigningInfos length exceeded limit of 7K: " + size); - } - - // Combine all arrays into one. - byte[] result = Arrays.copyOf(arrays[0], size); - int offset = arrays[0].length; - for (int i = 0, isize = this.signingInfoBlocks.length; i < isize; ++i) { - System.arraycopy(arrays[i + 1], 0, result, offset, arrays[i + 1].length); - offset += arrays[i + 1].length; - } - return result; - } - } - - // Always 2 for now. - public final int version; + public final int version; // Always 2 for now. public final byte[] hashingInfo; - // Can contain either SigningInfo or SigningInfo + one or multiple SigningInfoBlock. - // Passed as-is to the kernel. Can be retrieved later. - public final byte[] signingInfos; + public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later. - V4Signature(int version, byte[] hashingInfo, byte[] signingInfos) { + V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) { this.version = version; this.hashingInfo = hashingInfo; - this.signingInfos = signingInfos; + this.signingInfo = signingInfo; } static V4Signature readFrom(InputStream stream) throws IOException { @@ -217,7 +131,7 @@ public class V4Signature { public void writeTo(OutputStream stream) throws IOException { writeIntLE(stream, this.version); writeBytes(stream, this.hashingInfo); - writeBytes(stream, this.signingInfos); + writeBytes(stream, this.signingInfo); } static byte[] getSignedData(long fileSize, HashingInfo hashingInfo, SigningInfo signingInfo) { diff --git a/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java b/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java index 9712767..4185dbc 100644 --- a/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java +++ b/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java @@ -16,7 +16,6 @@ package com.android.apksig.internal.pkcs7; -import static com.android.apksig.Constants.OID_RSA_ENCRYPTION; import static com.android.apksig.internal.asn1.Asn1DerEncoder.ASN1_DER_NULL; import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA1; import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA256; @@ -93,7 +92,7 @@ public class AlgorithmIdentifier { throw new IllegalArgumentException( "Unexpected digest algorithm: " + digestAlgorithm); } - if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) { + if ("RSA".equalsIgnoreCase(keyAlgorithm)) { return Pair.of( jcaDigestPrefixForSigAlg + "withRSA", new AlgorithmIdentifier(OID_SIG_RSA, ASN1_DER_NULL)); diff --git a/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java b/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java index bbead72..87eae48 100644 --- a/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java +++ b/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java @@ -54,18 +54,6 @@ public abstract class AndroidSdkVersion { /** Android P. */ public static final int P = 28; - /** Android Q. */ - public static final int Q = 29; - /** Android R. */ public static final int R = 30; - - /** Android S. */ - public static final int S = 31; - - /** Android Sv2. */ - public static final int Sv2 = 32; - - /** Android T. */ - public static final int T = 33; } diff --git a/src/main/java/com/android/apksig/internal/zip/ZipUtils.java b/src/main/java/com/android/apksig/internal/zip/ZipUtils.java index 1c2e82c..9d9da15 100644 --- a/src/main/java/com/android/apksig/internal/zip/ZipUtils.java +++ b/src/main/java/com/android/apksig/internal/zip/ZipUtils.java @@ -70,20 +70,6 @@ public abstract class ZipUtils { } /** - * Sets the length of EOCD comment. - * - * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. - */ - public static void updateZipEocdCommentLen(ByteBuffer zipEndOfCentralDirectory) { - assertByteOrderLittleEndian(zipEndOfCentralDirectory); - int commentLen = zipEndOfCentralDirectory.remaining() - ZIP_EOCD_REC_MIN_SIZE; - setUnsignedInt16( - zipEndOfCentralDirectory, - zipEndOfCentralDirectory.position() + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET, - commentLen); - } - - /** * Returns the offset of the start of the ZIP Central Directory in the archive. * * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java index 83e0499..d799201 100644 --- a/src/test/java/com/android/apksig/ApkSignerTest.java +++ b/src/test/java/com/android/apksig/ApkSignerTest.java @@ -21,7 +21,6 @@ import static com.android.apksig.apk.ApkUtils.findZipSections; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -90,9 +89,9 @@ public class ApkSignerTest { // All signers with the same prefix and an _X suffix were signed with the private key of the // (X-1) signer. - static final String FIRST_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048"; - static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2"; - static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3"; + private static final String FIRST_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048"; + private static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2"; + private static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3"; private static final String EC_P256_SIGNER_RESOURCE_NAME = "ec-p256"; @@ -227,9 +226,7 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); - signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"), @@ -237,7 +234,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-aligned-in.apk", @@ -246,7 +242,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( @@ -299,7 +294,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-legacy-aligned-in.apk", @@ -308,7 +302,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-aligned-in.apk", @@ -317,7 +310,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( @@ -348,7 +340,6 @@ public class ApkSignerTest { .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-legacy-aligned-in.apk", @@ -357,7 +348,6 @@ public class ApkSignerTest { .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-aligned-in.apk", @@ -366,7 +356,6 @@ public class ApkSignerTest { .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( @@ -471,7 +460,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); assertGolden( "golden-unaligned-in.apk", @@ -494,7 +482,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); assertGolden( "golden-unaligned-in.apk", @@ -510,7 +497,6 @@ public class ApkSignerTest { .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as @@ -550,7 +536,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); assertGolden( "golden-legacy-aligned-in.apk", @@ -573,7 +558,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); assertGolden( "golden-legacy-aligned-in.apk", @@ -589,7 +573,6 @@ public class ApkSignerTest { .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); // Uncompressed entries in this input file are aligned by padding the "extra" field, as @@ -628,7 +611,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); assertGolden( "golden-aligned-in.apk", @@ -651,7 +633,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); assertGolden( "golden-aligned-in.apk", @@ -667,7 +648,6 @@ public class ApkSignerTest { .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); } @@ -970,7 +950,6 @@ public class ApkSignerTest { .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the @@ -1520,469 +1499,6 @@ public class ApkSignerTest { EC_P256_SIGNER_RESOURCE_NAME); } - @Test - public void testSetMinSdkVersionForRotation_lessThanT_noV31Block() throws Exception { - // The V3.1 signing block is intended to allow APK signing key rotation to target T+, but - // a minimum SDK version can be explicitly set for rotation; if it is less than T than - // the rotated key will be included in the V3.0 block. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApkMinRotationP = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result resultMinRotationP = verify(signedApkMinRotationP, null); - // The V3.1 signature scheme was introduced in T; specifying an older SDK version as the - // minimum for rotation should cause the APK to still be signed with rotation in the V3.0 - // signing block. - File signedApkMinRotationS = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(AndroidSdkVersion.S) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result resultMinRotationS = verify(signedApkMinRotationS, null); - - assertVerified(resultMinRotationP); - assertFalse(resultMinRotationP.isVerifiedUsingV31Scheme()); - assertEquals(1, resultMinRotationP.getV3SchemeSigners().size()); - assertResultContainsSigners(resultMinRotationP, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - assertVerified(resultMinRotationS); - assertFalse(resultMinRotationS.isVerifiedUsingV31Scheme()); - // While rotation is targeting S, signer blocks targeting specific SDK versions have not - // been tested in previous platform releases; ensure only a single signer block with the - // rotated key is in the V3 block. - assertEquals(1, resultMinRotationS.getV3SchemeSigners().size()); - assertResultContainsSigners(resultMinRotationS, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void testSetMinSdkVersionForRotation_TAndLater_v31Block() throws Exception { - // When T or later is specified as the minimum SDK version for rotation, then a new V3.1 - // signing block should be created with the new rotated key, and the V3.0 signing block - // should still be signed with the original key. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApkMinRotationT = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(AndroidSdkVersion.T) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result resultMinRotationT = verify(signedApkMinRotationT, null); - // The API level for a release after T is not yet defined, so for now treat it as T + 1. - File signedApkMinRotationU = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(AndroidSdkVersion.T + 1) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result resultMinRotationU = verify(signedApkMinRotationU, null); - - assertVerified(resultMinRotationT); - assertTrue(resultMinRotationT.isVerifiedUsingV31Scheme()); - assertResultContainsSigners(resultMinRotationT, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - assertV31SignerTargetsMinApiLevel(resultMinRotationT, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, - AndroidSdkVersion.T); - assertVerified(resultMinRotationU); - assertTrue(resultMinRotationU.isVerifiedUsingV31Scheme()); - assertResultContainsSigners(resultMinRotationU, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - assertV31SignerTargetsMinApiLevel(resultMinRotationU, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, - AndroidSdkVersion.T + 1); - } - - @Test - public void testSetMinSdkVersionForRotation_targetTNoOriginalSigner_fails() throws Exception { - // Similar to the V1 and V2 signatures schemes, if an app is targeting P or later with - // rotation targeting T, the original signer must be provided so that it can be used in the - // V3.0 signing block; if it is not provided the signer should throw an Exception. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = List - .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - assertThrows(IllegalArgumentException.class, () -> - sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(false) - .setV2SigningEnabled(false) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersion(28) - .setMinSdkVersionForRotation(AndroidSdkVersion.T) - .setSigningCertificateLineage(lineage))); - } - - @Test - public void testSetMinSdkVersionForRotation_targetTAndApkMinSdkT_onlySignsV3Block() - throws Exception { - // A V3.1 signing block should only exist alongside a V3.0 signing block; if an APK's - // min SDK version is greater than or equal to the SDK version for rotation then the - // original signer should not be required, and the rotated signing key should be in - // a V3.0 signing block. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = List - .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(false) - .setV2SigningEnabled(false) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersion(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) - .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result result = verifyForMinSdkVersion(signedApk, - V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT); - - assertVerified(result); - assertFalse(result.isVerifiedUsingV31Scheme()); - assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void testSetMinSdkVersionForRotation_targetTWithSourceStamp_noWarnings() - throws Exception { - // Source stamp verification will report a warning if a stamp signature is not found for any - // of the APK Signature Schemes used to sign the APK. This test verifies an APK signed with - // a rotated key in the v3.1 block and a source stamp successfully verifies, including the - // source stamp, without any warnings. - ApkSigner.SignerConfig rsa2048OriginalSignerConfig = getDefaultSignerConfigFromResources( - FIRST_RSA_2048_SIGNER_RESOURCE_NAME); - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - rsa2048OriginalSignerConfig, - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(AndroidSdkVersion.T) - .setSigningCertificateLineage(lineage) - .setSourceStampSignerConfig(rsa2048OriginalSignerConfig)); - ApkVerifier.Result result = verify(signedApk, null); - - assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, - AndroidSdkVersion.T); - assertSourceStampVerified(signedApk, result); - } - - @Test - public void testSetRotationTargetsDevRelease_target34_v30SignerTargetsAtLeast34() - throws Exception { - // During development of a new platform release the new platform will use the SDK version - // of the previously released platform, so in order to test rotation on a new platform - // release it must target the SDK version of the previous platform. However an APK signed - // with the v3.1 signature scheme and targeting rotation on the previous platform release X - // would still use rotation if that APK were installed on a device running release version - // X. To support targeting rotation on the main branch, the v3.1 signature scheme supports - // a rotation-targets-dev-release attribute; this allows the APK to use the v3.1 signer - // block on a development platform with SDK version X while a release platform X will - // skip this signer block when it sees this additional attribute. To ensure that the APK - // will still target the released platform X, the v3.0 signer must have a maxSdkVersion - // of at least X for the signer. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - int rotationMinSdkVersion = V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT + 1; - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(rotationMinSdkVersion) - .setSigningCertificateLineage(lineage) - .setRotationTargetsDevRelease(true)); - ApkVerifier.Result result = verify(signedApk, null); - - assertVerified(result); - assertTrue(result.isVerifiedUsingV31Scheme()); - assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); - assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - assertTrue(result.getV3SchemeSigners().get(0).getMaxSdkVersion() >= rotationMinSdkVersion); - } - - @Test - public void testV3_rotationMinSdkVersionLessThanTV3Only_origSignerNotRequired() - throws Exception { - // The v3.1 signature scheme allows a rotation-min-sdk-version be specified to target T+ - // for rotation; however if this value is less than the expected SDK version of T, then - // apksig should just use the rotated signing key in the v3.0 block. An APK that targets - // P+ that wants to use rotation in the v3.0 signing block should only need to provide - // the rotated signing key and lineage; this test ensures this behavior when the - // rotation-min-sdk-version is set to a value > P and < T. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApkRotationOnQ = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(false) - .setV2SigningEnabled(false) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersion(AndroidSdkVersion.P) - .setMinSdkVersionForRotation(AndroidSdkVersion.Q) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result resultRotationOnQ = verify(signedApkRotationOnQ, AndroidSdkVersion.P); - - assertVerified(resultRotationOnQ); - assertEquals(1, resultRotationOnQ.getV3SchemeSigners().size()); - assertFalse(resultRotationOnQ.isVerifiedUsingV31Scheme()); - assertResultContainsSigners(resultRotationOnQ, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void testV31_rotationMinSdkVersionEqualsMinSdkVersion_v3SignerPresent() - throws Exception { - // The SDK version for Sv2 (32) is used as the minSdkVersion for the V3.1 signature - // scheme to allow rotation to target the T development platform; this will be updated - // to the real SDK version of T once its SDK is finalized. This test verifies if a - // package has Sv2 as its minSdkVersion, the signing can complete as expected with the - // v3 block signed by the original signer and targeting just Sv2, and the v3.1 block - // signed by the rotated signer and targeting the dev release of Sv2 and all later releases. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original-minSdk32.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result result = verify(signedApk, null); - - assertVerified(result); - assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); - } - - @Test - public void testV31_rotationMinSdkVersionTWithoutLineage_v30VerificationSucceeds() - throws Exception { - // apksig allows setting a rotation-min-sdk-version without providing a rotated signing - // key / lineage; however in the absence of rotation, the rotation-min-sdk-version should - // be a no-op, and the stripping protection attribute should not be written to the v3.0 - // signer. - List<ApkSigner.SignerConfig> rsa2048SignerConfig = - Collections.singletonList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfig) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT)); - ApkVerifier.Result result = verify(signedApk, null); - - assertVerified(result); - assertFalse(result.isVerifiedUsingV31Scheme()); - assertTrue(result.isVerifiedUsingV3Scheme()); - } - - @Test - public void testV31_rotationMinSdkVersionDefault_rotationTargetsT() throws Exception { - // The v3.1 signature scheme was introduced in T to allow developers to target T+ for - // rotation due to known issues with rotation on previous platform releases. This test - // verifies an APK signed with a rotated signing key defaults to the original signing - // key used in the v3 signing block for pre-T devices, and the rotated signing key used - // in the v3.1 signing block for T+ devices. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result result = verify(signedApk, null); - - assertVerified(result); - assertTrue(result.isVerifiedUsingV3Scheme()); - assertTrue(result.isVerifiedUsingV31Scheme()); - assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); - assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, - AndroidSdkVersion.T); - } - - @Test - public void testV31_rotationMinSdkVersionP_rotationTargetsP() throws Exception { - // While the V3.1 signature scheme will target T by default, a package that has - // previously rotated can provide a rotation-min-sdk-version less than T to continue - // using the rotated signing key in the v3.0 block. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result result = verify(signedApk, null); - - assertVerified(result); - assertTrue(result.isVerifiedUsingV3Scheme()); - assertFalse(result.isVerifiedUsingV31Scheme()); - } - - @Test - public void testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner() - throws Exception { - // To support SDK version targeting in the v3.1 signature scheme, apksig added a - // rotation-min-sdk-version option to allow the caller to specify the level from which - // the rotated signer should be used. A value less than T should result in a single - // rotated signer in the V3 block (along with the corresponding lineage), and the V4 - // signature should use this signer. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.P) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result result = verify(signedApk, null); - - assertResultContainsV4Signers(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey() throws Exception { - // When an APK is signed with a rotated key and the rotation-min-sdk-version X is set to T+, - // a V3.1 block will be signed with the rotated signing key targeting X and later, and - // a V3.0 block will be signed with the original signing key targeting P - X-1. The - // V4 signature should contain both the original signing key and the rotated signing - // key; this ensures if an APK is installed on a device running an SDK version less than X, - // the V4 signature will be verified using the original signing key which will be the only - // signing key visible to the platform. - List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = - Arrays.asList( - getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), - getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - SigningCertificateLineage lineage = - Resources.toSigningCertificateLineage( - ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(rsa2048SignerConfigWithLineage) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(true) - .setMinSdkVersionForRotation(AndroidSdkVersion.T) - .setSigningCertificateLineage(lineage)); - ApkVerifier.Result result = verify(signedApk, null); - - assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void testSourceStampTimestamp_signWithSourceStamp_validTimestampValue() - throws Exception { - // Source stamps should include a timestamp attribute with the epoch time the stamp block - // was signed. This test verifies a standard signing with a source stamp includes a valid - // value for the source stamp timestamp attribute. - ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( - FIRST_RSA_2048_SIGNER_RESOURCE_NAME); - List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( - getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); - - File signedApk = sign("original.apk", - new ApkSigner.Builder(ecP256SignerConfig) - .setV1SigningEnabled(true) - .setV2SigningEnabled(true) - .setV3SigningEnabled(true) - .setV4SigningEnabled(false) - .setSourceStampSignerConfig(rsa2048SignerConfig)); - ApkVerifier.Result result = verify(signedApk, null); - - assertSourceStampVerified(signedApk, result); - long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); - assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0); - } - /** * Asserts the provided {@code signedApk} contains a signature block with the expected * {@code byte[]} value and block ID as specified in the {@code expectedBlock}. @@ -2011,20 +1527,8 @@ public class ApkSignerTest { * Asserts the provided verification {@code result} contains the expected {@code signers} for * each scheme that was used to verify the APK's signature. */ - static void assertResultContainsSigners(ApkVerifier.Result result, String... signers) + private static void assertResultContainsSigners(ApkVerifier.Result result, String... signers) throws Exception { - assertResultContainsSigners(result, false, signers); - } - - /** - * Asserts the provided verification {@code result} contains the expected {@code signers} for - * each scheme that was used to verify the APK's signature; if {@code rotationExpected} is set - * to {@code true}, then the first element in {@code signers} is treated as the expected - * original signer for any V1, V2, and V3 (where applicable) signatures, and the last element - * is the rotated expected signer for V3+. - */ - static void assertResultContainsSigners(ApkVerifier.Result result, - boolean rotationExpected, String... signers) throws Exception { // A result must be successfully verified before verifying any of the result's signers. assertTrue(result.isVerified()); @@ -2033,27 +1537,16 @@ public class ApkSignerTest { ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer); expectedSigners.addAll(signerConfig.getCertificates()); } - // If rotation is expected then the V1 and V2 signature should only be signed by the - // original signer. - List<X509Certificate> expectedV1Signers = - rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners; - List<X509Certificate> expectedV2Signers = - rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners; - // V3 only supports a single signer; if rotation is not expected or the V3.1 block contains - // the rotated signing key then the expected V3.0 signer should be the original signer. - List<X509Certificate> expectedV3Signers = - !rotationExpected || result.isVerifiedUsingV31Scheme() - ? List.of(expectedSigners.get(0)) - : List.of(expectedSigners.get(expectedSigners.size() - 1)); if (result.isVerifiedUsingV1Scheme()) { Set<X509Certificate> v1Signers = new HashSet<>(); for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { v1Signers.add(signer.getCertificate()); } - assertTrue("Expected V1 signers: " + getAllSubjectNamesFrom(expectedV1Signers) + assertEquals(expectedSigners.size(), v1Signers.size()); + assertTrue("Expected V1 signers: " + getAllSubjectNamesFrom(expectedSigners) + ", actual V1 signers: " + getAllSubjectNamesFrom(v1Signers), - v1Signers.containsAll(expectedV1Signers)); + v1Signers.containsAll(expectedSigners)); } if (result.isVerifiedUsingV2Scheme()) { @@ -2061,9 +1554,10 @@ public class ApkSignerTest { for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { v2Signers.add(signer.getCertificate()); } - assertTrue("Expected V2 signers: " + getAllSubjectNamesFrom(expectedV2Signers) + assertEquals(expectedSigners.size(), v2Signers.size()); + assertTrue("Expected V2 signers: " + getAllSubjectNamesFrom(expectedSigners) + ", actual V2 signers: " + getAllSubjectNamesFrom(v2Signers), - v2Signers.containsAll(expectedV2Signers)); + v2Signers.containsAll(expectedSigners)); } if (result.isVerifiedUsingV3Scheme()) { @@ -2071,68 +1565,11 @@ public class ApkSignerTest { for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { v3Signers.add(signer.getCertificate()); } - assertTrue("Expected V3 signers: " + getAllSubjectNamesFrom(expectedV3Signers) + assertEquals(expectedSigners.size(), v3Signers.size()); + assertTrue("Expected V3 signers: " + getAllSubjectNamesFrom(expectedSigners) + ", actual V3 signers: " + getAllSubjectNamesFrom(v3Signers), - v3Signers.containsAll(expectedV3Signers)); - } - - if (result.isVerifiedUsingV31Scheme()) { - Set<X509Certificate> v31Signers = new HashSet<>(); - for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { - v31Signers.add(signer.getCertificate()); - } - // V3.1 only supports specifying signatures with a rotated signing key; if a V3.1 - // signing block was verified then ensure it contains the expected rotated signer. - List<X509Certificate> expectedV31Signers = List - .of(expectedSigners.get(expectedSigners.size() - 1)); - assertTrue("Expected V3.1 signers: " + getAllSubjectNamesFrom(expectedV31Signers) - + ", actual V3.1 signers: " + getAllSubjectNamesFrom(v31Signers), - v31Signers.containsAll(expectedV31Signers)); - } - } - - /** - * Asserts the provided verification {@code result} contains the expected V4 {@code signers}. - */ - private static void assertResultContainsV4Signers(ApkVerifier.Result result, String... signers) - throws Exception { - assertTrue(result.isVerified()); - assertTrue(result.isVerifiedUsingV4Scheme()); - List<X509Certificate> expectedSigners = new ArrayList<>(); - for (String signer : signers) { - ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer); - expectedSigners.addAll(signerConfig.getCertificates()); - } - List<X509Certificate> v4Signers = new ArrayList<>(); - for (ApkVerifier.Result.V4SchemeSignerInfo signer : result.getV4SchemeSigners()) { - v4Signers.addAll(signer.getCertificates()); - } - assertTrue("Expected V4 signers: " + getAllSubjectNamesFrom(expectedSigners) - + ", actual V4 signers: " + getAllSubjectNamesFrom(v4Signers), - v4Signers.containsAll(expectedSigners)); - } - - /** - * Asserts the provided {@code result} contains the expected {@code signer} targeting - * {@code minSdkVersion} as the minimum version for rotation. - */ - static void assertV31SignerTargetsMinApiLevel(ApkVerifier.Result result, String signer, - int minSdkVersion) throws Exception { - assertTrue(result.isVerifiedUsingV31Scheme()); - ApkSigner.SignerConfig expectedSignerConfig = getDefaultSignerConfigFromResources(signer); - - for (ApkVerifier.Result.V3SchemeSignerInfo signerConfig : result.getV31SchemeSigners()) { - if (signerConfig.getCertificates() - .containsAll(expectedSignerConfig.getCertificates())) { - assertEquals("The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates()) - + ", is expected to target SDK version " + minSdkVersion - + ", instead it is targeting " + signerConfig.getMinSdkVersion(), - minSdkVersion, signerConfig.getMinSdkVersion()); - return; - } + v3Signers.containsAll(expectedSigners)); } - fail("Did not find the expected signer, " + getAllSubjectNamesFrom( - expectedSignerConfig.getCertificates())); } /** diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java index 9de2b59..9e1a75e 100644 --- a/src/test/java/com/android/apksig/ApkVerifierTest.java +++ b/src/test/java/com/android/apksig/ApkVerifierTest.java @@ -16,12 +16,7 @@ package com.android.apksig; -import static com.android.apksig.ApkSignerTest.FIRST_RSA_2048_SIGNER_RESOURCE_NAME; -import static com.android.apksig.ApkSignerTest.SECOND_RSA_2048_SIGNER_RESOURCE_NAME; -import static com.android.apksig.ApkSignerTest.assertResultContainsSigners; -import static com.android.apksig.ApkSignerTest.assertV31SignerTargetsMinApiLevel; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeNoException; @@ -30,13 +25,11 @@ import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.ApkVerifier.IssueWithParams; import com.android.apksig.ApkVerifier.Result.SourceStampInfo.SourceStampVerificationStatus; import com.android.apksig.apk.ApkFormatException; -import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.HexEncoding; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSources; -import java.security.Provider; import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; @@ -1313,123 +1306,6 @@ public class ApkVerifierTest { } @Test - public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception { - // A timestamp attribute was added to the source stamp, but verification of APKs that were - // generated prior to the addition of the timestamp should still complete successfully, - // returning a default value of 0 for the timestamp. - ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk"); - - assertTrue(verificationResult.isSourceStampVerified()); - assertEquals( - "A value of 0 should be returned for the timestamp when the attribute is not " - + "present", - 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); - } - - @Test - public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception { - // Once an APK is signed with a source stamp that contains a valid value for the timestamp - // attribute, verification of the source stamp should result in the same value for the - // timestamp returned to the verifier. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-valid-timestamp-value.apk"); - - assertTrue(verificationResult.isSourceStampVerified()); - assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); - } - - @Test - public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue() - throws Exception { - // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer - // as a little-endian long; while a larger buffer will not result in an error, any - // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a - // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read - // successfully. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-valid-timestamp-16-byte-buffer.apk"); - - assertTrue(verificationResult.isSourceStampVerified()); - assertEquals(1645126786, - verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); - } - - @Test - public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails() - throws Exception { - // If the source stamp timestamp attribute exists and is <= 0, then a warning should be - // reported to notify the caller to the invalid attribute value. This test verifies a - // a warning is reported when the timestamp attribute value is 0. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-invalid-timestamp-value-zero.apk"); - - assertSourceStampVerificationStatus(verificationResult, - SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); - assertSourceStampVerificationFailure(verificationResult, - Issue.SOURCE_STAMP_INVALID_TIMESTAMP); - } - - @Test - public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails() - throws Exception { - // If the source stamp timestamp attribute exists and is <= 0, then a warning should be - // reported to notify the caller to the invalid attribute value. This test verifies a - // a warning is reported when the timestamp attribute value is < 0. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-invalid-timestamp-value-less-than-zero.apk"); - - assertSourceStampVerificationStatus(verificationResult, - SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); - assertSourceStampVerificationFailure(verificationResult, - Issue.SOURCE_STAMP_INVALID_TIMESTAMP); - } - - @Test - public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails() - throws Exception { - // The source stamp's timestamp attribute value is expected to be written to the first 8 - // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp - // value is not written as a little-endian long to the first 8 bytes of the buffer, then - // an error should be reported for the timestamp attribute since the rest of the buffer will - // be ignored. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-timestamp-in-last-8-of-16-byte-buffer.apk"); - - assertSourceStampVerificationStatus(verificationResult, - SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); - assertSourceStampVerificationFailure(verificationResult, - Issue.SOURCE_STAMP_INVALID_TIMESTAMP); - } - - - @Test - public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception { - // Since the source stamp timestamp attribute value is a long, an attribute value with - // insufficient space to hold a long value should result in a warning reported to the user. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-int-timestamp-value.apk"); - - assertSourceStampVerificationStatus(verificationResult, - SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); - assertSourceStampVerificationFailure(verificationResult, - Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE); - } - - @Test - public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception { - // The source stamp timestamp attribute is part of the block's signed data; this test - // verifies if the value of the timestamp in the stamp block is modified then verification - // of the source stamp should fail. - ApkVerifier.Result verificationResult = verifySourceStamp( - "stamp-valid-timestamp-value-modified.apk"); - - assertSourceStampVerificationStatus(verificationResult, - SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); - assertSourceStampVerificationFailure(verificationResult, - Issue.SOURCE_STAMP_DID_NOT_VERIFY); - } - - @Test public void apkVerificationIssueAdapter_verifyAllBaseIssuesMapped() throws Exception { Field[] fields = ApkVerificationIssue.class.getFields(); StringBuilder msg = new StringBuilder(); @@ -1454,148 +1330,6 @@ public class ApkVerifierTest { } } - @Test - public void verifySignature_negativeModulusConscryptProvider() throws Exception { - Provider conscryptProvider = null; - try { - conscryptProvider = new org.conscrypt.OpenSSLProvider(); - Security.insertProviderAt(conscryptProvider, 1); - assertVerified(verify("v1v2v3-rsa-2048-negmod-in-cert.apk")); - } catch (UnsatisfiedLinkError e) { - // If the library for conscrypt is not available then skip this test. - return; - } finally { - if (conscryptProvider != null) { - Security.removeProvider(conscryptProvider.getName()); - } - } - } - - @Test - public void verifyV31_rotationTarget34_containsExpectedSigners() throws Exception { - // This test verifies an APK targeting a specific SDK version for rotation properly reports - // that version for the rotated signer in the v3.1 block, and all other signing blocks - // use the original signing key. - ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); - - assertVerified(result); - assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 34); - } - - @Test - public void verifyV31_missingStrippingAttr_warningReported() throws Exception { - // The v3.1 signing block supports targeting SDK versions; to protect against these target - // versions being modified the v3 signer contains a stripping protection attribute with the - // SDK version on which rotation should be applied. This test verifies a warning is reported - // when this attribute is not present in the v3 signer. - ApkVerifier.Result result = verify("v31-tgt-33-no-v3-attr.apk"); - - assertVerificationWarning(result, Issue.V31_ROTATION_MIN_SDK_ATTR_MISSING); - } - - @Test - public void verifyV31_strippingAttrMismatch_errorReportedOnSupportedVersions() - throws Exception { - // This test verifies if the stripping protection attribute does not properly match the - // minimum SDK version on which rotation is supported then the APK should fail verification. - ApkVerifier.Result result = verify("v31-tgt-34-v3-attr-value-33.apk"); - assertVerificationFailure(result, Issue.V31_ROTATION_MIN_SDK_MISMATCH); - - // SDK versions that do not support v3.1 should ignore the stripping protection attribute - // and the v3.1 signing block. - result = verifyForMaxSdkVersion("v31-tgt-34-v3-attr-value-33.apk", - V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - 1); - assertVerified(result); - } - - @Test - public void verifyV31_missingV31Block_errorReportedOnSupportedVersions() throws Exception { - // This test verifies if the stripping protection attribute contains a value for rotation - // but a v3.1 signing block was not found then the APK should fail verification. - ApkVerifier.Result result = verify("v31-block-stripped-v3-attr-value-33.apk"); - assertVerificationFailure(result, Issue.V31_BLOCK_MISSING); - - // SDK versions that do not support v3.1 should ignore the stripping protection attribute - // and the v3.1 signing block. - result = verifyForMaxSdkVersion("v31-block-stripped-v3-attr-value-33.apk", - V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - 1); - assertVerified(result); - } - - @Test - public void verifyV31_v31BlockWithoutV3Block_reportsError() throws Exception { - // A v3.1 block must always exist alongside a v3.0 block; if an APK's minSdkVersion is the - // same as the version supporting rotation then it should be written to a v3.0 block. - ApkVerifier.Result result = verify("v31-tgt-33-no-v3-block.apk"); - assertVerificationFailure(result, Issue.V31_BLOCK_FOUND_WITHOUT_V3_BLOCK); - } - - @Test - public void verifyV31_rotationTargetsDevRelease_resultReportsDevReleaseFlag() throws Exception { - // Development releases use the SDK version of the previous release until the SDK is - // finalized. In order to only target the development release and later, the v3.1 signature - // scheme supports targeting development releases such that the SDK version X will install - // on a device running X with the system property ro.build.version.codename set to a new - // development codename (eg T); a release platform will have this set to "REL", and the - // platform will ignore the v3.1 signer if the minSdkVersion is X and the codename is "REL". - ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-34-dev-release.apk"); - - assertVerified(result); - assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 34); - assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void verifyV3_v3RotatedSignerTargetsDevRelease_warningReported() throws Exception { - // While a v3.1 signer can target a development release, v3.0 does not support the same - // attribute since it is only intended for v3.1 with v3.0 using the original signer. This - // test verifies a warning is reported if an APK has this flag set on a v3.0 signer since it - // will be ignored by the platform. - ApkVerifier.Result result = verify("v3-rsa-2048_2-tgt-dev-release.apk"); - - assertVerificationWarning(result, Issue.V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER); - } - - @Test - public void verifyV31_rotationTargets34_resultContainsExpectedLineage() throws Exception { - // During verification of the v3.1 and v3.0 signing blocks, ApkVerifier will set the - // signing certificate lineage in the Result object; this test verifies a null lineage from - // a v3.0 signer does not overwrite a valid lineage from a v3.1 signer. - ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); - - assertNotNull(result.getSigningCertificateLineage()); - SigningCertificateLineageTest.assertLineageContainsExpectedSigners( - result.getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - } - - @Test - public void verify31_minSdkVersionT_resultSuccessfullyVerified() throws Exception { - // When a min-sdk-version of 33 is explicitly specified, apksig will behave the same as a - // device running this API level and only verify a v3.1 signature if it exists. This test - // verifies this v3.1 signature is sufficient to report the APK as verified. - ApkVerifier.Result result = verifyForMinSdkVersion("v31-rsa-2048_2-tgt-33-1-tgt-28.apk", - 33); - - assertVerified(result); - assertTrue(result.isVerifiedUsingV31Scheme()); - } - - @Test - public void verify31_minSdkVersionTTargetSdk30_resultSuccessfullyVerified() throws Exception { - // This test verifies when a min-sdk-version of 33 is specified and the APK targets API - // level 30 or later, the v3.1 signature is sufficient to report the APK meets the - // requirement of a minimum v2 signature. - ApkVerifier.Result result = verifyForMinSdkVersion( - "v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk", 33); - - assertVerified(result); - assertTrue(result.isVerifiedUsingV31Scheme()); - } - private ApkVerifier.Result verify(String apkFilenameInResources) throws IOException, ApkFormatException, NoSuchAlgorithmException { return verify(apkFilenameInResources, null, null); @@ -1734,27 +1468,13 @@ public class ApkVerifierTest { } static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) { - assertVerificationIssue(result, expectedIssue, true); - } - - static void assertVerificationWarning(ApkVerifier.Result result, Issue expectedIssue) { - assertVerificationIssue(result, expectedIssue, false); - } - - /** - * Asserts the provided {@code result} contains the {@code expectedIssue}; if {@code - * verifyError} is set to {@code true} then the specified {@link Issue} will be expected as an - * error, otherwise it will be expected as a warning. - */ - private static void assertVerificationIssue(ApkVerifier.Result result, Issue expectedIssue, - boolean verifyError) { - if (result.isVerified() && verifyError) { + if (result.isVerified()) { fail("APK verification succeeded instead of failing with " + expectedIssue); return; } StringBuilder msg = new StringBuilder(); - for (IssueWithParams issue : (verifyError ? result.getErrors() : result.getWarnings())) { + for (IssueWithParams issue : result.getErrors()) { if (expectedIssue.equals(issue.getIssue())) { return; } @@ -1765,8 +1485,7 @@ public class ApkVerifierTest { } for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { String signerName = signer.getName(); - for (ApkVerifier.IssueWithParams issue : (verifyError ? signer.getErrors() - : signer.getWarnings())) { + for (ApkVerifier.IssueWithParams issue : signer.getErrors()) { if (expectedIssue.equals(issue.getIssue())) { return; } @@ -1783,8 +1502,7 @@ public class ApkVerifierTest { } for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); - for (IssueWithParams issue : (verifyError ? signer.getErrors() - : signer.getWarnings())) { + for (IssueWithParams issue : signer.getErrors()) { if (expectedIssue.equals(issue.getIssue())) { return; } @@ -1799,8 +1517,7 @@ public class ApkVerifierTest { } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); - for (IssueWithParams issue : (verifyError ? signer.getErrors() - : signer.getWarnings())) { + for (IssueWithParams issue : signer.getErrors()) { if (expectedIssue.equals(issue.getIssue())) { return; } @@ -1813,22 +1530,6 @@ public class ApkVerifierTest { .append(issue); } } - for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { - String signerName = "signer #" + (signer.getIndex() + 1); - for (IssueWithParams issue : (verifyError ? signer.getErrors() - : signer.getWarnings())) { - if (expectedIssue.equals(issue.getIssue())) { - return; - } - if (msg.length() > 0) { - msg.append('\n'); - } - msg.append("APK Signature Scheme v3.1 signer ") - .append(signerName) - .append(": ") - .append(issue); - } - } fail( "APK failed verification for the wrong reason" diff --git a/src/test/java/com/android/apksig/SigningCertificateLineageTest.java b/src/test/java/com/android/apksig/SigningCertificateLineageTest.java index 07a48f1..d5dc71d 100644 --- a/src/test/java/com/android/apksig/SigningCertificateLineageTest.java +++ b/src/test/java/com/android/apksig/SigningCertificateLineageTest.java @@ -397,21 +397,6 @@ public class SigningCertificateLineageTest { assertLineageContainsExpectedSigners(lineageFromApk, expectedSigners); } - @Test - public void testLineageFromAPKWithV31BlockContainsExpectedSigners() throws Exception { - SignerConfig firstSigner = getSignerConfigFromResources( - FIRST_RSA_2048_SIGNER_RESOURCE_NAME); - SignerConfig secondSigner = getSignerConfigFromResources( - SECOND_RSA_2048_SIGNER_RESOURCE_NAME); - List<SignerConfig> expectedSigners = Arrays.asList(firstSigner, secondSigner); - DataSource apkDataSource = Resources.toDataSource(getClass(), - "v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); - SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource( - apkDataSource); - assertLineageContainsExpectedSigners(lineageFromApk, expectedSigners); - - } - @Test(expected = ApkFormatException.class) public void testLineageFromAPKWithInvalidZipCDSizeFails() throws Exception { // This test verifies that attempting to read the lineage from an APK where the zip @@ -550,20 +535,7 @@ public class SigningCertificateLineageTest { return lineage.spawnDescendant(oldSignerConfig, newSignerConfig); } - /** - * Asserts the provided {@code lineage} contains the {@code expectedSigners} from the test's - * resources. - */ - static void assertLineageContainsExpectedSigners(SigningCertificateLineage lineage, - String... expectedSigners) throws Exception { - List<SignerConfig> signers = new ArrayList<>(); - for (String expectedSigner : expectedSigners) { - signers.add(getSignerConfigFromResources(expectedSigner)); - } - assertLineageContainsExpectedSigners(lineage, signers); - } - - private static void assertLineageContainsExpectedSigners(SigningCertificateLineage lineage, + private void assertLineageContainsExpectedSigners(SigningCertificateLineage lineage, List<SignerConfig> signers) { assertEquals("The lineage does not contain the expected number of signers", signers.size(), lineage.size()); diff --git a/src/test/java/com/android/apksig/SourceStampVerifierTest.java b/src/test/java/com/android/apksig/SourceStampVerifierTest.java index 2e54a8a..f5020cc 100644 --- a/src/test/java/com/android/apksig/SourceStampVerifierTest.java +++ b/src/test/java/com/android/apksig/SourceStampVerifierTest.java @@ -21,12 +21,10 @@ import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.toHex; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.android.apksig.SourceStampVerifier.Result; import com.android.apksig.SourceStampVerifier.Result.SignerInfo; -import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSources; @@ -271,110 +269,6 @@ public class SourceStampVerifierTest { ApkVerificationIssue.JAR_SIG_NO_SIGNATURES); } - @Test - public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception { - // A timestamp attribute was added to the source stamp, but verification of APKs that were - // generated prior to the addition of the timestamp should still complete successfully, - // returning a default value of 0 for the timestamp. - Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", AndroidSdkVersion.P, - AndroidSdkVersion.P); - - assertVerified(verificationResult); - assertEquals( - "A value of 0 should be returned for the timestamp when the attribute is not " - + "present", - 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); - } - - @Test - public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception { - // Once an APK is signed with a source stamp that contains a valid value for the timestamp - // attribute, verification of the source stamp should result in the same value for the - // timestamp returned to the verifier. - Result verificationResult = verifySourceStamp("stamp-valid-timestamp-value.apk"); - - assertVerified(verificationResult); - assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); - } - - @Test - public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue() - throws Exception { - // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer - // as a little-endian long; while a larger buffer will not result in an error, any - // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a - // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read - // successfully. - Result verificationResult = verifySourceStamp("stamp-valid-timestamp-16-byte-buffer.apk"); - - assertEquals(1645126786, - verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); - } - - @Test - public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails() - throws Exception { - // If the source stamp timestamp attribute exists and is <= 0, then a warning should be - // reported to notify the caller to the invalid attribute value. This test verifies a - // a warning is reported when the timestamp attribute value is 0. - Result verificationResult = verifySourceStamp("stamp-invalid-timestamp-value-zero.apk"); - - assertSourceStampVerificationFailure(verificationResult, - ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP); - } - - @Test - public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails() - throws Exception { - // If the source stamp timestamp attribute exists and is <= 0, then a warning should be - // reported to notify the caller to the invalid attribute value. This test verifies a - // a warning is reported when the timestamp attribute value is < 0. - Result verificationResult = verifySourceStamp( - "stamp-invalid-timestamp-value-less-than-zero.apk"); - - assertSourceStampVerificationFailure(verificationResult, - ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP); - } - - @Test - public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails() - throws Exception { - // The source stamp's timestamp attribute value is expected to be written to the first 8 - // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp - // value is not written as a little-endian long to the first 8 bytes of the buffer, then - // an error should be reported for the timestamp attribute since the rest of the buffer will - // be ignored. - Result verificationResult = verifySourceStamp( - "stamp-timestamp-in-last-8-of-16-byte-buffer.apk"); - - assertSourceStampVerificationFailure(verificationResult, - ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP); - } - - @Test - public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception { - // Since the source stamp timestamp attribute value is a long, an attribute value with - // insufficient space to hold a long value should result in a warning reported to the user. - Result verificationResult = verifySourceStamp( - "stamp-int-timestamp-value.apk"); - - assertSourceStampVerificationFailure(verificationResult, - ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE); - } - - @Test - public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception { - // The source stamp timestamp attribute is part of the block's signed data; this test - // verifies if the value of the timestamp in the stamp block is modified then verification - // of the source stamp should fail. - Result verificationResult = verifySourceStamp( - "stamp-valid-timestamp-value-modified.apk"); - - assertSourceStampVerificationFailure(verificationResult, - ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY); - } - - private Result verifySourceStamp(String apkFilenameInResources) throws Exception { return verifySourceStamp(apkFilenameInResources, null, null, null); diff --git a/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-lineage-out.apk Binary files differindex a273b0c..965f901 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v2v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v2v3-lineage-out.apk Binary files differindex 2a0d383..13a3bc9 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-v2v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v3-lineage-out.apk Binary files differindex 4a4cda9..36f4825 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-lineage-out.apk Binary files differindex 376b0e5..f6b60d6 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-lineage-out.apk Binary files differindex 25ffa5b..b0debf6 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-lineage-out.apk Binary files differindex 341d7ba..f8b4171 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-lineage-out.apk Binary files differindex 5636723..02ecee0 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v2v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v2v3-lineage-out.apk Binary files differindex 4f21b4c..08538a0 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-v2v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v3-lineage-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v3-lineage-out.apk Binary files differindex 7514d20..9bcbc7a 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-v3-lineage-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/original-minSdk32.apk b/src/test/resources/com/android/apksig/original-minSdk32.apk Binary files differdeleted file mode 100644 index 1d50f27..0000000 --- a/src/test/resources/com/android/apksig/original-minSdk32.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk b/src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk Binary files differdeleted file mode 100644 index 0cd740c..0000000 --- a/src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk b/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk Binary files differdeleted file mode 100644 index f4a189e..0000000 --- a/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk b/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk Binary files differdeleted file mode 100644 index 6cfd082..0000000 --- a/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apk b/src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apk Binary files differdeleted file mode 100644 index 260b0ca..0000000 --- a/src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apk b/src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apk Binary files differdeleted file mode 100644 index da9c34d..0000000 --- a/src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk b/src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk Binary files differdeleted file mode 100644 index eefc148..0000000 --- a/src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk b/src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk Binary files differdeleted file mode 100644 index 3c6a501..0000000 --- a/src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk b/src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk Binary files differdeleted file mode 100644 index 6f73b92..0000000 --- a/src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v3-rsa-2048_2-tgt-dev-release.apk b/src/test/resources/com/android/apksig/v3-rsa-2048_2-tgt-dev-release.apk Binary files differdeleted file mode 100644 index d3b2c14..0000000 --- a/src/test/resources/com/android/apksig/v3-rsa-2048_2-tgt-dev-release.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-block-stripped-v3-attr-value-33.apk b/src/test/resources/com/android/apksig/v31-block-stripped-v3-attr-value-33.apk Binary files differdeleted file mode 100644 index d091075..0000000 --- a/src/test/resources/com/android/apksig/v31-block-stripped-v3-attr-value-33.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk b/src/test/resources/com/android/apksig/v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk Binary files differdeleted file mode 100644 index ad14731..0000000 --- a/src/test/resources/com/android/apksig/v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-33-1-tgt-28.apk b/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-33-1-tgt-28.apk Binary files differdeleted file mode 100644 index aeaec33..0000000 --- a/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-33-1-tgt-28.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-1-tgt-28.apk b/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-1-tgt-28.apk Binary files differdeleted file mode 100644 index ccf59d4..0000000 --- a/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-1-tgt-28.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-dev-release.apk b/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-dev-release.apk Binary files differdeleted file mode 100644 index 784f47e..0000000 --- a/src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-dev-release.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-tgt-33-no-v3-attr.apk b/src/test/resources/com/android/apksig/v31-tgt-33-no-v3-attr.apk Binary files differdeleted file mode 100644 index 284cd7a..0000000 --- a/src/test/resources/com/android/apksig/v31-tgt-33-no-v3-attr.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-tgt-33-no-v3-block.apk b/src/test/resources/com/android/apksig/v31-tgt-33-no-v3-block.apk Binary files differdeleted file mode 100644 index 1c797a7..0000000 --- a/src/test/resources/com/android/apksig/v31-tgt-33-no-v3-block.apk +++ /dev/null diff --git a/src/test/resources/com/android/apksig/v31-tgt-34-v3-attr-value-33.apk b/src/test/resources/com/android/apksig/v31-tgt-34-v3-attr-value-33.apk Binary files differdeleted file mode 100644 index ab87fcc..0000000 --- a/src/test/resources/com/android/apksig/v31-tgt-34-v3-attr-value-33.apk +++ /dev/null |