summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2018-03-08 00:15:18 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-03-08 00:15:18 +0000
commit429a58d18b32a9dcc82a3aa5edc99a519cf80df9 (patch)
tree1caf14ac49382ab9eb8f2729e5ebc15d5f921280
parenta1aa6abe36251827620130477b1b6faec4be33c0 (diff)
parent4ba1eeaa0e0468131da08a5c5f461361cac79ff1 (diff)
Merge "Verify the content length in the verity digest" into pi-dev
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java4
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java6
-rw-r--r--core/java/android/util/apk/ApkSigningBlockUtils.java43
3 files changed, 46 insertions, 7 deletions
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 62222b5764e7..533d72590f0a 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -213,7 +213,9 @@ public class ApkSignatureSchemeV2Verifier {
byte[] verityRootHash = null;
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
- verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+ verityDigest, apk.length(), signatureInfo);
}
return new VerifiedSigner(
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 9436b296e9e2..4431bcef1ff4 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -165,7 +165,7 @@ public class ApkSignatureSchemeV3Verifier {
private static VerifiedSigner verify(
RandomAccessFile apk,
SignatureInfo signatureInfo,
- boolean doVerifyIntegrity) throws SecurityException {
+ boolean doVerifyIntegrity) throws SecurityException, IOException {
int signerCount = 0;
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
VerifiedSigner result = null;
@@ -214,7 +214,9 @@ public class ApkSignatureSchemeV3Verifier {
}
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
- result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+ verityDigest, apk.length(), signatureInfo);
}
return result;
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 40db758e1d4b..1c67434b2609 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -285,11 +285,46 @@ final class ApkSigningBlockUtils {
return result;
}
+ /**
+ * Return the verity digest only if the length of digest content looks correct.
+ * When verity digest is generated, the last incomplete 4k chunk is padded with 0s before
+ * hashing. This means two almost identical APKs with different number of 0 at the end will have
+ * the same verity digest. To avoid this problem, the length of the source content (excluding
+ * Signing Block) is appended to the verity digest, and the digest is returned only if the
+ * length is consistent to the current APK.
+ */
+ static byte[] parseVerityDigestAndVerifySourceLength(
+ byte[] data, long fileSize, SignatureInfo signatureInfo) throws SecurityException {
+ // FORMAT:
+ // OFFSET DATA TYPE DESCRIPTION
+ // * @+0 bytes uint8[32] Merkle tree root hash of SHA-256
+ // * @+32 bytes int64 Length of source data
+ int kRootHashSize = 32;
+ int kSourceLengthSize = 8;
+
+ if (data.length != kRootHashSize + kSourceLengthSize) {
+ throw new SecurityException("Verity digest size is wrong: " + data.length);
+ }
+ ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.position(kRootHashSize);
+ long expectedSourceLength = buffer.getLong();
+
+ long signingBlockSize = signatureInfo.centralDirOffset
+ - signatureInfo.apkSigningBlockOffset;
+ if (expectedSourceLength != fileSize - signingBlockSize) {
+ throw new SecurityException("APK content size did not verify");
+ }
+
+ return Arrays.copyOfRange(data, 0, kRootHashSize);
+ }
+
private static void verifyIntegrityForVerityBasedAlgorithm(
- byte[] expectedRootHash,
+ byte[] expectedDigest,
RandomAccessFile apk,
SignatureInfo signatureInfo) throws SecurityException {
try {
+ byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
+ apk.length(), signatureInfo);
ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
signatureInfo, new ByteBufferFactory() {
@Override
@@ -373,9 +408,9 @@ final class ApkSigningBlockUtils {
static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
- static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0411;
- static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0413;
- static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0415;
+ static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0421;
+ static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423;
+ static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425;
static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;