aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/android/apksig/ApkVerifier.java29
-rw-r--r--src/main/java/com/android/apksig/Constants.java5
-rw-r--r--src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java6
-rw-r--r--src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java6
-rw-r--r--src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java8
-rw-r--r--src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java5
-rw-r--r--src/test/java/com/android/apksig/ApkSignerTest.java100
-rw-r--r--src/test/java/com/android/apksig/ApkVerifierTest.java31
-rw-r--r--src/test/resources/com/android/apksig/v1-only-10-signers.apkbin0 -> 18389 bytes
-rw-r--r--src/test/resources/com/android/apksig/v1-only-11-signers.apkbin0 -> 22297 bytes
-rw-r--r--src/test/resources/com/android/apksig/v2-only-10-signers.apkbin0 -> 20688 bytes
-rw-r--r--src/test/resources/com/android/apksig/v2-only-11-signers.apkbin0 -> 24784 bytes
12 files changed, 190 insertions, 0 deletions
diff --git a/src/main/java/com/android/apksig/ApkVerifier.java b/src/main/java/com/android/apksig/ApkVerifier.java
index 354dfbd..c0cc9c5 100644
--- a/src/main/java/com/android/apksig/ApkVerifier.java
+++ b/src/main/java/com/android/apksig/ApkVerifier.java
@@ -1198,6 +1198,15 @@ public class ApkVerifier {
}
private void mergeFrom(ApkSigningBlockUtils.Result source) {
+ if (source == null) {
+ return;
+ }
+ if (source.containsErrors()) {
+ mErrors.addAll(source.getErrors());
+ }
+ if (source.containsWarnings()) {
+ mWarnings.addAll(source.getWarnings());
+ }
switch (source.signatureSchemeVersion) {
case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
mVerifiedUsingV2Scheme = source.verified;
@@ -1718,6 +1727,16 @@ public class ApkVerifier {
JAR_SIG_NO_SIGNATURES("No JAR signatures"),
/**
+ * APK signature scheme v1 has exceeded the maximum number of jar signers.
+ * <ul>
+ * <li>Parameter 1: maximum allowed signers ({@code Integer})</li>
+ * <li>Parameter 2: total number of signers ({@code Integer})</li>
+ * </ul>
+ */
+ JAR_SIG_MAX_SIGNATURES_EXCEEDED(
+ "APK Signature Scheme v1 only supports a maximum of %1$d signers, found %2$d"),
+
+ /**
* APK does not contain any entries covered by JAR signatures.
*/
JAR_SIG_NO_SIGNED_ZIP_ENTRIES("No JAR entries covered by JAR signatures"),
@@ -2159,6 +2178,16 @@ public class ApkVerifier {
+ "no such signature was found. Signature stripped?"),
/**
+ * APK signature scheme v2 has exceeded the maximum number of signers.
+ * <ul>
+ * <li>Parameter 1: maximum allowed signers ({@code Integer})</li>
+ * <li>Parameter 2: total number of signers ({@code Integer})</li>
+ * </ul>
+ */
+ V2_SIG_MAX_SIGNATURES_EXCEEDED(
+ "APK Signature Scheme V2 only supports a maximum of %1$d signers, found %2$d"),
+
+ /**
* APK Signature Scheme v2 signature contains no signers.
*/
V2_SIG_NO_SIGNERS("No signers in APK Signature Scheme v2 signature"),
diff --git a/src/main/java/com/android/apksig/Constants.java b/src/main/java/com/android/apksig/Constants.java
index 680c5c3..c771aca 100644
--- a/src/main/java/com/android/apksig/Constants.java
+++ b/src/main/java/com/android/apksig/Constants.java
@@ -34,6 +34,11 @@ public class Constants {
public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4;
+ /**
+ * The maximum number of signers supported by the v1 and v2 APK Signature Schemes.
+ */
+ public static final int MAX_APK_SIGNERS = 10;
+
public static final String MANIFEST_ENTRY_NAME = V1SchemeConstants.MANIFEST_ENTRY_NAME;
public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID =
diff --git a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java
index 85301ca..758ddd1 100644
--- a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java
+++ b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java
@@ -16,6 +16,7 @@
package com.android.apksig.internal.apk.v1;
+import static com.android.apksig.Constants.MAX_APK_SIGNERS;
import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoDigestAlgorithmOid;
import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoSignatureAlgorithm;
@@ -246,6 +247,11 @@ public abstract class V1SchemeSigner {
if (signerConfigs.isEmpty()) {
throw new IllegalArgumentException("At least one signer config must be provided");
}
+ if (signerConfigs.size() > MAX_APK_SIGNERS) {
+ throw new IllegalArgumentException(
+ "APK Signature Scheme v1 only supports a maximum of " + MAX_APK_SIGNERS + ", "
+ + signerConfigs.size() + " provided");
+ }
OutputManifestFile manifest =
generateManifestFile(
jarEntryDigestAlgorithm, jarEntryDigests, sourceManifestBytes);
diff --git a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java
index 6d7e997..d3caeff 100644
--- a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java
+++ b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java
@@ -16,6 +16,7 @@
package com.android.apksig.internal.apk.v1;
+import static com.android.apksig.Constants.MAX_APK_SIGNERS;
import static com.android.apksig.internal.oid.OidConstants.getSigAlgSupportedApiLevels;
import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getJcaDigestAlgorithm;
import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getJcaSignatureAlgorithm;
@@ -301,6 +302,11 @@ public abstract class V1SchemeVerifier {
result.addError(Issue.JAR_SIG_NO_SIGNATURES);
return;
}
+ if (signers.size() > MAX_APK_SIGNERS) {
+ result.addError(Issue.JAR_SIG_MAX_SIGNATURES_EXCEEDED, MAX_APK_SIGNERS,
+ signers.size());
+ return;
+ }
// Verify each signer's signature block file .(RSA|DSA|EC) against the corresponding
// signature file .SF. Any error encountered for any signer terminates verification, to
diff --git a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java
index b69b7d3..06da96c 100644
--- a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java
+++ b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java
@@ -16,6 +16,7 @@
package com.android.apksig.internal.apk.v2;
+import static com.android.apksig.Constants.MAX_APK_SIGNERS;
import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements;
import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes;
import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeCertificates;
@@ -28,6 +29,7 @@ import com.android.apksig.internal.apk.SignatureAlgorithm;
import com.android.apksig.internal.util.Pair;
import com.android.apksig.util.DataSource;
import com.android.apksig.util.RunnablesExecutor;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -184,6 +186,12 @@ public abstract class V2SchemeSigner {
// FORMAT:
// * length-prefixed sequence of length-prefixed signer blocks.
+ if (signerConfigs.size() > MAX_APK_SIGNERS) {
+ throw new IllegalArgumentException(
+ "APK Signature Scheme v2 only supports a maximum of " + MAX_APK_SIGNERS + ", "
+ + signerConfigs.size() + " provided");
+ }
+
List<byte[]> signerBlocks = new ArrayList<>(signerConfigs.size());
if (preservedV2SignerBlocks != null && preservedV2SignerBlocks.size() > 0) {
signerBlocks.addAll(preservedV2SignerBlocks);
diff --git a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java
index f367908..4d6e3e1 100644
--- a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java
+++ b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java
@@ -16,6 +16,8 @@
package com.android.apksig.internal.apk.v2;
+import static com.android.apksig.Constants.MAX_APK_SIGNERS;
+
import com.android.apksig.ApkVerifier.Issue;
import com.android.apksig.apk.ApkFormatException;
import com.android.apksig.apk.ApkUtils;
@@ -222,6 +224,9 @@ public abstract class V2SchemeVerifier {
return;
}
}
+ if (signerCount > MAX_APK_SIGNERS) {
+ result.addError(Issue.V2_SIG_MAX_SIGNATURES_EXCEEDED, MAX_APK_SIGNERS, signerCount);
+ }
}
/**
diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java
index d799201..7dcf110 100644
--- a/src/test/java/com/android/apksig/ApkSignerTest.java
+++ b/src/test/java/com/android/apksig/ApkSignerTest.java
@@ -818,6 +818,106 @@ public class ApkSignerTest {
}
@Test
+ public void testV1SigningAllowedWithMaximumNumberOfSigners() throws Exception {
+ // The APK Signature Scheme v1 supports a maximum of 10 signers; this test verifies a
+ // signing config with the maximum number of signers is allowed to sign the APK.
+ List<ApkSigner.SignerConfig> signers = List.of(
+ getDefaultSignerConfigFromResources("dsa-1024"),
+ getDefaultSignerConfigFromResources("dsa-2048"),
+ getDefaultSignerConfigFromResources("dsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-1024"),
+ getDefaultSignerConfigFromResources("rsa-2048"),
+ getDefaultSignerConfigFromResources("rsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-4096"),
+ getDefaultSignerConfigFromResources("rsa-8192"),
+ getDefaultSignerConfigFromResources("ec-p256"),
+ getDefaultSignerConfigFromResources("ec-p384")
+ );
+ sign("original.apk",
+ new ApkSigner.Builder(signers)
+ .setV1SigningEnabled(true)
+ .setV2SigningEnabled(false)
+ .setV3SigningEnabled(false)
+ .setV4SigningEnabled(false));
+ }
+
+ @Test
+ public void testV1SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception {
+ // This test ensures a v1 signing config with more than the maximum supported number
+ // of signers will fail to sign.
+ List<ApkSigner.SignerConfig> signers = List.of(
+ getDefaultSignerConfigFromResources("dsa-1024"),
+ getDefaultSignerConfigFromResources("dsa-2048"),
+ getDefaultSignerConfigFromResources("dsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-1024"),
+ getDefaultSignerConfigFromResources("rsa-2048"),
+ getDefaultSignerConfigFromResources("rsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-4096"),
+ getDefaultSignerConfigFromResources("rsa-8192"),
+ getDefaultSignerConfigFromResources("ec-p256"),
+ getDefaultSignerConfigFromResources("ec-p384"),
+ getDefaultSignerConfigFromResources("ec-p521")
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ sign("original.apk",
+ new ApkSigner.Builder(signers)
+ .setV1SigningEnabled(true)
+ .setV2SigningEnabled(false)
+ .setV3SigningEnabled(false)
+ .setV4SigningEnabled(false)));
+ }
+
+ @Test
+ public void testV2SigningAllowedWithMaximumNumberOfSigners() throws Exception {
+ // The APK Signature Scheme v2 supports a maximum of 10 signers; this test verifies a
+ // signing config with the maximum number of signers is allowed to sign the APK.
+ List<ApkSigner.SignerConfig> signers = List.of(
+ getDefaultSignerConfigFromResources("dsa-1024"),
+ getDefaultSignerConfigFromResources("dsa-2048"),
+ getDefaultSignerConfigFromResources("dsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-1024"),
+ getDefaultSignerConfigFromResources("rsa-2048"),
+ getDefaultSignerConfigFromResources("rsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-4096"),
+ getDefaultSignerConfigFromResources("rsa-8192"),
+ getDefaultSignerConfigFromResources("ec-p256"),
+ getDefaultSignerConfigFromResources("ec-p384")
+ );
+ sign("original.apk",
+ new ApkSigner.Builder(signers)
+ .setV1SigningEnabled(false)
+ .setV2SigningEnabled(true)
+ .setV3SigningEnabled(false)
+ .setV4SigningEnabled(false));
+ }
+
+ @Test
+ public void testV2SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception {
+ // This test ensures a v2 signing config with more than the maximum supported number
+ // of signers will fail to sign.
+ List<ApkSigner.SignerConfig> signers = List.of(
+ getDefaultSignerConfigFromResources("dsa-1024"),
+ getDefaultSignerConfigFromResources("dsa-2048"),
+ getDefaultSignerConfigFromResources("dsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-1024"),
+ getDefaultSignerConfigFromResources("rsa-2048"),
+ getDefaultSignerConfigFromResources("rsa-3072"),
+ getDefaultSignerConfigFromResources("rsa-4096"),
+ getDefaultSignerConfigFromResources("rsa-8192"),
+ getDefaultSignerConfigFromResources("ec-p256"),
+ getDefaultSignerConfigFromResources("ec-p384"),
+ getDefaultSignerConfigFromResources("ec-p521")
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ sign("original.apk",
+ new ApkSigner.Builder(signers)
+ .setV1SigningEnabled(false)
+ .setV2SigningEnabled(true)
+ .setV3SigningEnabled(false)
+ .setV4SigningEnabled(false)));
+ }
+
+ @Test
public void testWeirdZipCompressionMethod() throws Exception {
// 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,
diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java
index 9e1a75e..79950dc 100644
--- a/src/test/java/com/android/apksig/ApkVerifierTest.java
+++ b/src/test/java/com/android/apksig/ApkVerifierTest.java
@@ -245,6 +245,20 @@ public class ApkVerifierTest {
}
@Test
+ public void testV1MaxSupportedSignersAccepted() throws Exception {
+ // The APK Signature Scheme V1 supports a maximum of 10 signers; this test ensures an
+ // APK signed with that many signers successfully verifies.
+ assertVerified(verify("v1-only-10-signers.apk"));
+ }
+
+ @Test
+ public void testV1MoreThanMaxSupportedSignersRejected() throws Exception {
+ // This test ensure an APK signed with more than the supported number of signers fails
+ // to verify.
+ assertVerificationFailure("v1-only-11-signers.apk", Issue.JAR_SIG_MAX_SIGNATURES_EXCEEDED);
+ }
+
+ @Test
public void testV2StrippedRejected() throws Exception {
// APK signed with v1 and v2 schemes, but v2 signature was stripped from the file (by using
// zipalign).
@@ -630,6 +644,23 @@ public class ApkVerifierTest {
}
@Test
+ public void testV2MaxSupportedSignersAccepted() throws Exception {
+ // The APK Signature Scheme v2 supports a maximum of 10 signers; this test ensures an
+ // APK signed with that many signers successfully verifies.
+ assertVerified(verifyForMinSdkVersion("v2-only-10-signers.apk", AndroidSdkVersion.N));
+ }
+
+ @Test
+ public void testV2MoreThanMaxSupportedSignersRejected() throws Exception {
+ // This test ensure an APK signed with more than the supported number of signers fails
+ // to verify.
+ assertVerificationFailure(
+ verifyForMinSdkVersion("v2-only-11-signers.apk", AndroidSdkVersion.N),
+ Issue.V2_SIG_MAX_SIGNATURES_EXCEEDED);
+ }
+
+
+ @Test
public void testCorrectCertUsedFromPkcs7SignedDataCertsSet() throws Exception {
// Obtained by prepending the rsa-1024 certificate to the PKCS#7 SignedData certificates set
// of v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk META-INF/CERT.RSA. The certs
diff --git a/src/test/resources/com/android/apksig/v1-only-10-signers.apk b/src/test/resources/com/android/apksig/v1-only-10-signers.apk
new file mode 100644
index 0000000..198beeb
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-only-10-signers.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v1-only-11-signers.apk b/src/test/resources/com/android/apksig/v1-only-11-signers.apk
new file mode 100644
index 0000000..95e6c61
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-only-11-signers.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v2-only-10-signers.apk b/src/test/resources/com/android/apksig/v2-only-10-signers.apk
new file mode 100644
index 0000000..ad34c14
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v2-only-10-signers.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v2-only-11-signers.apk b/src/test/resources/com/android/apksig/v2-only-11-signers.apk
new file mode 100644
index 0000000..674b6e4
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v2-only-11-signers.apk
Binary files differ