summaryrefslogtreecommitdiff
path: root/libfec
diff options
context:
space:
mode:
authorTianjie Xu <xunchang@google.com>2019-12-02 12:55:33 -0800
committerTianjie Xu <xunchang@google.com>2019-12-18 14:27:02 -0800
commit622e628b4dd1f0347c43c7c43ad73aa1d8542f09 (patch)
treefcf9f2908042a9d5d9763c4014126842ba023739 /libfec
parentfb4066a3900492e46a46b7d2ee09708e3168314f (diff)
downloadextras-622e628b4dd1f0347c43c7c43ad73aa1d8542f09.tar.gz
Add sha1 support for hashtree
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
Diffstat (limited to 'libfec')
-rw-r--r--libfec/fec_open.cpp5
-rw-r--r--libfec/fec_private.h50
-rw-r--r--libfec/fec_read.cpp33
-rw-r--r--libfec/fec_verity.cpp213
-rw-r--r--libfec/test/fec_unittest.cpp49
5 files changed, 213 insertions, 137 deletions
diff --git a/libfec/fec_open.cpp b/libfec/fec_open.cpp
index c8ae9804..18731f21 100644
--- a/libfec/fec_open.cpp
+++ b/libfec/fec_open.cpp
@@ -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) {
diff --git a/libfec/fec_private.h b/libfec/fec_private.h
index 81f34d59..62687994 100644
--- a/libfec/fec_private.h
+++ b/libfec/fec_private.h
@@ -19,19 +19,20 @@
#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)
diff --git a/libfec/fec_read.cpp b/libfec/fec_read.cpp
index 9ea6430b..889f990d 100644
--- a/libfec/fec_read.cpp
+++ b/libfec/fec_read.cpp
@@ -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);
diff --git a/libfec/fec_verity.cpp b/libfec/fec_verity.cpp
index 25b6ce48..9ac23777 100644
--- a/libfec/fec_verity.cpp
+++ b/libfec/fec_verity.cpp
@@ -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;
diff --git a/libfec/test/fec_unittest.cpp b/libfec/test/fec_unittest.cpp
index f2c1e7dc..ca5d91a8 100644
--- a/libfec/test/fec_unittest.cpp
+++ b/libfec/test/fec_unittest.cpp
@@ -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);
+}