summaryrefslogtreecommitdiff
path: root/libfec/fec_open.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libfec/fec_open.cpp')
-rw-r--r--libfec/fec_open.cpp156
1 files changed, 156 insertions, 0 deletions
diff --git a/libfec/fec_open.cpp b/libfec/fec_open.cpp
index 18731f21..8f3fa22d 100644
--- a/libfec/fec_open.cpp
+++ b/libfec/fec_open.cpp
@@ -18,7 +18,9 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <android-base/strings.h>
#include <ext4_utils/ext4_sb.h>
+#include <libavb/libavb.h>
#include <squashfs_utils.h>
#if defined(__linux__)
@@ -493,6 +495,145 @@ int fec_get_status(struct fec_handle *f, struct fec_status *s)
return 0;
}
+static int parse_vbmeta_from_footer(fec_handle *f,
+ std::vector<uint8_t> *vbmeta) {
+ if (f->size <= AVB_FOOTER_SIZE) {
+ debug("file size not large enough to be avb images:" PRIu64, f->size);
+ return -1;
+ }
+
+ AvbFooter footer_read;
+ if (!raw_pread(f->fd, &footer_read, AVB_FOOTER_SIZE,
+ f->size - AVB_FOOTER_SIZE)) {
+ error("failed to read footer: %s", strerror(errno));
+ return -1;
+ }
+
+ AvbFooter footer;
+ if (!avb_footer_validate_and_byteswap(&footer_read, &footer)) {
+ debug("invalid avb footer");
+ return -1;
+ }
+ uint64_t vbmeta_offset = footer.vbmeta_offset;
+ uint64_t vbmeta_size = footer.vbmeta_size;
+ check(vbmeta_offset <= f->size - sizeof(footer) - vbmeta_size);
+
+ std::vector<uint8_t> vbmeta_data(vbmeta_size, 0);
+ // TODO(xunchang) handle the sparse image with libsparse.
+ if (!raw_pread(f->fd, vbmeta_data.data(), vbmeta_data.size(),
+ vbmeta_offset)) {
+ error("failed to read avb vbmeta: %s", strerror(errno));
+ return -1;
+ }
+
+ if (auto status = avb_vbmeta_image_verify(
+ vbmeta_data.data(), vbmeta_data.size(), nullptr, nullptr);
+ status != AVB_VBMETA_VERIFY_RESULT_OK &&
+ status != AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED) {
+ error("failed to verify avb vbmeta, status: %d", status);
+ return -1;
+ }
+ *vbmeta = std::move(vbmeta_data);
+ return 0;
+}
+
+static int parse_avb_image(fec_handle *f, const std::vector<uint8_t> &vbmeta) {
+ // TODO(xunchang) check if avb verification or hashtree is disabled.
+
+ // Look for the hashtree descriptor, we expect exactly one descriptor in
+ // vbmeta.
+ // TODO(xunchang) handle the image with AvbHashDescriptor.
+ auto parse_descriptor = [](const AvbDescriptor *descriptor,
+ void *user_data) {
+ if (descriptor &&
+ avb_be64toh(descriptor->tag) == AVB_DESCRIPTOR_TAG_HASHTREE) {
+ auto desp = static_cast<const AvbDescriptor **>(user_data);
+ *desp = descriptor;
+ return false;
+ }
+ return true;
+ };
+
+ const AvbHashtreeDescriptor *hashtree_descriptor_ptr = nullptr;
+ avb_descriptor_foreach(vbmeta.data(), vbmeta.size(), parse_descriptor,
+ &hashtree_descriptor_ptr);
+
+ AvbHashtreeDescriptor hashtree_descriptor;
+ if (!avb_hashtree_descriptor_validate_and_byteswap(hashtree_descriptor_ptr,
+ &hashtree_descriptor)) {
+ error("failed to verify avb hashtree descriptor");
+ return -1;
+ }
+
+ // The partition name, salt, root append right after the hashtree
+ // descriptor.
+ auto read_ptr = reinterpret_cast<const uint8_t *>(hashtree_descriptor_ptr);
+ // Calculate the offset with respect to the vbmeta; and check both the
+ // salt & root are within the range.
+ uint32_t salt_offset =
+ sizeof(AvbHashtreeDescriptor) + hashtree_descriptor.partition_name_len;
+ uint32_t root_offset = salt_offset + hashtree_descriptor.salt_len;
+ check(hashtree_descriptor.salt_len < vbmeta.size());
+ check(salt_offset < vbmeta.size() - hashtree_descriptor.salt_len);
+ check(hashtree_descriptor.root_digest_len < vbmeta.size());
+ check(root_offset < vbmeta.size() - hashtree_descriptor.root_digest_len);
+ std::vector<uint8_t> salt(
+ read_ptr + salt_offset,
+ read_ptr + salt_offset + hashtree_descriptor.salt_len);
+ std::vector<uint8_t> root_hash(
+ read_ptr + root_offset,
+ read_ptr + root_offset + hashtree_descriptor.root_digest_len);
+
+ // Expect the AVB image has the format:
+ // 1. hashtree
+ // 2. ecc data
+ // 3. vbmeta
+ // 4. avb footer
+ check(hashtree_descriptor.fec_offset ==
+ hashtree_descriptor.tree_offset + hashtree_descriptor.tree_size);
+ check(hashtree_descriptor.fec_offset <=
+ f->size - hashtree_descriptor.fec_size);
+
+ f->data_size = hashtree_descriptor.fec_offset;
+
+ f->ecc.blocks = fec_div_round_up(f->data_size, FEC_BLOCKSIZE);
+ f->ecc.rounds = fec_div_round_up(f->ecc.blocks, f->ecc.rsn);
+ f->ecc.size = hashtree_descriptor.fec_size;
+ f->ecc.start = hashtree_descriptor.fec_offset;
+ // TODO(xunchang) verify the integrity of the ecc data.
+ f->ecc.valid = true;
+
+ std::string hash_algorithm =
+ reinterpret_cast<char *>(hashtree_descriptor.hash_algorithm);
+ int nid = -1;
+ if (android::base::EqualsIgnoreCase(hash_algorithm, "sha1")) {
+ nid = NID_sha1;
+ } else if (android::base::EqualsIgnoreCase(hash_algorithm, "sha256")) {
+ nid = NID_sha256;
+ } else {
+ error("unsupported hash algorithm %s", hash_algorithm.c_str());
+ }
+
+ hashtree_info hashtree;
+ hashtree.initialize(hashtree_descriptor.tree_offset,
+ hashtree_descriptor.tree_offset / FEC_BLOCKSIZE, salt,
+ nid);
+ if (hashtree.verify_tree(f, root_hash.data()) != 0) {
+ error("failed to verify hashtree");
+ return -1;
+ }
+
+ // We have validate the hashtree,
+ f->data_size = hashtree.hash_start;
+ f->avb = {
+ .valid = true,
+ .vbmeta = vbmeta,
+ .hashtree = std::move(hashtree),
+ };
+
+ return 0;
+}
+
/* opens `path' using given options and returns a fec_handle in `handle' if
successful */
int fec_open(struct fec_handle **handle, const char *path, int mode, int flags,
@@ -546,6 +687,21 @@ int fec_open(struct fec_handle **handle, const char *path, int mode, int flags,
f->data_size = f->size; /* until ecc and/or verity are loaded */
+ std::vector<uint8_t> vbmeta;
+ if (parse_vbmeta_from_footer(f.get(), &vbmeta) == 0) {
+ if (parse_avb_image(f.get(), vbmeta) != 0) {
+ error("failed to parse avb image.");
+ return -1;
+ }
+
+ *handle = f.release();
+ return 0;
+ }
+ // TODO(xunchang) For android, handle the case when vbmeta is in a separate
+ // image. We could use avb_slot_verify() && AvbOps from libavb_user.
+
+ // Fall back to use verity format.
+
if (load_ecc(f.get()) == -1) {
debug("error-correcting codes not found from '%s'", path);
}