Fix JAR sig verification with multiple digests am: 483b4735ef am: 23af335fa3 am: df4616c317
am: 7731297815
Change-Id: I6939b94f25493baf8d82b6d9263d69896ce79656
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 d7a4eb0..fdf459a 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
@@ -1425,18 +1425,18 @@
continue;
}
- Collection<NamedDigest> expectedDigests =
- getDigestsToVerify(manifestSection, "-Digest", minSdkVersion, maxSdkVersion);
+ List<NamedDigest> expectedDigests =
+ new ArrayList<>(
+ getDigestsToVerify(
+ manifestSection, "-Digest", minSdkVersion, maxSdkVersion));
if (expectedDigests.isEmpty()) {
result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
continue;
}
MessageDigest[] mds = new MessageDigest[expectedDigests.size()];
- int mdIndex = 0;
- for (NamedDigest expectedDigest : expectedDigests) {
- mds[mdIndex] = getMessageDigest(expectedDigest.jcaDigestAlgorithm);
- mdIndex++;
+ for (int i = 0; i < expectedDigests.size(); i++) {
+ mds[i] = getMessageDigest(expectedDigests.get(i).jcaDigestAlgorithm);
}
try {
@@ -1451,9 +1451,9 @@
throw new IOException("Failed to read entry: " + entryName, e);
}
- mdIndex = 0;
- for (NamedDigest expectedDigest : expectedDigests) {
- byte[] actualDigest = mds[mdIndex].digest();
+ for (int i = 0; i < expectedDigests.size(); i++) {
+ NamedDigest expectedDigest = expectedDigests.get(i);
+ byte[] actualDigest = mds[i].digest();
if (!Arrays.equals(expectedDigest.digest, actualDigest)) {
result.addError(
Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java
index 218d450..4268d84 100644
--- a/src/test/java/com/android/apksig/ApkVerifierTest.java
+++ b/src/test/java/com/android/apksig/ApkVerifierTest.java
@@ -525,19 +525,100 @@
assertVerified(verifyForMinSdkVersion("v2-only-empty.apk", AndroidSdkVersion.N));
}
+ @Test
+ public void testV1MultipleDigestAlgsInManifestAndSignatureFile() throws Exception {
+ // MANIFEST.MF contains SHA-1 and SHA-256 digests for each entry, .SF contains only SHA-1
+ // digests. This file was obtained by:
+ // jarsigner -sigalg SHA256withRSA -digestalg SHA-256 ... <file> ...
+ // jarsigner -sigalg SHA1withRSA -digestalg SHA1 ... <same file> ...
+ assertVerified(verify("v1-sha1-sha256-manifest-and-sha1-sf.apk"));
+
+ // MANIFEST.MF and .SF contain SHA-1 and SHA-256 digests for each entry. This file was
+ // obtained by modifying apksigner to output multiple digests.
+ assertVerified(verify("v1-sha1-sha256-manifest-and-sf.apk"));
+
+ // One of the digests is wrong in either MANIFEST.MF or .SF. These files were obtained by
+ // modifying apksigner to output multiple digests and to flip a bit to create a wrong
+ // digest.
+
+ // SHA-1 digests in MANIFEST.MF are wrong, but SHA-256 digests are OK.
+ // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18
+ // and higher.
+ assertVerificationFailure(
+ verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk"),
+ Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
+ assertVerificationFailure(
+ verifyForMaxSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 17),
+ Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
+ assertVerified(
+ verifyForMinSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 18));
+
+ // SHA-1 digests in .SF are wrong, but SHA-256 digests are OK.
+ // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18
+ // and higher.
+ assertVerificationFailure(
+ verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk"),
+ Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
+ assertVerificationFailure(
+ verifyForMaxSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 17),
+ Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
+ assertVerified(
+ verifyForMinSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 18));
+
+ // SHA-256 digests in MANIFEST.MF are wrong, but SHA-1 digests are OK.
+ // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17
+ // and lower.
+ assertVerificationFailure(
+ verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk"),
+ Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
+ assertVerificationFailure(
+ verifyForMinSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 18),
+ Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
+ assertVerified(
+ verifyForMaxSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 17));
+
+ // SHA-256 digests in .SF are wrong, but SHA-1 digests are OK.
+ // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17
+ // and lower.
+ assertVerificationFailure(
+ verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk"),
+ Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
+ assertVerificationFailure(
+ verifyForMinSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 18),
+ Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
+ assertVerified(
+ verifyForMaxSdkVersion(
+ "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 17));
+ }
+
private ApkVerifier.Result verify(String apkFilenameInResources)
throws IOException, ApkFormatException, NoSuchAlgorithmException {
- return verify(apkFilenameInResources, null);
+ return verify(apkFilenameInResources, null, null);
}
private ApkVerifier.Result verifyForMinSdkVersion(
String apkFilenameInResources, int minSdkVersion)
throws IOException, ApkFormatException, NoSuchAlgorithmException {
- return verify(apkFilenameInResources, minSdkVersion);
+ return verify(apkFilenameInResources, minSdkVersion, null);
+ }
+
+ private ApkVerifier.Result verifyForMaxSdkVersion(
+ String apkFilenameInResources, int maxSdkVersion)
+ throws IOException, ApkFormatException, NoSuchAlgorithmException {
+ return verify(apkFilenameInResources, null, maxSdkVersion);
}
private ApkVerifier.Result verify(
- String apkFilenameInResources, Integer minSdkVersionOverride)
+ String apkFilenameInResources,
+ Integer minSdkVersionOverride,
+ Integer maxSdkVersionOverride)
throws IOException, ApkFormatException, NoSuchAlgorithmException {
byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources);
@@ -546,6 +627,9 @@
if (minSdkVersionOverride != null) {
builder.setMinCheckedPlatformVersion(minSdkVersionOverride);
}
+ if (maxSdkVersionOverride != null) {
+ builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride);
+ }
return builder.build().verify();
}
@@ -585,8 +669,11 @@
}
private void assertVerified(
- String apkFilenameInResources, Integer minSdkVersionOverride) throws Exception {
- assertVerified(verify(apkFilenameInResources, minSdkVersionOverride));
+ String apkFilenameInResources,
+ Integer minSdkVersionOverride,
+ Integer maxSdkVersionOverride) throws Exception {
+ assertVerified(
+ verify(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride));
}
static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) {
@@ -643,23 +730,25 @@
private void assertVerifiedForEach(
String apkFilenamePatternInResources, String[] args) throws Exception {
- assertVerifiedForEach(apkFilenamePatternInResources, args, null);
+ assertVerifiedForEach(apkFilenamePatternInResources, args, null, null);
}
private void assertVerifiedForEach(
- String apkFilenamePatternInResources, String[] args, Integer minSdkVersionOverride)
- throws Exception {
+ String apkFilenamePatternInResources,
+ String[] args,
+ Integer minSdkVersionOverride,
+ Integer maxSdkVersionOverride) throws Exception {
for (String arg : args) {
String apkFilenameInResources =
String.format(Locale.US, apkFilenamePatternInResources, arg);
- assertVerified(apkFilenameInResources, minSdkVersionOverride);
+ assertVerified(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride);
}
}
private void assertVerifiedForEachForMinSdkVersion(
String apkFilenameInResources, String[] args, int minSdkVersion)
throws Exception {
- assertVerifiedForEach(apkFilenameInResources, args, minSdkVersion);
+ assertVerifiedForEach(apkFilenameInResources, args, minSdkVersion, null);
}
private static byte[] sha256(byte[] msg) throws Exception {
diff --git a/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk
new file mode 100644
index 0000000..1d90c7e
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk
new file mode 100644
index 0000000..a16e442
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk
new file mode 100644
index 0000000..29a6030
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk
new file mode 100644
index 0000000..eb12bcc
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf.apk b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf.apk
new file mode 100644
index 0000000..f347a1c
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sha1-sf.apk b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sha1-sf.apk
new file mode 100644
index 0000000..f347a1c
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sha1-sf.apk
Binary files differ