From 984e7524eaf95e736488f1b491383d80946117df Mon Sep 17 00:00:00 2001 From: Alex Buynytskyy Date: Thu, 19 May 2022 10:38:47 -0700 Subject: [PATCH] Calculate correct checksum. To maintain parity with fs-verity, we need to calculate checksums according to https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#fs-verity-descriptor Bug: 233217301 Fixes: 233217301 Test: atest ChecksumsTest Change-Id: Ie0108f12ba988536a1d2fd9dddf1614681026f7c --- .../com/android/server/pm/ApkChecksums.java | 53 ++++++++++++++++--- .../server/pm/PackageManagerServiceUtils.java | 2 +- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java index 39119948fa3e2..c2f2b0af997ad 100644 --- a/services/core/java/com/android/server/pm/ApkChecksums.java +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -77,6 +77,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.file.Files; import java.security.DigestException; import java.security.InvalidParameterException; @@ -584,7 +585,7 @@ public class ApkChecksums { }); checksums.put(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, - generatedRootHash)); + verityHashForFile(file, generatedRootHash))); } catch (IOException | NoSuchAlgorithmException | DigestException e) { Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e); } @@ -649,19 +650,20 @@ public class ApkChecksums { // Skip /product folder. // TODO(b/231354111): remove this hack once we are allowed to change SELinux rules. if (!containsFile(Environment.getProductDirectory(), filePath)) { - byte[] hash = VerityUtils.getFsverityRootHash(filePath); - if (hash != null) { - return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash); + byte[] verityHash = VerityUtils.getFsverityRootHash(filePath); + if (verityHash != null) { + return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, verityHash); } } // v4 next try { ApkSignatureSchemeV4Verifier.VerifiedSigner signer = ApkSignatureSchemeV4Verifier.extractCertificates(filePath); - byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256, - null); - if (hash != null) { - return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash); + byte[] rootHash = signer.contentDigests.getOrDefault( + CONTENT_DIGEST_VERITY_CHUNKED_SHA256, null); + if (rootHash != null) { + return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, + verityHashForFile(new File(filePath), rootHash)); } } catch (SignatureNotFoundException e) { // Nothing @@ -671,6 +673,41 @@ public class ApkChecksums { return null; } + /** + * Returns fs-verity digest as described in + * https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#fs-verity-descriptor + * @param file the Merkle tree is built over + * @param rootHash Merkle tree root hash + */ + static byte[] verityHashForFile(File file, byte[] rootHash) { + try { + ByteBuffer buffer = ByteBuffer.allocate(256); // sizeof(fsverity_descriptor) + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.put((byte) 1); // __u8 version, must be 1 + buffer.put((byte) 1); // __u8 hash_algorithm, FS_VERITY_HASH_ALG_SHA256 + buffer.put((byte) 12); // __u8, FS_VERITY_LOG_BLOCKSIZE + buffer.put((byte) 0); // __u8, size of salt in bytes; 0 if none + buffer.putInt(0); // __le32 __reserved_0x04, must be 0 + buffer.putLong(file.length()); // __le64 data_size + buffer.put(rootHash); // root_hash, first 32 bytes + final int padding = 32 + 32 + 144; // root_hash, last 32 bytes, we are using sha256. + // salt, 32 bytes + // reserved, 144 bytes + for (int i = 0; i < padding; ++i) { + buffer.put((byte) 0); + } + + buffer.flip(); + + final MessageDigest md = MessageDigest.getInstance(ALGO_SHA256); + md.update(buffer); + return md.digest(); + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Device does not support MessageDigest algorithm", e); + return null; + } + } + private static Map extractHashFromV2V3Signature( String split, String filePath, int types) { Map contentDigests = null; diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 703be169f14cb..719c3b767b465 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -1069,7 +1069,7 @@ public class PackageManagerServiceUtils { if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) { throw new IOException("Root has not present"); } - return hashInfo.rawRootHash; + return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash); } catch (IOException ignore) { Slog.e(TAG, "ERROR: could not load root hash from incremental install"); }