diff options
author | Michael Groover <mpgroover@google.com> | 2018-07-06 18:55:19 -0700 |
---|---|---|
committer | Michael Groover <mpgroover@google.com> | 2018-07-06 18:55:19 -0700 |
commit | dded3f8f4dc262b42d6b47f05262ac42806368e8 (patch) | |
tree | 42df71936ac221fecc812a5a7cb4d006ef002cae | |
parent | e74151cdd2c79d057be46be651943e9bfbd7ba94 (diff) | |
download | apksig-android-o-mr1-iot-release-1.0.2.tar.gz |
Add apksig Signature Scheme v3 testsandroid-p-preview-5android-o-mr1-iot-release-1.0.2
Change-Id: Ia8504c2bec9ace8856901d12d328371f0b560a5a
Fixes: 111221005
Test: gradlew test
89 files changed, 902 insertions, 73 deletions
diff --git a/src/apksigner/java/com/android/apksigner/ApkSignerTool.java b/src/apksigner/java/com/android/apksigner/ApkSignerTool.java index 4713bab..aeb02cd 100644 --- a/src/apksigner/java/com/android/apksigner/ApkSignerTool.java +++ b/src/apksigner/java/com/android/apksigner/ApkSignerTool.java @@ -20,7 +20,10 @@ import com.android.apksig.ApkSigner; import com.android.apksig.ApkVerifier; 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.util.DataSource; +import com.android.apksig.util.DataSources; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; @@ -31,6 +34,9 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -85,6 +91,8 @@ public class ApkSignerTool { private static MessageDigest sha1 = null; private static MessageDigest md5 = null; + public static final int ZIP_MAGIC = 0x04034b50; + public static void main(String[] params) throws Exception { if ((params.length == 0) || ("--help".equals(params[0])) || ("-h".equals(params[0]))) { printUsage(HELP_PAGE_GENERAL); @@ -217,7 +225,7 @@ public class ApkSignerTool { signerParams.certFile = optionsParser.getRequiredValue("Certificate file"); } else if ("lineage".equals(optionName)) { File lineageFile = new File(optionsParser.getRequiredValue("Lineage File")); - lineage = SigningCertificateLineage.readFromFile(lineageFile); + lineage = getLineageFromInputFile(lineageFile); } else if (("v".equals(optionName)) || ("verbose".equals(optionName))) { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("next-provider".equals(optionName)) { @@ -651,7 +659,7 @@ public class ApkSignerTool { SigningCertificateLineage lineage; if (inputKeyLineage != null) { // we already have history, add the new key to the end of it - lineage = SigningCertificateLineage.readFromFile(inputKeyLineage); + lineage = getLineageFromInputFile(inputKeyLineage); lineage.updateSignerCapabilities(oldSignerConfig, oldSignerParams.signerCapabilitiesBuilder.build()); lineage = lineage.spawnDescendant(oldSignerConfig, @@ -682,6 +690,7 @@ public class ApkSignerTool { boolean verbose = false; boolean printCerts = false; boolean lineageUpdated = false; + File inputKeyLineage = null; File outputKeyLineage = null; String optionName; OptionsParser optionsParser = new OptionsParser(params); @@ -692,8 +701,7 @@ public class ApkSignerTool { printUsage(HELP_PAGE_LINEAGE); return; } else if ("in".equals(optionName)) { - File inputKeyLineage = new File(optionsParser.getRequiredValue("Input file name")); - lineage = SigningCertificateLineage.readFromFile(inputKeyLineage); + inputKeyLineage = new File(optionsParser.getRequiredValue("Input file name")); } else if ("out".equals(optionName)) { outputKeyLineage = new File(optionsParser.getRequiredValue("Output file name")); } else if ("signer".equals(optionName)) { @@ -709,9 +717,11 @@ public class ApkSignerTool { + ". See --help for supported options."); } } - if (lineage == null) { + if (inputKeyLineage == null) { throw new ParameterException("Input lineage file parameter not present"); } + lineage = getLineageFromInputFile(inputKeyLineage); + try (PasswordRetriever passwordRetriever = new PasswordRetriever()) { for (int i = 0; i < signers.size(); i++) { SignerParams signerParams = signers.get(i); @@ -775,6 +785,29 @@ public class ApkSignerTool { } } + /** + * Extracts the Signing Certificate Lineage from the provided lineage or APK file. + */ + private static SigningCertificateLineage getLineageFromInputFile(File inputLineageFile) + throws ParameterException { + try (RandomAccessFile f = new RandomAccessFile(inputLineageFile, "r")) { + if (f.length() < 4) { + throw new ParameterException("The input file is not a valid lineage file."); + } + DataSource apk = DataSources.asDataSource(f); + int magicValue = apk.getByteBuffer(0, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(); + if (magicValue == SigningCertificateLineage.MAGIC) { + return SigningCertificateLineage.readFromFile(inputLineageFile); + } else if (magicValue == ZIP_MAGIC) { + return SigningCertificateLineage.readFromApkFile(inputLineageFile); + } else { + throw new ParameterException("The input file is not a valid lineage file."); + } + } catch (IOException | ApkFormatException | IllegalArgumentException e) { + throw new ParameterException(e.getMessage()); + } + } + private static SignerParams processSignerParams(OptionsParser optionsParser) throws OptionsParser.OptionsException, ParameterException { SignerParams signerParams = new SignerParams(); diff --git a/src/apksigner/java/com/android/apksigner/help_lineage.txt b/src/apksigner/java/com/android/apksigner/help_lineage.txt index 0816176..3f4922d 100644 --- a/src/apksigner/java/com/android/apksigner/help_lineage.txt +++ b/src/apksigner/java/com/android/apksigner/help_lineage.txt @@ -10,6 +10,8 @@ has been migrated to the new signing certificate. --in Input SigningCertificateLineage. This file contains a binary representation of a SigningCertificateLineage object which contains the proof-of-rotation for different signing certificates. + An APK previously signed with a SigningCertificateLineage can also be + specified; the lineage will then be read from the signed data in the APK. --out File into which to put the binary representation of a SigningCertificateLineage object. diff --git a/src/apksigner/java/com/android/apksigner/help_rotate.txt b/src/apksigner/java/com/android/apksigner/help_rotate.txt index cd569e7..ff58372 100644 --- a/src/apksigner/java/com/android/apksigner/help_rotate.txt +++ b/src/apksigner/java/com/android/apksigner/help_rotate.txt @@ -10,7 +10,8 @@ new, for use in a key rotation scenario using APK Signature Scheme v3. a SigningCertificateLineage object, which contains the proof-of-rotation for different signing certificates. This can be used with APK Signature Scheme v3 to rotate the signing certificate for an APK. - + An APK previously signed with a SigningCertificateLineage can also be + specified; the lineage will then be read from the signed data in the APK. --out File into which to put the binary representation of a SigningCertificateLineage object. diff --git a/src/apksigner/java/com/android/apksigner/help_sign.txt b/src/apksigner/java/com/android/apksigner/help_sign.txt index 07d0f03..b9d0146 100644 --- a/src/apksigner/java/com/android/apksigner/help_sign.txt +++ b/src/apksigner/java/com/android/apksigner/help_sign.txt @@ -73,6 +73,9 @@ certificate. points in the lineage and will be used on older platform versions when the newest signer in the lineage is unsupported. + An APK previously signed with a SigningCertificateLineage + can also be specified; the lineage will then be read from + the signed data in the APK. -h, --help Show help about this command and exit diff --git a/src/main/java/com/android/apksig/SigningCertificateLineage.java b/src/main/java/com/android/apksig/SigningCertificateLineage.java index a27cbe9..54340d7 100644 --- a/src/main/java/com/android/apksig/SigningCertificateLineage.java +++ b/src/main/java/com/android/apksig/SigningCertificateLineage.java @@ -19,17 +19,21 @@ package com.android.apksig; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; import com.android.apksig.apk.ApkFormatException; +import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.SignatureAlgorithm; +import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage; import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage.SigningCertificateNode; import com.android.apksig.internal.util.AndroidSdkVersion; +import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.util.RandomAccessFileDataSink; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; +import com.android.apksig.zip.ZipFormatException; import java.io.File; import java.io.IOException; @@ -66,7 +70,7 @@ import java.util.List; */ public class SigningCertificateLineage { - private final static int MAGIC = 0x3eff39d1; + public final static int MAGIC = 0x3eff39d1; private final static int FIRST_VERSION = 1; @@ -157,8 +161,125 @@ public class SigningCertificateLineage { return new SigningCertificateLineage(minSdkVersion, parsedLineage); } - public static SigningCertificateLineage readFromApkFile(File apkFile) { - throw new UnsupportedOperationException("Not yet implemented"); + /** + * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3 + * signature block of the provided APK File. + * + * @throws IllegalArgumentException if the provided APK does not contain a V3 signature block, + * or if the V3 signature block does not contain a valid lineage. + */ + public static SigningCertificateLineage readFromApkFile(File apkFile) + throws IOException, ApkFormatException { + try (RandomAccessFile f = new RandomAccessFile(apkFile, "r")) { + DataSource apk = DataSources.asDataSource(f, 0, f.length()); + return readFromApkDataSource(apk); + } + } + + /** + * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3 + * signature block of the provided APK DataSource. + * + * @throws IllegalArgumentException if the provided APK does not contain a V3 signature block, + * or if the V3 signature block does not contain a valid lineage. + */ + public static SigningCertificateLineage readFromApkDataSource(DataSource apk) + throws IOException, ApkFormatException { + SignatureInfo signatureInfo; + try { + ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); + ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( + ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); + signatureInfo = + ApkSigningBlockUtils.findSignature(apk, zipSections, + V3SchemeSigner.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); + 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( + "The provided APK does not contain a valid lineage."); + } else if (lineages.size() > 1) { + result = consolidateLineages(lineages); + } else { + result = lineages.get(0); + } + return result; + } + + /** + * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the provided + * signed data portion of a signer in a V3 signature block. + * + * @throws IllegalArgumentException if the provided signed data does not contain a valid + * lineage. + */ + public static SigningCertificateLineage readFromSignedData(ByteBuffer signedData) + throws IOException, ApkFormatException { + // FORMAT: + // * length-prefixed sequence of length-prefixed digests: + // * length-prefixed sequence of certificates: + // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). + // * uint-32: minSdkVersion + // * uint-32: maxSdkVersion + // * length-prefixed sequence of length-prefixed additional attributes: + // * uint32: ID + // * (length - 4) bytes: value + // * uint32: Proof-of-rotation ID: 0x3ba06f8c + // * length-prefixed proof-of-rotation structure + // consume the digests through the maxSdkVersion to reach the lineage in the attributes + getLengthPrefixedSlice(signedData); + getLengthPrefixedSlice(signedData); + signedData.getInt(); + signedData.getInt(); + // iterate over the additional attributes adding any lineages to the List + ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData); + List<SigningCertificateLineage> lineages = new ArrayList<>(1); + while (additionalAttributes.hasRemaining()) { + ByteBuffer attribute = getLengthPrefixedSlice(additionalAttributes); + int id = attribute.getInt(); + if (id == V3SchemeSigner.PROOF_OF_ROTATION_ATTR_ID) { + byte[] value = ByteBufferUtils.toByteArray(attribute); + SigningCertificateLineage lineage = readFromV3AttributeValue(value); + lineages.add(lineage); + } + } + SigningCertificateLineage result; + // There should only be a single attribute with the lineage, but if there are multiple then + // attempt to consolidate the lineages. + if (lineages.isEmpty()) { + throw new IllegalArgumentException("The signed data does not contain a valid lineage."); + } else if (lineages.size() > 1) { + result = consolidateLineages(lineages); + } else { + result = lineages.get(0); + } + return result; } public void writeToFile(File file) throws IOException { 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 5545d6c..fc70a0a 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 @@ -59,7 +59,7 @@ import java.util.Map; */ public abstract class V3SchemeSigner { - private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; + public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; /** Hidden constructor to prevent instantiation. */ private V3SchemeSigner() {} diff --git a/src/test/java/com/android/apksig/AllTests.java b/src/test/java/com/android/apksig/AllTests.java index cd5c909..4a9243d 100644 --- a/src/test/java/com/android/apksig/AllTests.java +++ b/src/test/java/com/android/apksig/AllTests.java @@ -23,6 +23,7 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ ApkSignerTest.class, ApkVerifierTest.class, + SigningCertificateLineageTest.class, com.android.apksig.apk.AllTests.class, com.android.apksig.internal.AllTests.class, com.android.apksig.util.AllTests.class, diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java index 80f35ba..1434017 100644 --- a/src/test/java/com/android/apksig/ApkSignerTest.java +++ b/src/test/java/com/android/apksig/ApkSignerTest.java @@ -16,10 +16,12 @@ package com.android.apksig; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.apk.ApkFormatException; +import com.android.apksig.internal.util.ByteBufferDataSource; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSinks; import com.android.apksig.util.DataSource; @@ -35,6 +37,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SignatureException; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Test; @@ -50,6 +53,15 @@ public class ApkSignerTest { */ private static final boolean KEEP_FAILING_OUTPUT_AS_FILES = false; + // All signers with the same prefix and an _X suffix were signed with the private key of the + // (X-1) signer. + 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 LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME = + "rsa-2048-lineage-2-signers"; + public static void main(String[] params) throws Exception { File outDir = (params.length > 0) ? new File(params[0]) : new File("."); generateGoldenFiles(outDir); @@ -63,7 +75,13 @@ public class ApkSignerTest { throw new IOException("Failed to create directory: " + outDir); } List<ApkSigner.SignerConfig> rsa2048SignerConfig = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); + List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = Arrays.asList( + rsa2048SignerConfig.get(0), + getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); + SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( + ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); signGolden( "golden-unaligned-in.apk", @@ -83,58 +101,205 @@ public class ApkSignerTest { new File(outDir, "golden-unaligned-v1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(false)); + .setV2SigningEnabled(false) + .setV3SigningEnabled(false)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(false)); + .setV2SigningEnabled(false) + .setV3SigningEnabled(false)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(false)); + .setV2SigningEnabled(false) + .setV3SigningEnabled(false)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + + signGolden( + "golden-unaligned-in.apk", + new File(outDir, "golden-unaligned-v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true)); + signGolden( + "golden-legacy-aligned-in.apk", + new File(outDir, "golden-legacy-aligned-v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true)); + signGolden( + "golden-aligned-in.apk", + new File(outDir, "golden-aligned-v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true)); + + signGolden( + "golden-unaligned-in.apk", + new File(outDir, "golden-unaligned-v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-legacy-aligned-in.apk", + new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-aligned-in.apk", + new File(outDir, "golden-aligned-v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v1v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v1v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v1v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + + signGolden( + "golden-unaligned-in.apk", + new File(outDir, "golden-unaligned-v2v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + signGolden( + "golden-legacy-aligned-in.apk", + new File(outDir, "golden-legacy-aligned-v2v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + signGolden( + "golden-aligned-in.apk", + new File(outDir, "golden-aligned-v2v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + signGolden( + "golden-unaligned-in.apk", + new File(outDir, "golden-unaligned-v2v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-legacy-aligned-in.apk", + new File(outDir, "golden-legacy-aligned-v2v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-aligned-in.apk", + new File(outDir, "golden-aligned-v2v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-unaligned-in.apk", + new File(outDir, "golden-unaligned-v1v2v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + signGolden( + "golden-legacy-aligned-in.apk", + new File(outDir, "golden-legacy-aligned-v1v2v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + signGolden( + "golden-aligned-in.apk", + new File(outDir, "golden-aligned-v1v2v3-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + signGolden( + "golden-unaligned-in.apk", + new File(outDir, "golden-unaligned-v1v2v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-legacy-aligned-in.apk", + new File(outDir, "golden-legacy-aligned-v1v2v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + signGolden( + "golden-aligned-in.apk", + new File(outDir, "golden-aligned-v1v2v3-lineage-out.apk"), + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); signGolden( "original.apk", new File(outDir, "golden-rsa-out.apk"), @@ -169,8 +334,13 @@ public class ApkSignerTest { // NOTE: Expected output files can be re-generated by running the "main" method. List<ApkSigner.SignerConfig> rsa2048SignerConfig = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); - + Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); + List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = Arrays.asList( + rsa2048SignerConfig.get(0), + getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); + SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), + LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); // Uncompressed entries in this input file are not aligned -- the file was created using // the jar utility. temp4.txt entry was then manually added into the archive. This entry's // ZIP Local File Header "extra" field declares that the entry's data must be aligned to @@ -182,17 +352,59 @@ public class ApkSignerTest { "golden-unaligned-in.apk", "golden-unaligned-v1-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(false)); + .setV2SigningEnabled(false) + .setV3SigningEnabled(false)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + assertGolden( + "golden-unaligned-in.apk", "golden-unaligned-v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true)); + assertGolden( + "golden-unaligned-in.apk", "golden-unaligned-v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v1v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + assertGolden( + "golden-unaligned-in.apk", "golden-unaligned-v2v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + assertGolden( + "golden-unaligned-in.apk", "golden-unaligned-v2v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + assertGolden( + "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + assertGolden( + "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as // performed by zipalign at the time of writing. This padding technique produces ZIP @@ -205,17 +417,59 @@ public class ApkSignerTest { "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(false)); + .setV2SigningEnabled(false) + .setV3SigningEnabled(false)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + assertGolden( + "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true)); + assertGolden( + "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + assertGolden( + "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + assertGolden( + "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + assertGolden( + "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + assertGolden( + "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); // Uncompressed entries in this input file are aligned by padding the "extra" field, as // generated by signapk and apksigner. This padding technique produces "extra" fields which @@ -227,17 +481,59 @@ public class ApkSignerTest { "golden-aligned-in.apk", "golden-aligned-v1-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(false)); + .setV2SigningEnabled(false) + .setV3SigningEnabled(false)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + assertGolden( + "golden-aligned-in.apk", "golden-aligned-v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true)); + assertGolden( + "golden-aligned-in.apk", "golden-aligned-v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v1v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) - .setV2SigningEnabled(true)); + .setV2SigningEnabled(true) + .setV3SigningEnabled(false)); + assertGolden( + "golden-aligned-in.apk", "golden-aligned-v2v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + assertGolden( + "golden-aligned-in.apk", "golden-aligned-v2v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + assertGolden( + "golden-aligned-in.apk", "golden-aligned-v1v2v3-out.apk", + new ApkSigner.Builder(rsa2048SignerConfig) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + assertGolden( + "golden-aligned-in.apk", "golden-aligned-v1v2v3-lineage-out.apk", + new ApkSigner.Builder(rsa2048SignerConfigWithLineage) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); } @Test @@ -245,8 +541,8 @@ public class ApkSignerTest { // Regression tests for minSdkVersion-based signature/digest algorithm selection // NOTE: Expected output files can be re-generated by running the "main" method. - List<ApkSigner.SignerConfig> rsaSignerConfig = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + List<ApkSigner.SignerConfig> rsaSignerConfig = Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertGolden("original.apk", "golden-rsa-out.apk", new ApkSigner.Builder(rsaSignerConfig)); assertGolden( "original.apk", "golden-rsa-minSdkVersion-1-out.apk", @@ -265,8 +561,8 @@ public class ApkSignerTest { @Test public void testRsaSignedVerifies() throws Exception { - List<ApkSigner.SignerConfig> signers = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + List<ApkSigner.SignerConfig> signers = Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); String in = "original.apk"; // Sign so that the APK is guaranteed to verify on API Level 1+ @@ -318,8 +614,8 @@ public class ApkSignerTest { public void testV1SigningRejectsInvalidZipEntryNames() throws Exception { // ZIP/JAR entry name cannot contain CR, LF, or NUL characters when the APK is being // JAR-signed. - List<ApkSigner.SignerConfig> signers = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + List<ApkSigner.SignerConfig> signers = Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); try { sign("v1-only-with-cr-in-entry-name.apk", new ApkSigner.Builder(signers).setV1SigningEnabled(true)); @@ -344,8 +640,8 @@ public class ApkSignerTest { // Any ZIP compression method other than STORED is treated as DEFLATED by Android. // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry, // but the entry is actually Deflate-compressed. - List<ApkSigner.SignerConfig> signers = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + List<ApkSigner.SignerConfig> signers = Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); sign("weird-compression-method.apk", new ApkSigner.Builder(signers)); } @@ -355,8 +651,8 @@ public class ApkSignerTest { // uses the compressionMethod from Central Directory instead. // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed. - List<ApkSigner.SignerConfig> signers = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + List<ApkSigner.SignerConfig> signers = Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); sign("mismatched-compression-method.apk", new ApkSigner.Builder(signers)); } @@ -364,8 +660,8 @@ public class ApkSignerTest { public void testDebuggableApk() throws Exception { // APK which uses a boolean value "true" in its android:debuggable String apk = "debuggable-boolean.apk"; - List<ApkSigner.SignerConfig> signers = - Collections.singletonList(getDefaultSignerConfigFromResources("rsa-2048")); + List<ApkSigner.SignerConfig> signers = Collections.singletonList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); // Signing debuggable APKs is permitted by default sign(apk, new ApkSigner.Builder(signers)); // Signing debuggable APK succeeds when explicitly requested @@ -391,6 +687,125 @@ public class ApkSignerTest { } catch (SignatureException expected) {} } + @Test(expected = IllegalStateException.class) + public void testV3SigningWithSignersNotInLineageFails() throws Exception { + // APKs signed with the v3 scheme after a key rotation must specify the lineage containing + // the proof of rotation. This test verifies that the signing will fail if the provided + // signers are not in the specified lineage. + List<ApkSigner.SignerConfig> signers = Arrays.asList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), + getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); + SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), + "rsa-1024-lineage-2-signers"); + sign("original.apk", new ApkSigner.Builder(signers).setSigningCertificateLineage(lineage)); + } + + @Test + public void testSigningWithLineageRequiresOldestSignerForV1AndV2() throws Exception { + // After a key rotation the oldest signer must still be specified for v1 and v2 signing. + // The lineage contains the proof of rotation and will be used to determine the oldest + // signer. + ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources( + FIRST_RSA_2048_SIGNER_RESOURCE_NAME); + ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources( + SECOND_RSA_2048_SIGNER_RESOURCE_NAME); + ApkSigner.SignerConfig thirdSigner = getDefaultSignerConfigFromResources( + THIRD_RSA_2048_SIGNER_RESOURCE_NAME); + SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), + "rsa-2048-lineage-3-signers"); + + // Verifies that the v1 signing scheme requires the oldest signer after a key rotation. + List<ApkSigner.SignerConfig> signers = Collections.singletonList(thirdSigner); + try { + sign("original.apk", new ApkSigner.Builder(signers) + .setV1SigningEnabled(true) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + fail("The signing should have failed due to the oldest signer in the lineage not being" + + " provided for v1 signing"); + } catch (IllegalArgumentException expected) {} + + // Verifies that the v2 signing scheme requires the oldest signer after a key rotation. + try { + sign("original.apk", new ApkSigner.Builder(signers) + .setV1SigningEnabled(false) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + fail("The signing should have failed due to the oldest signer in the lineage not being" + + " provided for v2 signing"); + } catch (IllegalArgumentException expected) {} + + // Verifies that when only the v3 signing scheme is requested the oldest signer does not + // need to be provided. + sign("original.apk", new ApkSigner.Builder(signers) + .setV1SigningEnabled(false) + .setV2SigningEnabled(false) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + + // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the + // requirement that the oldest signer be provided for v1 and v2 signing. + signers = Arrays.asList(secondSigner, thirdSigner); + try { + sign("original.apk", new ApkSigner.Builder(signers) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + fail("The signing should have failed due to the oldest signer in the lineage not being" + + " provided for v1/v2 signing"); + } catch (IllegalArgumentException expected) {} + + // Verifies that the signing is successful when the oldest and newest signers are provided + // and that intermediate signers are not required. + signers = Arrays.asList(firstSigner, thirdSigner); + sign("original.apk", new ApkSigner.Builder(signers) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + } + + @Test(expected = IllegalStateException.class) + public void testV3SigningWithMultipleSignersAndNoLineageFails() throws Exception { + // The v3 signing scheme does not support multiple signers; if multiple signers are provided + // it is assumed these signers are part of the lineage. This test verifies v3 signing + // fails if multiple signers are provided without a lineage. + ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources( + FIRST_RSA_2048_SIGNER_RESOURCE_NAME); + ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources( + SECOND_RSA_2048_SIGNER_RESOURCE_NAME); + List<ApkSigner.SignerConfig> signers = Arrays.asList(firstSigner, secondSigner); + sign("original.apk", new ApkSigner.Builder(signers) + .setV1SigningEnabled(true) + .setV2SigningEnabled(true) + .setV3SigningEnabled(true)); + } + + @Test + public void testLineageCanBeReadAfterV3Signing() throws Exception { + SigningCertificateLineage.SignerConfig firstSigner = Resources.toLineageSignerConfig( + getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); + SigningCertificateLineage.SignerConfig secondSigner = Resources.toLineageSignerConfig( + getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME); + SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(firstSigner, + secondSigner).build(); + List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList( + getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), + getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); + DataSource out = sign("original.apk", new ApkSigner.Builder(signerConfigs) + .setV3SigningEnabled(true) + .setSigningCertificateLineage(lineage)); + SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource( + out); + assertTrue("The first signer was not in the lineage from the signed APK", + lineageFromApk.isSignerInLineage((firstSigner))); + assertTrue("The second signer was not in the lineage from the signed APK", + lineageFromApk.isSignerInLineage((secondSigner))); + } + /** * Asserts that signing the specified golden input file using the provided signing * configuration produces output identical to the specified golden output file. diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java index 6f6c04d..1f9e208 100644 --- a/src/test/java/com/android/apksig/ApkVerifierTest.java +++ b/src/test/java/com/android/apksig/ApkVerifierTest.java @@ -258,6 +258,15 @@ public class ApkVerifierTest { } @Test + public void testV3StrippedRejected() throws Exception { + // APK signed with v2 and v3 schemes, but v3 signature was stripped from the file by + // modifying the v3 block ID to be the verity padding block ID. Without the stripping + // protection this modification ignores the v3 signing scheme block. + assertVerificationFailure( + "v3-stripped.apk", Issue.V2_SIG_MISSING_APK_SIG_REFERENCED); + } + + @Test public void testV2OneSignerOneSignatureAccepted() throws Exception { // APK signed with v2 scheme only, one signer, one signature assertVerifiedForEachForMinSdkVersion( @@ -278,6 +287,22 @@ public class ApkVerifierTest { } @Test + public void testV3OneSignerOneSignatureAccepted() throws Exception { + // APK signed with v3 scheme only, one signer, one signature + assertVerifiedForEachForMinSdkVersion( + "v3-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.P); + assertVerifiedForEachForMinSdkVersion( + "v3-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P); + assertVerifiedForEachForMinSdkVersion( + "v3-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P); + + assertVerifiedForEachForMinSdkVersion( + "v3-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P); + assertVerifiedForEachForMinSdkVersion( + "v3-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P); + } + + @Test public void testV2OneSignerOneRsaPssSignatureAccepted() throws Exception { assumeThatRsaPssAvailable(); // APK signed with v2 scheme only, one signer, one signature @@ -312,6 +337,27 @@ public class ApkVerifierTest { } @Test + public void testV3SignatureDoesNotMatchSignedDataRejected() throws Exception { + // APK signed with v3 scheme only, but the signature over signed-data does not verify + + // Bitflip in DSA signature. Based on v3-only-with-dsa-sha256-2048.apk. + assertVerificationFailure( + "v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk", + Issue.V3_SIG_DID_NOT_VERIFY); + + // Bitflip in signed data. Based on v3-only-with-rsa-pkcs1-sha256-3072.apk + assertVerificationFailure( + "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk", + Issue.V3_SIG_DID_NOT_VERIFY); + + // Based on v3-only-with-ecdsa-sha512-p521 with the signature ID changed to be ECDSA with + // SHA-256. + assertVerificationFailure( + "v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk", + Issue.V3_SIG_DID_NOT_VERIFY); + } + + @Test public void testV2RsaPssSignatureDoesNotMatchSignedDataRejected() throws Exception { assumeThatRsaPssAvailable(); @@ -343,6 +389,24 @@ public class ApkVerifierTest { } @Test + public void testV3ContentDigestMismatchRejected() throws Exception { + // APK signed with v3 scheme only, but the digest of contents does not match the digest + // stored in signed-data. + + // Based on v3-only-with-rsa-pkcs1-sha512-8192. Obtained by flipping a bit in the local + // file header of the APK. + assertVerificationFailure( + "v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", + Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY); + + // Based on v3-only-with-dsa-sha256-3072.apk. Obtained by modifying APK signer to flip the + // leftmost bit in content digest before signing signed-data. + assertVerificationFailure( + "v3-only-with-dsa-sha256-3072-digest-mismatch.apk", + Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY); + } + + @Test public void testNoApkSignatureSchemeBlockRejected() throws Exception { // APK signed with v2 scheme only, but the rules for verifying APK Signature Scheme v2 // signatures say that this APK must not be verified using APK Signature Scheme v2. @@ -373,6 +437,21 @@ public class ApkVerifierTest { assertVerified(verify("v1-with-apk-sig-block-but-without-apk-sig-scheme-v2-block.apk")); } + @Test + public void testNoV3ApkSignatureSchemeBlockRejected() throws Exception { + // Obtained from v3-only-with-ecdsa-sha512-p384.apk by flipping a bit in the magic field + // in the footer of the APK Signing Block. + assertVerificationFailure( + "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", + Issue.JAR_SIG_NO_MANIFEST); + + // Obtained from v3-only-with-rsa-pkcs1-sha512-4096.apk by modifying the size in the APK + // Signature Block header and footer. + assertVerificationFailure( + "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk", + Issue.JAR_SIG_NO_MANIFEST); + } + @Test(expected = ApkFormatException.class) public void testTruncatedZipCentralDirectoryRejected() throws Exception { // Obtained by modifying APK signer to truncate the ZIP Central Directory by one byte. The @@ -392,6 +471,16 @@ public class ApkVerifierTest { } @Test + public void testV3UnknownPairIgnoredInApkSigningBlock() throws Exception { + // Obtained by modifying APK signer to emit an unknown ID value pair into APK Signing Block + // before the ID value pair containing the APK Signature Scheme v3 Block. The unknown + // ID value should be ignored. + assertVerified( + verifyForMinSdkVersion( + "v3-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.P)); + } + + @Test public void testV2UnknownSignatureAlgorithmsIgnored() throws Exception { // APK is signed with a known signature algorithm and with a couple of unknown ones. // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to @@ -402,6 +491,24 @@ public class ApkVerifierTest { } @Test + public void testV3UnknownSignatureAlgorithmsIgnored() throws Exception { + // APK is signed with a known signature algorithm and a couple of unknown ones. + // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to + // known ones. + assertVerified( + verifyForMinSdkVersion( + "v3-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.P)); + } + + @Test + public void testV3WithOnlyUnknownSignatureAlgorithmsRejected() throws Exception { + // APK is only signed with an unknown signature algorithm. Obtained by modifying APK + // signer's ID for a known signature algorithm. + assertVerificationFailure( + "v3-only-no-supported-sig-algs.apk", Issue.V3_SIG_NO_SUPPORTED_SIGNATURES); + } + + @Test public void testV2UnknownAdditionalAttributeIgnored() throws Exception { // APK's v2 signature contains an unknown additional attribute, but is otherwise fine. // Obtained by modifying APK signer to output an additional attribute with ID 0x01020304 @@ -411,6 +518,19 @@ public class ApkVerifierTest { } @Test + public void testV3UnknownAdditionalAttributeIgnored() throws Exception { + // APK's v3 signature contains unknown additional attributes before and after the lineage. + // Obtained by modifying APK signer to output additional attributes with IDs 0x11223344 + // and 0x99aabbcc with values 0x55667788 and 0xddeeff00 + assertVerified( + verifyForMinSdkVersion("v3-only-unknown-additional-attr.apk", AndroidSdkVersion.P)); + + // APK's v2 and v3 signatures contain unknown additional attributes before and after the + // anti-stripping and lineage attributes. + assertVerified( + verifyForMinSdkVersion("v2v3-unknown-additional-attr.apk", AndroidSdkVersion.P)); } + + @Test public void testV2MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception { // APK is signed with a single signature algorithm, but the digests block claims that it is // signed with two different signature algorithms. Obtained by modifying APK Signer to @@ -421,6 +541,16 @@ public class ApkVerifierTest { } @Test + public void testV3MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception { + // APK is signed with a single signature algorithm, but the digests block claims that it is + // signed with two different signature algorithms. Obtained by modifying APK Signer to + // emit an additional digest record with signature algorithm 0x11223344. + assertVerificationFailure( + "v3-only-signatures-and-digests-block-mismatch.apk", + Issue.V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS); + } + + @Test public void testV2MismatchBetweenPublicKeyAndCertificateRejected() throws Exception { // APK is signed with v2 only. The public key field does not match the public key in the // leaf certificate. Obtained by modifying APK signer to write out a modified leaf @@ -431,6 +561,16 @@ public class ApkVerifierTest { } @Test + public void testV3MismatchBetweenPublicKeyAndCertificateRejected() throws Exception { + // APK is signed with v3 only. The public key field does not match the public key in the + // leaf certificate. Obtained by modifying APK signer to write out a modified leaf + // certificate where the RSA modulus has a bitflip. + assertVerificationFailure( + "v3-only-cert-and-public-key-mismatch.apk", + Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD); + } + + @Test public void testV2SignerBlockWithNoCertificatesRejected() throws Exception { // APK is signed with v2 only. There are no certificates listed in the signer block. // Obtained by modifying APK signer to output no certificates. @@ -439,6 +579,14 @@ public class ApkVerifierTest { } @Test + public void testV3SignerBlockWithNoCertificatesRejected() throws Exception { + // APK is signed with v3 only. There are no certificates listed in the signer block. + // Obtained by modifying APK signer to output no certificates. + assertVerificationFailure( + "v3-only-no-certs-in-sig.apk", Issue.V3_SIG_NO_CERTIFICATES); + } + + @Test public void testTwoSignersAccepted() throws Exception { // APK signed by two different signers assertVerified(verify("two-signers.apk")); @@ -593,6 +741,12 @@ public class ApkVerifierTest { verifyForMinSdkVersion("v2-only-empty.apk", AndroidSdkVersion.N); fail("ApkFormatException should've been thrown"); } catch (ApkFormatException expected) {} + + // APK Signature Scheme v3 signed empty ZIP archive + try { + verifyForMinSdkVersion("v3-only-empty.apk", AndroidSdkVersion.P); + fail("ApkFormatException should've been thrown"); + } catch (ApkFormatException expected) {} } @Test @@ -982,6 +1136,19 @@ public class ApkVerifierTest { .append(signerName).append(": ").append(issue); } } + for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { + String signerName = "signer #" + (signer.getIndex() + 1); + for (IssueWithParams issue : signer.getErrors()) { + if (expectedIssue.equals(issue.getIssue())) { + return; + } + if (msg.length() > 0) { + msg.append('\n'); + } + msg.append("APK Signature Scheme v3 signer ") + .append(signerName).append(": ").append(issue); + } + } fail("APK failed verification for the wrong reason" + ". Expected: " + expectedIssue + ", actual: " + msg); diff --git a/src/test/java/com/android/apksig/SigningCertificateLineageTest.java b/src/test/java/com/android/apksig/SigningCertificateLineageTest.java index 2f6c10d..2038421 100644 --- a/src/test/java/com/android/apksig/SigningCertificateLineageTest.java +++ b/src/test/java/com/android/apksig/SigningCertificateLineageTest.java @@ -19,7 +19,9 @@ package com.android.apksig; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.util.ByteBufferDataSource; @@ -76,7 +78,7 @@ public class SigningCertificateLineageTest { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(lineage, mSigners); - SignerConfig unknownSigner = getSignerConfigFromResources( + SignerConfig unknownSigner = Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertFalse("The signer " + unknownSigner.getCertificate().getSubjectDN() + " should not be in the lineage", lineage.isSignerInLineage(unknownSigner)); @@ -94,21 +96,26 @@ public class SigningCertificateLineageTest { @Test public void testLineageFromFileContainsExpectedSigners() throws Exception { // This file contains the lineage with the three rsa-2048 signers - DataSource lineageDataSource = getDataSourceFromResources("rsa-2048-lineage-3-signers"); + DataSource lineageDataSource = Resources.toDataSource(getClass(), + "rsa-2048-lineage-3-signers"); SigningCertificateLineage lineage = SigningCertificateLineage.readFromDataSource( lineageDataSource); List<SignerConfig> signers = new ArrayList<>(3); - signers.add(getSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); - signers.add(getSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); - signers.add(getSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); + signers.add( + Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); + signers.add( + Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); + signers.add( + Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertLineageContainsExpectedSigners(lineage, signers); } @Test public void testLineageFromFileDoesNotContainUnknownSigner() throws Exception { // This file contains the lineage with the first two rsa-2048 signers - SigningCertificateLineage lineage = getLineageFromResources("rsa-2048-lineage-2-signers"); - SignerConfig unknownSigner = getSignerConfigFromResources( + SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), + "rsa-2048-lineage-2-signers"); + SignerConfig unknownSigner = Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertFalse("The signer " + unknownSigner.getCertificate().getSubjectDN() + " should not be in the lineage", lineage.isSignerInLineage(unknownSigner)); @@ -117,14 +124,14 @@ public class SigningCertificateLineageTest { @Test(expected = IllegalArgumentException.class) public void testLineageFromFileWithInvalidMagicFails() throws Exception { // This file contains the lineage with two rsa-2048 signers and a modified MAGIC value - getLineageFromResources("rsa-2048-lineage-invalid-magic"); + Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-invalid-magic"); } @Test(expected = IllegalArgumentException.class) public void testLineageFromFileWithInvalidVersionFails() throws Exception { // This file contains the lineage with two rsa-2048 signers and an invalid value of FF for // the version - getLineageFromResources("rsa-2048-lineage-invalid-version"); + Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-invalid-version"); } @Test @@ -172,10 +179,11 @@ public class SigningCertificateLineageTest { public void testCapabilitiesAreNotUpdatedWithDefaultValues() throws Exception { // This file contains the lineage with the first two rsa-2048 signers with the first signer // having all of the capabilities set to false. - SigningCertificateLineage lineage = getLineageFromResources( + SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-no-capabilities-first-signer"); List<Boolean> expectedCapabilityValues = Arrays.asList(false, false, false, false, false); - SignerConfig oldSignerConfig = getSignerConfigFromResources("rsa-2048"); + SignerConfig oldSignerConfig = Resources.toLineageSignerConfig(getClass(), + FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SignerCapabilities oldSignerCapabilities = lineage.getSignerCapabilities(oldSignerConfig); assertExpectedCapabilityValues(oldSignerCapabilities, expectedCapabilityValues); // The builder is called directly to ensure all of the capabilities are set to the default @@ -188,8 +196,10 @@ public class SigningCertificateLineageTest { @Test public void testFirstRotationWitNonDefaultCapabilitiesForSigners() throws Exception { - SignerConfig oldSigner = getSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); - SignerConfig newSigner = getSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); + SignerConfig oldSigner = Resources.toLineageSignerConfig(getClass(), + FIRST_RSA_2048_SIGNER_RESOURCE_NAME); + SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(), + SECOND_RSA_2048_SIGNER_RESOURCE_NAME); List<Boolean> oldSignerCapabilityValues = Arrays.asList(false, false, false, false, false); List<Boolean> newSignerCapabilityValues = Arrays.asList(false, true, false, false, false); SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(oldSigner, @@ -209,7 +219,8 @@ public class SigningCertificateLineageTest { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig oldSigner = mSigners.get(mSigners.size() - 1); - SignerConfig newSigner = getSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); + SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(), + THIRD_RSA_2048_SIGNER_RESOURCE_NAME); List<Boolean> newSignerCapabilityValues = Arrays.asList(false, false, false, false, false); lineage = lineage.spawnDescendant(oldSigner, newSigner, buildSignerCapabilities(newSignerCapabilityValues)); @@ -225,7 +236,8 @@ public class SigningCertificateLineageTest { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig oldestSigner = mSigners.get(0); - SignerConfig newSigner = getSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); + SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(), + THIRD_RSA_2048_SIGNER_RESOURCE_NAME); lineage.spawnDescendant(oldestSigner, newSigner); } @@ -344,6 +356,66 @@ public class SigningCertificateLineageTest { SigningCertificateLineage.consolidateLineages(lineages); } + @Test + public void testLineageFromAPKContainsExpectedSigners() throws Exception { + SignerConfig firstSigner = getSignerConfigFromResources( + FIRST_RSA_2048_SIGNER_RESOURCE_NAME); + SignerConfig secondSigner = getSignerConfigFromResources( + SECOND_RSA_2048_SIGNER_RESOURCE_NAME); + SignerConfig thirdSigner = getSignerConfigFromResources( + THIRD_RSA_2048_SIGNER_RESOURCE_NAME); + List<SignerConfig> expectedSigners = Arrays.asList(firstSigner, secondSigner, thirdSigner); + DataSource apkDataSource = Resources.toDataSource(getClass(), + "v1v2v3-with-rsa-2048-lineage-3-signers.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 + // sections cannot be parsed fails. This APK is based off the + // v1v2v3-with-rsa-2048-lineage-3-signers.apk with a modified CD size in the EoCD. + DataSource apkDataSource = Resources.toDataSource(getClass(), + "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk"); + SigningCertificateLineage.readFromApkDataSource(apkDataSource); + } + + @Test + public void testLineageFromAPKWithNoLineageFails() throws Exception { + // This test verifies that attempting to read the lineage from an APK without a lineage + // fails. + // This is a valid APK that has only been signed with the V1 and V2 signature schemes; + // since the lineage is an attribute in the V3 signature block this test should fail. + DataSource apkDataSource = Resources.toDataSource(getClass(), + "golden-aligned-v1v2-out.apk"); + try { + SigningCertificateLineage.readFromApkDataSource(apkDataSource); + fail("A failure should have been reported due to the APK not containing a V3 signing " + + "block"); + } catch (IllegalArgumentException expected) {} + + // This is a valid APK signed with the V1, V2, and V3 signature schemes, but there is no + // lineage in the V3 signature block. + apkDataSource = Resources.toDataSource(getClass(), "golden-aligned-v1v2v3-out.apk"); + try { + SigningCertificateLineage.readFromApkDataSource(apkDataSource); + fail("A failure should have been reported due to the APK containing a V3 signing " + + "block without the lineage attribute"); + } catch (IllegalArgumentException expected) {} + + // This APK is based off the v1v2v3-with-rsa-2048-lineage-3-signers.apk with a bit flip + // in the lineage attribute ID in the V3 signature block. + apkDataSource = Resources.toDataSource(getClass(), + "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk"); + try { + SigningCertificateLineage.readFromApkDataSource(apkDataSource); + fail("A failure should have been reported due to the APK containing a V3 signing " + + "block with a modified lineage attribute ID"); + } catch (IllegalArgumentException expected) {} + } + /** * Builds a new {@code SigningCertificateLinage.SignerCapabilities} object using the values in * the provided {@code List}. The {@code List} should contain {@code boolean} values to be @@ -410,10 +482,10 @@ public class SigningCertificateLineageTest { */ private SigningCertificateLineage createLineageWithSignersFromResources( String oldSignerResourceName, String newSignerResourceName) throws Exception { - SignerConfig oldSignerConfig = getSignerConfigFromResources( + SignerConfig oldSignerConfig = Resources.toLineageSignerConfig(getClass(), oldSignerResourceName); mSigners.add(oldSignerConfig); - SignerConfig newSignerConfig = getSignerConfigFromResources( + SignerConfig newSignerConfig = Resources.toLineageSignerConfig(getClass(), newSignerResourceName); mSigners.add(newSignerConfig); return new SigningCertificateLineage.Builder(oldSignerConfig, newSignerConfig).build(); @@ -432,7 +504,8 @@ public class SigningCertificateLineageTest { assertTrue("The mSigners list did not contain the expected signers to update the lineage", mSigners.size() >= 2); SignerConfig oldSignerConfig = mSigners.get(mSigners.size() - 1); - SignerConfig newSignerConfig = getSignerConfigFromResources(newSignerResourceName); + SignerConfig newSignerConfig = Resources.toLineageSignerConfig(getClass(), + newSignerResourceName); mSigners.add(newSignerConfig); return lineage.spawnDescendant(oldSignerConfig, newSignerConfig); } @@ -467,16 +540,4 @@ public class SigningCertificateLineageTest { return new DefaultApkSignerEngine.SignerConfig.Builder(resourcePrefix, privateKey, Collections.singletonList(cert)).build(); } - - private static DataSource getDataSourceFromResources(String dataSourceResourceName) - throws IOException { - return new ByteBufferDataSource(ByteBuffer.wrap(Resources - .toByteArray(SigningCertificateLineageTest.class, dataSourceResourceName))); - } - - private SigningCertificateLineage getLineageFromResources(String fileResourceName) - throws IOException { - DataSource lineageDataSource = getDataSourceFromResources(fileResourceName); - return SigningCertificateLineage.readFromDataSource(lineageDataSource); - } } diff --git a/src/test/java/com/android/apksig/internal/util/Resources.java b/src/test/java/com/android/apksig/internal/util/Resources.java index 8a761ff..09c206d 100644 --- a/src/test/java/com/android/apksig/internal/util/Resources.java +++ b/src/test/java/com/android/apksig/internal/util/Resources.java @@ -16,8 +16,13 @@ package com.android.apksig.internal.util; +import com.android.apksig.ApkSignerTest; +import com.android.apksig.SigningCertificateLineage; +import com.android.apksig.util.DataSource; + import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -109,4 +114,24 @@ public final class Resources { return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); } + + public static SigningCertificateLineage.SignerConfig toLineageSignerConfig(Class<?> cls, + String resourcePrefix) throws Exception { + PrivateKey privateKey = toPrivateKey(cls, resourcePrefix + ".pk8"); + X509Certificate cert = Resources.toCertificate(cls, + resourcePrefix + ".x509.pem"); + return new SigningCertificateLineage.SignerConfig.Builder(privateKey, cert).build(); + } + + public static DataSource toDataSource(Class<?> cls, String dataSourceResourceName) + throws IOException { + return new ByteBufferDataSource(ByteBuffer.wrap(Resources + .toByteArray(ApkSignerTest.class, dataSourceResourceName))); + } + + public static SigningCertificateLineage toSigningCertificateLineage(Class<?> cls, + String fileResourceName) throws IOException { + DataSource lineageDataSource = toDataSource(cls, fileResourceName); + return SigningCertificateLineage.readFromDataSource(lineageDataSource); + } } diff --git a/src/test/resources/com/android/apksig/golden-aligned-out.apk b/src/test/resources/com/android/apksig/golden-aligned-out.apk Binary files differindex ca7041b..2396782 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v1-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v1-out.apk Binary files differindex e413b4e..403e45a 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-v1-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-v1-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v1v2-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v1v2-out.apk Binary files differindex ca7041b..5133049 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-v1v2-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-v1v2-out.apk 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 differnew file mode 100644 index 0000000..b9dc782 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-out.apk Binary files differnew file mode 100644 index 0000000..2396782 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-aligned-v1v2v3-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v2-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v2-out.apk Binary files differindex 664d198..d947e3c 100644 --- a/src/test/resources/com/android/apksig/golden-aligned-v2-out.apk +++ b/src/test/resources/com/android/apksig/golden-aligned-v2-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 differnew file mode 100644 index 0000000..88c571b --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-aligned-v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v2v3-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v2v3-out.apk Binary files differnew file mode 100644 index 0000000..25f35cc --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-aligned-v2v3-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 differnew file mode 100644 index 0000000..30e1f72 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-aligned-v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-aligned-v3-out.apk b/src/test/resources/com/android/apksig/golden-aligned-v3-out.apk Binary files differnew file mode 100644 index 0000000..f97cbeb --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-aligned-v3-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-out.apk Binary files differindex fbae100..d177361 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v1-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1-out.apk Binary files differindex bdc2165..e8a0bf2 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-v1-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2-out.apk Binary files differindex fbae100..cc03744 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2-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 differnew file mode 100644 index 0000000..e359da7 --- /dev/null +++ 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-v1v2v3-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-out.apk Binary files differnew file mode 100644 index 0000000..d177361 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v2-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v2-out.apk Binary files differindex 5956da5..68f07ed 100644 --- a/src/test/resources/com/android/apksig/golden-legacy-aligned-v2-out.apk +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v2-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 differnew file mode 100644 index 0000000..4b51e4f --- /dev/null +++ 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-v2v3-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-out.apk Binary files differnew file mode 100644 index 0000000..7177862 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-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 differnew file mode 100644 index 0000000..bd3e668 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-out.apk b/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-out.apk Binary files differnew file mode 100644 index 0000000..67a7d3f --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-legacy-aligned-v3-out.apk diff --git a/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-1-out.apk b/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-1-out.apk Binary files differindex 1269499..7289853 100644 --- a/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-1-out.apk +++ b/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-1-out.apk diff --git a/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-18-out.apk b/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-18-out.apk Binary files differindex 9bfe23b..232db96 100644 --- a/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-18-out.apk +++ b/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-18-out.apk diff --git a/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-24-out.apk b/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-24-out.apk Binary files differindex 9bfe23b..232db96 100644 --- a/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-24-out.apk +++ b/src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-24-out.apk diff --git a/src/test/resources/com/android/apksig/golden-rsa-out.apk b/src/test/resources/com/android/apksig/golden-rsa-out.apk Binary files differindex 9bfe23b..232db96 100644 --- a/src/test/resources/com/android/apksig/golden-rsa-out.apk +++ b/src/test/resources/com/android/apksig/golden-rsa-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-out.apk Binary files differindex 1b10755..0bd34c4 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v1-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v1-out.apk Binary files differindex 1309566..6ddc448 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-v1-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-v1-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v1v2-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v1v2-out.apk Binary files differindex 1b10755..c708211 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-v1v2-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-v1v2-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 differnew file mode 100644 index 0000000..dd6324b --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-out.apk Binary files differnew file mode 100644 index 0000000..0bd34c4 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v2-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v2-out.apk Binary files differindex bd56d9f..4fdc18c 100644 --- a/src/test/resources/com/android/apksig/golden-unaligned-v2-out.apk +++ b/src/test/resources/com/android/apksig/golden-unaligned-v2-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 differnew file mode 100644 index 0000000..4e523ca --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-unaligned-v2v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v2v3-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v2v3-out.apk Binary files differnew file mode 100644 index 0000000..74e7dbc --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-unaligned-v2v3-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 differnew file mode 100644 index 0000000..831c756 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-unaligned-v3-lineage-out.apk diff --git a/src/test/resources/com/android/apksig/golden-unaligned-v3-out.apk b/src/test/resources/com/android/apksig/golden-unaligned-v3-out.apk Binary files differnew file mode 100644 index 0000000..3196267 --- /dev/null +++ b/src/test/resources/com/android/apksig/golden-unaligned-v3-out.apk diff --git a/src/test/resources/com/android/apksig/rsa-1024-lineage-2-signers b/src/test/resources/com/android/apksig/rsa-1024-lineage-2-signers Binary files differnew file mode 100644 index 0000000..4f4315c --- /dev/null +++ b/src/test/resources/com/android/apksig/rsa-1024-lineage-2-signers diff --git a/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk Binary files differnew file mode 100644 index 0000000..1bd3828 --- /dev/null +++ b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk diff --git a/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk Binary files differnew file mode 100644 index 0000000..2621f21 --- /dev/null +++ b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk diff --git a/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-block.apk b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-block.apk Binary files differnew file mode 100644 index 0000000..17dea33 --- /dev/null +++ b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-block.apk diff --git a/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers.apk b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers.apk Binary files differnew file mode 100644 index 0000000..fd141b5 --- /dev/null +++ b/src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers.apk diff --git a/src/test/resources/com/android/apksig/v2v3-signed-v3-block-stripped.apk b/src/test/resources/com/android/apksig/v2v3-signed-v3-block-stripped.apk Binary files differnew file mode 100644 index 0000000..751c67d --- /dev/null +++ b/src/test/resources/com/android/apksig/v2v3-signed-v3-block-stripped.apk diff --git a/src/test/resources/com/android/apksig/v2v3-unknown-additional-attr.apk b/src/test/resources/com/android/apksig/v2v3-unknown-additional-attr.apk Binary files differnew file mode 100644 index 0000000..cb080f4 --- /dev/null +++ b/src/test/resources/com/android/apksig/v2v3-unknown-additional-attr.apk diff --git a/src/test/resources/com/android/apksig/v3-only-cert-and-public-key-mismatch.apk b/src/test/resources/com/android/apksig/v3-only-cert-and-public-key-mismatch.apk Binary files differnew file mode 100644 index 0000000..2291e7e --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-cert-and-public-key-mismatch.apk diff --git a/src/test/resources/com/android/apksig/v3-only-empty.apk b/src/test/resources/com/android/apksig/v3-only-empty.apk Binary files differnew file mode 100644 index 0000000..15cb0ec --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-empty.apk diff --git a/src/test/resources/com/android/apksig/v3-only-no-certs-in-sig.apk b/src/test/resources/com/android/apksig/v3-only-no-certs-in-sig.apk Binary files differnew file mode 100644 index 0000000..86e7971 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-no-certs-in-sig.apk diff --git a/src/test/resources/com/android/apksig/v3-only-no-supported-sig-algs.apk b/src/test/resources/com/android/apksig/v3-only-no-supported-sig-algs.apk Binary files differnew file mode 100644 index 0000000..f0debf3 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-no-supported-sig-algs.apk diff --git a/src/test/resources/com/android/apksig/v3-only-signatures-and-digests-block-mismatch.apk b/src/test/resources/com/android/apksig/v3-only-signatures-and-digests-block-mismatch.apk Binary files differnew file mode 100644 index 0000000..31aea2f --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-signatures-and-digests-block-mismatch.apk diff --git a/src/test/resources/com/android/apksig/v3-only-unknown-additional-attr.apk b/src/test/resources/com/android/apksig/v3-only-unknown-additional-attr.apk Binary files differnew file mode 100644 index 0000000..2245922 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-unknown-additional-attr.apk diff --git a/src/test/resources/com/android/apksig/v3-only-unknown-pair-in-apk-sig-block.apk b/src/test/resources/com/android/apksig/v3-only-unknown-pair-in-apk-sig-block.apk Binary files differnew file mode 100644 index 0000000..49eeaf3 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-unknown-pair-in-apk-sig-block.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-1024.apk b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-1024.apk Binary files differnew file mode 100644 index 0000000..af6b0d7 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-1024.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk Binary files differnew file mode 100644 index 0000000..50dbab2 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048.apk b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048.apk Binary files differnew file mode 100644 index 0000000..3d2161e --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-3072-digest-mismatch.apk b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-3072-digest-mismatch.apk Binary files differnew file mode 100644 index 0000000..42f885b --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-3072-digest-mismatch.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-3072.apk b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-3072.apk Binary files differnew file mode 100644 index 0000000..c58902d --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-3072.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p256.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p256.apk Binary files differnew file mode 100644 index 0000000..5ef4fec --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p256.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p384.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p384.apk Binary files differnew file mode 100644 index 0000000..75135af --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p384.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p521.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p521.apk Binary files differnew file mode 100644 index 0000000..74071f0 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p521.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p256.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p256.apk Binary files differnew file mode 100644 index 0000000..543c1f3 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p256.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk Binary files differnew file mode 100644 index 0000000..ce79751 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384.apk Binary files differnew file mode 100644 index 0000000..36fa0ee --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk Binary files differnew file mode 100644 index 0000000..8e89c98 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521.apk b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521.apk Binary files differnew file mode 100644 index 0000000..b74b4fb --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-ignorable-unsupported-sig-algs.apk b/src/test/resources/com/android/apksig/v3-only-with-ignorable-unsupported-sig-algs.apk Binary files differnew file mode 100644 index 0000000..88ae376 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-ignorable-unsupported-sig-algs.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-1024.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-1024.apk Binary files differnew file mode 100644 index 0000000..7a62c24 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-1024.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-16384.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-16384.apk Binary files differnew file mode 100644 index 0000000..825cfba --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-16384.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-2048.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-2048.apk Binary files differnew file mode 100644 index 0000000..1ab85f8 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-2048.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk Binary files differnew file mode 100644 index 0000000..ddaaccd --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072.apk Binary files differnew file mode 100644 index 0000000..8bcc82c --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-4096.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-4096.apk Binary files differnew file mode 100644 index 0000000..0c9391c --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-4096.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-8192.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-8192.apk Binary files differnew file mode 100644 index 0000000..41db21b --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-8192.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-1024.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-1024.apk Binary files differnew file mode 100644 index 0000000..776d366 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-1024.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-16384.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-16384.apk Binary files differnew file mode 100644 index 0000000..85146f1 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-16384.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-2048.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-2048.apk Binary files differnew file mode 100644 index 0000000..8b1b915 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-2048.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-3072.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-3072.apk Binary files differnew file mode 100644 index 0000000..5b364fd --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-3072.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk Binary files differnew file mode 100644 index 0000000..52d5a67 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096.apk Binary files differnew file mode 100644 index 0000000..c210b70 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk Binary files differnew file mode 100644 index 0000000..2800929 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk diff --git a/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192.apk b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192.apk Binary files differnew file mode 100644 index 0000000..3c2cc79 --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192.apk diff --git a/src/test/resources/com/android/apksig/v3-stripped.apk b/src/test/resources/com/android/apksig/v3-stripped.apk Binary files differnew file mode 100644 index 0000000..751c67d --- /dev/null +++ b/src/test/resources/com/android/apksig/v3-stripped.apk |