Merge "Resolve handling of supported signatures and digests for max SDK"
diff --git a/src/main/java/com/android/apksig/ApkVerifier.java b/src/main/java/com/android/apksig/ApkVerifier.java
index 5e458ef..6e0a520 100644
--- a/src/main/java/com/android/apksig/ApkVerifier.java
+++ b/src/main/java/com/android/apksig/ApkVerifier.java
@@ -40,6 +40,7 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -185,6 +186,26 @@
Result result = new Result();
+ // The SUPPORTED_APK_SIG_SCHEME_NAMES contains the mapping from version number to scheme
+ // name, but the verifiers use this parameter as the schemes supported by the target SDK
+ // range. Since the code below skips signature verification based on max SDK the mapping of
+ // supported schemes needs to be modified to ensure the verifiers do not report a stripped
+ // signature for an SDK range that does not support that signature version. For instance an
+ // APK with V1, V2, and V3 signatures and a max SDK of O would skip the V3 signature
+ // verification, but the SUPPORTED_APK_SIG_SCHEME_NAMES contains version 3, so when the V2
+ // verification is performed it would see the stripping protection attribute, see that V3
+ // is in the list of supported signatures, and report a stripped signature.
+ Map<Integer, String> supportedSchemeNames;
+ if (maxSdkVersion >= AndroidSdkVersion.P) {
+ supportedSchemeNames = SUPPORTED_APK_SIG_SCHEME_NAMES;
+ } else if (maxSdkVersion >= AndroidSdkVersion.N) {
+ supportedSchemeNames = new HashMap<>(1);
+ supportedSchemeNames.put(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2,
+ SUPPORTED_APK_SIG_SCHEME_NAMES.get(
+ ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2));
+ } else {
+ supportedSchemeNames = Collections.EMPTY_MAP;
+ }
// Android N and newer attempts to verify APKs using the APK Signing Block, which can
// include v2 and/or v3 signatures. If none is found, it falls back to JAR signature
// verification. If the signature is found but does not verify, the APK is rejected.
@@ -220,7 +241,7 @@
V2SchemeVerifier.verify(
apk,
zipSections,
- SUPPORTED_APK_SIG_SCHEME_NAMES,
+ supportedSchemeNames,
foundApkSigSchemeIds,
Math.max(minSdkVersion, AndroidSdkVersion.N),
maxSdkVersion);
@@ -261,7 +282,7 @@
V1SchemeVerifier.verify(
apk,
zipSections,
- SUPPORTED_APK_SIG_SCHEME_NAMES,
+ supportedSchemeNames,
foundApkSigSchemeIds,
minSdkVersion,
maxSdkVersion);
diff --git a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
index c9cc4c1..556c643 100644
--- a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
+++ b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
@@ -215,6 +215,12 @@
}
ContentDigestAlgorithm contentDigestAlgorithm =
signatureAlgorithm.getContentDigestAlgorithm();
+ // if the current digest algorithm is not in the list provided by the caller then
+ // ignore it; the signer may contain digests not recognized by the specified SDK
+ // range.
+ if (!contentDigestAlgorithms.contains(contentDigestAlgorithm)) {
+ continue;
+ }
byte[] expectedDigest = expected.getValue();
byte[] actualDigest = actualContentDigests.get(contentDigestAlgorithm);
if (!Arrays.equals(expectedDigest, actualDigest)) {
diff --git a/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java b/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java
index 484334d..615d251 100644
--- a/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java
+++ b/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java
@@ -36,6 +36,9 @@
/** Android 5.0. A flat one with beautiful shadows. But still tasty. */
public static final int LOLLIPOP = 21;
+ /** Android 6.0. M is for Marshmallow! */
+ public static final int M = 23;
+
/** Android 7.0. N is for Nougat. */
public static final int N = 24;
diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java
index 1f9e208..351d0a8 100644
--- a/src/test/java/com/android/apksig/ApkVerifierTest.java
+++ b/src/test/java/com/android/apksig/ApkVerifierTest.java
@@ -267,6 +267,17 @@
}
@Test
+ public void testSignaturesIgnoredForMaxSDK() throws Exception {
+ // The V2 signature scheme was introduced in N, and V3 was introduced in P. This test
+ // verifies a max SDK of pre-P ignores the V3 signature and a max SDK of pre-N ignores both
+ // the V2 and V3 signatures.
+ assertVerified(verifyForMaxSdkVersion("v1v2v3-with-rsa-2048-lineage-3-signers.apk",
+ AndroidSdkVersion.O));
+ assertVerified(verifyForMaxSdkVersion("v1v2v3-with-rsa-2048-lineage-3-signers.apk",
+ AndroidSdkVersion.M));
+ }
+
+ @Test
public void testV2OneSignerOneSignatureAccepted() throws Exception {
// APK signed with v2 scheme only, one signer, one signature
assertVerifiedForEachForMinSdkVersion(