OSDN Git Service

Add sha1 support for hashtree
authorTianjie Xu <xunchang@google.com>
Mon, 2 Dec 2019 20:55:33 +0000 (12:55 -0800)
committerTianjie Xu <xunchang@google.com>
Wed, 18 Dec 2019 22:27:02 +0000 (14:27 -0800)
AVB format uses sha1 as the hash algorithm when computing the hash of
each block. This cl adds the support to use either sha1 or sha256 in
the hashtree_info struct. It also moves some helper functions as the member
function of the struct.

Bug: 144388532
Test: unit tests pass
Change-Id: Ie4aef1bdd8fa89240f7c6f83fcb2e33f47a468cb

libfec/fec_open.cpp
libfec/fec_private.h
libfec/fec_read.cpp
libfec/fec_verity.cpp
libfec/test/fec_unittest.cpp

index c8ae980..18731f2 100644 (file)
@@ -80,7 +80,8 @@ static int find_offset(uint64_t file_size, int roots, uint64_t *offset,
 /* returns verity metadata size for a `size' byte file */
 static uint64_t get_verity_size(uint64_t size, int)
 {
-    return VERITY_METADATA_SIZE + verity_get_size(size, NULL, NULL);
+    return VERITY_METADATA_SIZE +
+           verity_get_size(size, NULL, NULL, SHA256_DIGEST_LENGTH);
 }
 
 /* computes the verity metadata offset for a file with size `f->size' */
@@ -328,7 +329,7 @@ static int load_verity(fec_handle *f)
     if (rc == 0) {
         debug("file system size = %" PRIu64, offset);
         /* Jump over the verity tree appended to the filesystem */
-        offset += verity_get_size(offset, NULL, NULL);
+        offset += verity_get_size(offset, NULL, NULL, SHA256_DIGEST_LENGTH);
         rc = verity_parse_header(f, offset);
 
         if (rc == 0) {
index 81f34d5..6268799 100644 (file)
 
 #include <errno.h>
 #include <fcntl.h>
-#include <memory>
-#include <new>
 #include <pthread.h>
 #include <stdio.h>
 #include <string.h>
-#include <string>
 #include <sys/syscall.h>
 #include <unistd.h>
+
+#include <memory>
+#include <string>
 #include <vector>
 
 #include <crypto_utils/android_pubkey.h>
 #include <fec/ecc.h>
 #include <fec/io.h>
+#include <openssl/obj_mac.h>
 #include <openssl/sha.h>
 #include <utils/Compat.h>
 
@@ -75,14 +76,43 @@ struct ecc_info {
 };
 
 struct hashtree_info {
+    // The number of the input data blocks to compute the hashtree.
     uint64_t data_blocks;
-    uint32_t hash_data_blocks;
-    uint32_t hash_size;
-    uint64_t hash_data_offset;
+    // The offset of hashtree in the final image.
     uint64_t hash_start;
-    std::vector<uint8_t> hash;
+    // The hash concatenation of the input data, i.e. lowest level of the
+    // hashtree.
+    std::vector<uint8_t> hash_data;
     std::vector<uint8_t> salt;
     std::vector<uint8_t> zero_hash;
+
+    // Initialize the hashtree offsets and properties with the input parameters.
+    int initialize(uint64_t hash_start, uint64_t data_blocks,
+                   const std::vector<uint8_t> &salt, int nid);
+
+    // Checks if the bytes in 'block' has the expected hash. And the 'index' is
+    // the block number of is the input block in the filesystem.
+    bool check_block_hash_with_index(uint64_t index, const uint8_t *block);
+
+    // Reads the verity hash tree, validates it against the root hash in `root',
+    // corrects errors if necessary, and copies valid data blocks for later use
+    // to 'hashtree'.
+    int verify_tree(const fec_handle *f, const uint8_t *root);
+
+   private:
+    bool ecc_read_hashes(fec_handle *f, uint64_t hash_offset, uint8_t *hash,
+                         uint64_t data_offset, uint8_t *data);
+
+    // Computes the hash for FEC_BLOCKSIZE bytes from buffer 'block' and
+    // compares it to the expected value in 'expected'.
+    bool check_block_hash(const uint8_t *expected, const uint8_t *block);
+
+    // Computes the hash of 'block' and put the result in 'hash'.
+    int get_hash(const uint8_t *block, uint8_t *hash);
+
+    int nid_;  // NID for the hash algorithm.
+    uint32_t digest_length_;
+    uint32_t padded_digest_length_;
 };
 
 struct verity_info {
@@ -124,13 +154,11 @@ extern ssize_t process(fec_handle *f, uint8_t *buf, size_t count,
 
 /* verity functions */
 extern uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
-        uint32_t *level_hashes);
+                                uint32_t *level_hashes,
+                                uint32_t padded_digest_size);
 
 extern int verity_parse_header(fec_handle *f, uint64_t offset);
 
-extern bool check_block_hash(const uint8_t *expected, const uint8_t *block,
-                             const std::vector<uint8_t> &salt);
-
 /* helper macros */
 #ifndef unlikely
     #define unlikely(x) __builtin_expect(!!(x), 0)
index 9ea6430..889f990 100644 (file)
@@ -79,8 +79,7 @@ static inline bool is_erasure(fec_handle *f, uint64_t offset,
 
     uint64_t n = offset / FEC_BLOCKSIZE;
 
-    return !check_block_hash(&f->hashtree().hash[n * SHA256_DIGEST_LENGTH],
-                             data, f->hashtree().salt);
+    return !f->hashtree().check_block_hash_with_index(n, data);
 }
 
 /* check if `offset' is within a block expected to contain zeros */
@@ -88,18 +87,18 @@ static inline bool is_zero(fec_handle *f, uint64_t offset)
 {
     auto hashtree = f->hashtree();
 
-    if (hashtree.hash.empty() || unlikely(offset >= f->data_size)) {
+    if (hashtree.hash_data.empty() || unlikely(offset >= f->data_size)) {
         return false;
     }
 
     uint64_t hash_offset = (offset / FEC_BLOCKSIZE) * SHA256_DIGEST_LENGTH;
 
-    if (unlikely(hash_offset > hashtree.hash_data_blocks * FEC_BLOCKSIZE -
-                                   SHA256_DIGEST_LENGTH)) {
+    if (unlikely(hash_offset >
+                 hashtree.hash_data.size() - SHA256_DIGEST_LENGTH)) {
         return false;
     }
 
-    return !memcmp(hashtree.zero_hash.data(), &hashtree.hash[hash_offset],
+    return !memcmp(hashtree.zero_hash.data(), &hashtree.hash_data[hash_offset],
                    SHA256_DIGEST_LENGTH);
 }
 
@@ -120,7 +119,7 @@ static int __ecc_read(fec_handle *f, void *rs, uint8_t *dest, uint64_t offset,
     int neras = 0;
 
     /* verity is required to check for erasures */
-    check(!use_erasures || !f->hashtree().hash.empty());
+    check(!use_erasures || !f->hashtree().hash_data.empty());
 
     for (int i = 0; i < e->rsn; ++i) {
         uint64_t interleaved = fec_ecc_interleave(rsb * e->rsn + i, e->rsn,
@@ -181,7 +180,7 @@ static int __ecc_read(fec_handle *f, void *rs, uint8_t *dest, uint64_t offset,
                 error("RS block %" PRIu64 ": decoding failed (%d erasures)",
                     rsb, neras);
                 dump("raw RS block", rsb, copy, FEC_RSM);
-            } else if (f->hashtree().hash.empty()) {
+            } else if (f->hashtree().hash_data.empty()) {
                 warn("RS block %" PRIu64 ": decoding failed", rsb);
             } else {
                 debug("RS block %" PRIu64 ": decoding failed", rsb);
@@ -290,7 +289,7 @@ static ssize_t verity_read(fec_handle *f, uint8_t *dest, size_t count,
     check(dest);
     check(offset < f->data_size);
     check(offset + count <= f->data_size);
-    check(!f->hashtree().hash.empty());
+    check(!f->hashtree().hash_data.empty());
     check(errors);
 
     debug("[%" PRIu64 ", %" PRIu64 ")", offset, offset + count);
@@ -307,14 +306,12 @@ static ssize_t verity_read(fec_handle *f, uint8_t *dest, size_t count,
     size_t left = count;
     uint8_t data[FEC_BLOCKSIZE];
 
-    uint64_t max_hash_block = (f->hashtree().hash_data_blocks * FEC_BLOCKSIZE -
-                               SHA256_DIGEST_LENGTH) /
-                              SHA256_DIGEST_LENGTH;
+    uint64_t max_hash_block =
+        (f->hashtree().hash_data.size() - SHA256_DIGEST_LENGTH) /
+        SHA256_DIGEST_LENGTH;
 
     while (left > 0) {
         check(curr <= max_hash_block);
-
-        uint8_t *hash = &f->hashtree().hash[curr * SHA256_DIGEST_LENGTH];
         uint64_t curr_offset = curr * FEC_BLOCKSIZE;
 
         bool expect_zeros = is_zero(f, curr_offset);
@@ -332,7 +329,7 @@ static ssize_t verity_read(fec_handle *f, uint8_t *dest, size_t count,
             return -1;
         }
 
-        if (likely(check_block_hash(hash, data, f->hashtree().salt))) {
+        if (likely(f->hashtree().check_block_hash_with_index(curr, data))) {
             goto valid;
         }
 
@@ -357,14 +354,14 @@ static ssize_t verity_read(fec_handle *f, uint8_t *dest, size_t count,
            erasure locations is slower */
         if (__ecc_read(f, rs.get(), data, curr_offset, false, ecc_data.get(),
                        errors) == FEC_BLOCKSIZE &&
-            check_block_hash(hash, data, f->hashtree().salt)) {
+            f->hashtree().check_block_hash_with_index(curr, data)) {
             goto corrected;
         }
 
         /* try to correct with erasures */
         if (__ecc_read(f, rs.get(), data, curr_offset, true, ecc_data.get(),
                        errors) == FEC_BLOCKSIZE &&
-            check_block_hash(hash, data, f->hashtree().salt)) {
+            f->hashtree().check_block_hash_with_index(curr, data)) {
             goto corrected;
         }
 
@@ -527,7 +524,7 @@ ssize_t fec_pread(struct fec_handle *f, void *buf, size_t count,
         return -1;
     }
 
-    if (!f->hashtree().hash.empty()) {
+    if (!f->hashtree().hash_data.empty()) {
         return process(f, (uint8_t *)buf,
                        get_max_count(offset, count, f->data_size), offset,
                        verity_read);
index 25b6ce4..9ac2377 100644 (file)
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include <android-base/strings.h>
+#include <openssl/evp.h>
 
 #include "fec_private.h"
 
@@ -94,10 +95,9 @@ static int parse_uint64(const char *src, uint64_t maxval, uint64_t *dst)
    number of hash tree levels in `verity_levels,' and the number of hashes per
    level in `level_hashes', if the parameters are non-NULL */
 uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
-        uint32_t *level_hashes)
-{
-    /* we assume a known metadata size, 4 KiB block size, and SHA-256 to avoid
-       relying on disk content */
+                         uint32_t *level_hashes, uint32_t padded_digest_size) {
+    // we assume a known metadata size, 4 KiB block size, and SHA-256 or SHA1 to
+    // avoid relying on disk content.
 
     uint32_t level = 0;
     uint64_t total = 0;
@@ -108,7 +108,7 @@ uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
             level_hashes[level] = hashes;
         }
 
-        hashes = fec_div_round_up(hashes * SHA256_DIGEST_LENGTH, FEC_BLOCKSIZE);
+        hashes = fec_div_round_up(hashes * padded_digest_size, FEC_BLOCKSIZE);
         total += hashes;
 
         ++level;
@@ -121,52 +121,74 @@ uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
     return total * FEC_BLOCKSIZE;
 }
 
-// Computes a SHA-256 salted with 'salt' from a FEC_BLOCKSIZE byte buffer
-// 'block', and copies the hash to 'hash'.
-static inline int get_hash(const uint8_t *block, uint8_t *hash,
-                           const std::vector<uint8_t> &salt) {
-    SHA256_CTX ctx;
-    SHA256_Init(&ctx);
+int hashtree_info::get_hash(const uint8_t *block, uint8_t *hash) {
+    auto md = EVP_get_digestbynid(nid_);
+    check(md)
+    auto mdctx = EVP_MD_CTX_new();
+    check(mdctx)
 
-    check(!salt.empty());
-    SHA256_Update(&ctx, salt.data(), salt.size());
+    EVP_DigestInit_ex(mdctx, md, nullptr);
+    EVP_DigestUpdate(mdctx, salt.data(), salt.size());
+    EVP_DigestUpdate(mdctx, block, FEC_BLOCKSIZE);
+    unsigned int hash_size;
+    EVP_DigestFinal_ex(mdctx, hash, &hash_size);
+    EVP_MD_CTX_free(mdctx);
 
-    check(block);
-    SHA256_Update(&ctx, block, FEC_BLOCKSIZE);
+    check(hash_size == digest_length_)
 
-    check(hash);
-    SHA256_Final(hash, &ctx);
+    std::fill(hash + hash_size, hash + padded_digest_length_, 0);
     return 0;
 }
 
-/* computes a verity hash for FEC_BLOCKSIZE bytes from buffer `block' and
-   compares it to the expected value in `expected' */
-bool check_block_hash(const uint8_t *expected, const uint8_t *block,
-                      const std::vector<uint8_t> &salt) {
-    check(block);
+int hashtree_info::initialize(uint64_t hash_start, uint64_t data_blocks,
+                              const std::vector<uint8_t> &salt, int nid) {
+    check(nid == NID_sha256 || nid == NID_sha1);
+
+    this->hash_start = hash_start;
+    this->data_blocks = data_blocks;
+    this->salt = salt;
+    this->nid_ = nid;
 
-    uint8_t hash[SHA256_DIGEST_LENGTH];
+    digest_length_ = nid == NID_sha1 ? SHA_DIGEST_LENGTH : SHA256_DIGEST_LENGTH;
+    // The padded digest size for both sha256 and sha1 are 256 bytes.
+    padded_digest_length_ = SHA256_DIGEST_LENGTH;
 
-    if (unlikely(get_hash(block, hash, salt) == -1)) {
+    return 0;
+}
+
+bool hashtree_info::check_block_hash(const uint8_t *expected,
+                                     const uint8_t *block) {
+    check(block);
+    std::vector<uint8_t> hash(digest_length_, 0);
+
+    if (unlikely(get_hash(block, hash.data()) == -1)) {
         error("failed to hash");
         return false;
     }
 
     check(expected);
-    return !memcmp(expected, hash, SHA256_DIGEST_LENGTH);
+    return !memcmp(expected, hash.data(), digest_length_);
 }
 
-/* reads a verity hash and the corresponding data block using error correction,
-   if available */
-static bool ecc_read_hashes(fec_handle *f, uint64_t hash_offset,
-        uint8_t *hash, uint64_t data_offset, uint8_t *data)
-{
+bool hashtree_info::check_block_hash_with_index(uint64_t index,
+                                                const uint8_t *block) {
+    check(index < data_blocks)
+
+    const uint8_t *expected = &hash_data[index * padded_digest_length_];
+    return check_block_hash(expected, block);
+}
+
+// Reads the hash and the corresponding data block using error correction, if
+// available.
+bool hashtree_info::ecc_read_hashes(fec_handle *f, uint64_t hash_offset,
+                                    uint8_t *hash, uint64_t data_offset,
+                                    uint8_t *data) {
     check(f);
 
-    if (hash && fec_pread(f, hash, SHA256_DIGEST_LENGTH, hash_offset) !=
-                    SHA256_DIGEST_LENGTH) {
+    if (hash &&
+        fec_pread(f, hash, digest_length_, hash_offset) != digest_length_) {
         error("failed to read hash tree: offset %" PRIu64 ": %s", hash_offset,
-            strerror(errno));
+              strerror(errno));
         return false;
     }
 
@@ -174,46 +196,38 @@ static bool ecc_read_hashes(fec_handle *f, uint64_t hash_offset,
 
     if (fec_pread(f, data, FEC_BLOCKSIZE, data_offset) != FEC_BLOCKSIZE) {
         error("failed to read hash tree: data_offset %" PRIu64 ": %s",
-            data_offset, strerror(errno));
+              data_offset, strerror(errno));
         return false;
     }
 
     return true;
 }
 
-/* reads the verity hash tree, validates it against the root hash in `root',
-   corrects errors if necessary, and copies valid data blocks for later use
-   to `f->verity.hash' */
-static int verify_tree(hashtree_info *hashtree, const fec_handle *f,
-                       const uint8_t *root) {
-    uint8_t data[FEC_BLOCKSIZE];
-    uint8_t hash[SHA256_DIGEST_LENGTH];
-
-    check(hashtree);
+int hashtree_info::verify_tree(const fec_handle *f, const uint8_t *root) {
     check(f);
     check(root);
 
+    uint8_t data[FEC_BLOCKSIZE];
+
     uint32_t levels = 0;
 
     /* calculate the size and the number of levels in the hash tree */
-    hashtree->hash_size =
-        verity_get_size(hashtree->data_blocks * FEC_BLOCKSIZE, &levels, NULL);
+    uint64_t hash_size = verity_get_size(data_blocks * FEC_BLOCKSIZE, &levels,
+                                         NULL, padded_digest_length_);
 
-    check(hashtree->hash_start < UINT64_MAX - hashtree->hash_size);
-    check(hashtree->hash_start + hashtree->hash_size <= f->data_size);
+    check(hash_start < UINT64_MAX - hash_size);
+    check(hash_start + hash_size <= f->data_size);
 
-    uint64_t hash_offset = hashtree->hash_start;
+    uint64_t hash_offset = hash_start;
     uint64_t data_offset = hash_offset + FEC_BLOCKSIZE;
 
-    hashtree->hash_data_offset = data_offset;
-
     /* validate the root hash */
     if (!raw_pread(f->fd, data, FEC_BLOCKSIZE, hash_offset) ||
-        !check_block_hash(root, data, hashtree->salt)) {
+        !check_block_hash(root, data)) {
         /* try to correct */
-        if (!ecc_read_hashes(const_cast<fec_handle *>(f), 0, NULL, hash_offset,
-                             data) ||
-            !check_block_hash(root, data, hashtree->salt)) {
+        if (!ecc_read_hashes(const_cast<fec_handle *>(f), 0, nullptr,
+                             hash_offset, data) ||
+            !check_block_hash(root, data)) {
             error("root hash invalid");
             return -1;
         } else if (f->mode & O_RDWR &&
@@ -228,59 +242,59 @@ static int verify_tree(hashtree_info *hashtree, const fec_handle *f,
     /* calculate the number of hashes on each level */
     uint32_t hashes[levels];
 
-    verity_get_size(hashtree->data_blocks * FEC_BLOCKSIZE, NULL, hashes);
+    verity_get_size(data_blocks * FEC_BLOCKSIZE, NULL, hashes,
+                    padded_digest_length_);
 
+    uint64_t hash_data_offset = data_offset;
+    uint32_t hash_data_blocks = 0;
     /* calculate the size and offset for the data hashes */
     for (uint32_t i = 1; i < levels; ++i) {
         uint32_t blocks = hashes[levels - i];
         debug("%u hash blocks on level %u", blocks, levels - i);
 
-        hashtree->hash_data_offset = data_offset;
-        hashtree->hash_data_blocks = blocks;
+        hash_data_offset = data_offset;
+        hash_data_blocks = blocks;
 
         data_offset += blocks * FEC_BLOCKSIZE;
     }
 
-    check(hashtree->hash_data_blocks);
-    check(hashtree->hash_data_blocks <= hashtree->hash_size / FEC_BLOCKSIZE);
+    check(hash_data_blocks);
+    check(hash_data_blocks <= hash_size / FEC_BLOCKSIZE);
 
-    check(hashtree->hash_data_offset);
-    check(hashtree->hash_data_offset <=
-          UINT64_MAX - (hashtree->hash_data_blocks * FEC_BLOCKSIZE));
-    check(hashtree->hash_data_offset < f->data_size);
-    check(hashtree->hash_data_offset +
-              hashtree->hash_data_blocks * FEC_BLOCKSIZE <=
-          f->data_size);
+    check(hash_data_offset);
+    check(hash_data_offset <= UINT64_MAX - (hash_data_blocks * FEC_BLOCKSIZE));
+    check(hash_data_offset < f->data_size);
+    check(hash_data_offset + hash_data_blocks * FEC_BLOCKSIZE <= f->data_size);
 
     /* copy data hashes to memory in case they are corrupted, so we don't
        have to correct them every time they are needed */
-    std::vector<uint8_t> data_hashes(hashtree->hash_data_blocks * FEC_BLOCKSIZE,
-                                     0);
+    std::vector<uint8_t> data_hashes(hash_data_blocks * FEC_BLOCKSIZE, 0);
 
     /* validate the rest of the hash tree */
     data_offset = hash_offset + FEC_BLOCKSIZE;
 
+    std::vector<uint8_t> buffer(padded_digest_length_, 0);
     for (uint32_t i = 1; i < levels; ++i) {
         uint32_t blocks = hashes[levels - i];
 
         for (uint32_t j = 0; j < blocks; ++j) {
             /* ecc reads are very I/O intensive, so read raw hash tree and do
                error correcting only if it doesn't validate */
-            if (!raw_pread(f->fd, hash, SHA256_DIGEST_LENGTH,
-                           hash_offset + j * SHA256_DIGEST_LENGTH) ||
+            if (!raw_pread(f->fd, buffer.data(), padded_digest_length_,
+                           hash_offset + j * padded_digest_length_) ||
                 !raw_pread(f->fd, data, FEC_BLOCKSIZE,
                            data_offset + j * FEC_BLOCKSIZE)) {
                 error("failed to read hashes: %s", strerror(errno));
                 return -1;
             }
 
-            if (!check_block_hash(hash, data, hashtree->salt)) {
+            if (!check_block_hash(buffer.data(), data)) {
                 /* try to correct */
                 if (!ecc_read_hashes(const_cast<fec_handle *>(f),
-                                     hash_offset + j * SHA256_DIGEST_LENGTH,
-                                     hash, data_offset + j * FEC_BLOCKSIZE,
-                                     data) ||
-                    !check_block_hash(hash, data, hashtree->salt)) {
+                                     hash_offset + j * padded_digest_length_,
+                                     buffer.data(),
+                                     data_offset + j * FEC_BLOCKSIZE, data) ||
+                    !check_block_hash(buffer.data(), data)) {
                     error("invalid hash tree: hash_offset %" PRIu64
                           ", "
                           "data_offset %" PRIu64 ", block %u",
@@ -291,8 +305,8 @@ static int verify_tree(hashtree_info *hashtree, const fec_handle *f,
                 /* update the corrected blocks to the file if we are in r/w
                    mode */
                 if (f->mode & O_RDWR) {
-                    if (!raw_pwrite(f->fd, hash, SHA256_DIGEST_LENGTH,
-                                    hash_offset + j * SHA256_DIGEST_LENGTH) ||
+                    if (!raw_pwrite(f->fd, buffer.data(), padded_digest_length_,
+                                    hash_offset + j * padded_digest_length_) ||
                         !raw_pwrite(f->fd, data, FEC_BLOCKSIZE,
                                     data_offset + j * FEC_BLOCKSIZE)) {
                         error("failed to write hashes: %s", strerror(errno));
@@ -301,7 +315,7 @@ static int verify_tree(hashtree_info *hashtree, const fec_handle *f,
                 }
             }
 
-            if (blocks == hashtree->hash_data_blocks) {
+            if (blocks == hash_data_blocks) {
                 std::copy(data, data + FEC_BLOCKSIZE,
                           data_hashes.begin() + j * FEC_BLOCKSIZE);
             }
@@ -313,7 +327,14 @@ static int verify_tree(hashtree_info *hashtree, const fec_handle *f,
 
     debug("valid");
 
-    hashtree->hash = std::move(data_hashes);
+    this->hash_data = std::move(data_hashes);
+
+    std::vector<uint8_t> zero_block(FEC_BLOCKSIZE, 0);
+    zero_hash.resize(padded_digest_length_, 0);
+    if (get_hash(zero_block.data(), zero_hash.data()) == -1) {
+        error("failed to hash");
+        return -1;
+    }
     return 0;
 }
 
@@ -328,7 +349,6 @@ static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useec
 
     debug("offset = %" PRIu64 ", size = %u", offset, size);
 
-    verity_info *v = &f->verity;
     std::string table(size, 0);
 
     if (!useecc) {
@@ -347,6 +367,8 @@ static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useec
     int i = 0;
     std::vector<uint8_t> salt;
     uint8_t root[SHA256_DIGEST_LENGTH];
+    uint64_t hash_start = 0;
+    uint64_t data_blocks = 0;
 
     auto tokens = android::base::Split(table, " ");
 
@@ -368,7 +390,7 @@ static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useec
             break;
         case 5: /* num_data_blocks */
             if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE,
-                             &v->hashtree.data_blocks) == -1) {
+                             &data_blocks) == -1) {
                 error("invalid number of verity data blocks: %s",
                     token.c_str());
                 return -1;
@@ -376,12 +398,12 @@ static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useec
             break;
         case 6: /* hash_start_block */
             if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE,
-                             &v->hashtree.hash_start) == -1) {
+                             &hash_start) == -1) {
                 error("invalid verity hash start block: %s", token.c_str());
                 return -1;
             }
 
-            v->hashtree.hash_start *= FEC_BLOCKSIZE;
+            hash_start *= FEC_BLOCKSIZE;
             break;
         case 7: /* algorithm */
             if (token != "sha256") {
@@ -420,32 +442,25 @@ static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useec
         return -1;
     }
 
-    check(v->hashtree.hash_start < f->data_size);
+    check(hash_start < f->data_size);
 
-    if (v->metadata_start < v->hashtree.hash_start) {
-        check(v->hashtree.data_blocks == v->metadata_start / FEC_BLOCKSIZE);
+    verity_info *v = &f->verity;
+    if (v->metadata_start < hash_start) {
+        check(data_blocks == v->metadata_start / FEC_BLOCKSIZE);
     } else {
-        check(v->hashtree.data_blocks ==
-              v->hashtree.hash_start / FEC_BLOCKSIZE);
+        check(data_blocks == hash_start / FEC_BLOCKSIZE);
     }
 
-    v->hashtree.salt = std::move(salt);
     v->table = std::move(table);
 
+    v->hashtree.initialize(hash_start, data_blocks, salt, NID_sha256);
     if (!(f->flags & FEC_VERITY_DISABLE)) {
-        if (verify_tree(&v->hashtree, f, root) == -1) {
+        if (v->hashtree.verify_tree(f, root) == -1) {
             return -1;
         }
 
-        check(!v->hashtree.hash.empty());
-
-        std::vector<uint8_t> zero_block(FEC_BLOCKSIZE, 0);
-        v->hashtree.zero_hash.assign(SHA256_DIGEST_LENGTH, 0);
-        if (get_hash(zero_block.data(), v->hashtree.zero_hash.data(),
-                     v->hashtree.salt) == -1) {
-            error("failed to hash");
-            return -1;
-        }
+        check(!v->hashtree.hash_data.empty());
+        check(!v->hashtree.zero_hash.empty());
     }
 
     return 0;
index f2c1e7d..ca5d91a 100644 (file)
@@ -37,9 +37,10 @@ class FecUnitTest : public ::testing::Test {
             std::vector<uint8_t> tmp_vec(4096, i);
             image_.insert(image_.end(), tmp_vec.begin(), tmp_vec.end());
         }
-
+    }
+    void BuildHashtree(const std::string &hash_name) {
         // Build the hashtree.
-        HashTreeBuilder builder(4096, HashTreeBuilder::HashFunction("sha256"));
+        HashTreeBuilder builder(4096, HashTreeBuilder::HashFunction(hash_name));
         // Use a random salt.
         salt_ = std::vector<uint8_t>(64, 10);
         ASSERT_TRUE(builder.Initialize(image_.size(), salt_));
@@ -47,16 +48,18 @@ class FecUnitTest : public ::testing::Test {
         ASSERT_TRUE(builder.BuildHashTree());
         root_hash_ = builder.root_hash();
 
-        // Append the hashtree to the end of image.
         TemporaryFile temp_file;
         ASSERT_TRUE(builder.WriteHashTreeToFd(temp_file.fd, 0));
         android::base::ReadFileToString(temp_file.path, &hashtree_content_);
-        image_.insert(image_.end(), hashtree_content_.begin(),
-                      hashtree_content_.end());
     }
 
     // Builds the verity metadata and appends the bytes to the image.
     void BuildAndAppendsVerityMetadata() {
+        BuildHashtree("sha256");
+        // Append the hashtree to the end of image.
+        image_.insert(image_.end(), hashtree_content_.begin(),
+                      hashtree_content_.end());
+
         // The metadata table has the format: "1 block_device, block_device,
         // BLOCK_SIZE, BLOCK_SIZE, data_blocks, data_blocks, 'sha256',
         // root_hash, salt".
@@ -131,8 +134,12 @@ TEST_F(FecUnitTest, LoadVerityImage_ParseVerity) {
     // the fec hashtree only stores the hash of the lowest level.
     ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
                                    hashtree_content_.end()),
-              handle->hashtree().hash);
-    ASSERT_EQ(hashtree_content_.size(), handle->hashtree().hash_size);
+              handle->hashtree().hash_data);
+
+    uint64_t hash_size =
+        verity_get_size(handle->hashtree().data_blocks * FEC_BLOCKSIZE, nullptr,
+                        nullptr, SHA256_DIGEST_LENGTH);
+    ASSERT_EQ(hashtree_content_.size(), hash_size);
 }
 
 TEST_F(FecUnitTest, LoadVerityImage_ParseEcc) {
@@ -167,3 +174,31 @@ TEST_F(FecUnitTest, LoadVerityImage_ParseEcc) {
     // 256 (data) + 3 (hashtree) + 8 (verity meta)
     ASSERT_EQ(267, ecc_metadata.blocks);
 }
+
+TEST_F(FecUnitTest, VerityImage_FecRead) {
+    TemporaryFile verity_image;
+    BuildAndAppendsVerityMetadata();
+    ASSERT_TRUE(android::base::WriteFully(verity_image.fd, image_.data(),
+                                          image_.size()));
+    TemporaryFile ecc_image;
+    BuildAndAppendsEccImage(verity_image.path, ecc_image.path);
+    std::string ecc_content;
+    ASSERT_TRUE(android::base::ReadFileToString(ecc_image.path, &ecc_content));
+    ASSERT_TRUE(android::base::WriteStringToFd(ecc_content, verity_image.fd));
+
+    // Corrupt the last block
+    uint64_t corrupt_offset = 4096 * 255;
+    ASSERT_EQ(corrupt_offset, lseek64(verity_image.fd, corrupt_offset, 0));
+    std::vector<uint8_t> corruption(100, 10);
+    ASSERT_TRUE(android::base::WriteFully(verity_image.fd, corruption.data(),
+                                          corruption.size()));
+
+    std::vector<uint8_t> read_data(1024, 0);
+    struct fec_handle *handle = nullptr;
+    ASSERT_EQ(0,
+              fec_open(&handle, verity_image.path, O_RDONLY, FEC_FS_EXT4, 2));
+    std::unique_ptr<fec_handle> guard(handle);
+
+    ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
+    ASSERT_EQ(std::vector<uint8_t>(1024, 255), read_data);
+}