summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianjie Xu <xunchang@google.com>2020-03-27 20:33:48 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-03-27 20:33:48 +0000
commitf76271cb2d5579ee4b4e77d694cef837e4075382 (patch)
treebe2a73f1c04f9037a0bd4b815ab60ab0a28f4603
parentcebc313870e23d23fa190a935a36720088c58b69 (diff)
parentd5e379cffb2312d38d402085e55a24164ecf0511 (diff)
downloadextras-f76271cb2d5579ee4b4e77d694cef837e4075382.tar.gz
Merge changes from topic "fec_avb" into rvc-dev
* changes: Allow building libfec without libavb Suport parsing hashtree and ecc data from avb
-rw-r--r--libfec/Android.bp46
-rw-r--r--libfec/avb_utils.cpp160
-rw-r--r--libfec/avb_utils.h30
-rw-r--r--libfec/avb_utils_stub.cpp27
-rw-r--r--libfec/fec_open.cpp18
-rw-r--r--libfec/fec_private.h10
-rw-r--r--libfec/test/Android.bp3
-rw-r--r--libfec/test/fec_unittest.cpp96
-rw-r--r--verity/Android.bp1
-rw-r--r--verity/fec/Android.bp5
10 files changed, 389 insertions, 7 deletions
diff --git a/libfec/Android.bp b/libfec/Android.bp
index 78f2fedd..3da6fe7c 100644
--- a/libfec/Android.bp
+++ b/libfec/Android.bp
@@ -1,16 +1,22 @@
// Copyright 2015 The Android Open Source Project
-cc_library {
- name: "libfec",
- host_supported: true,
- recovery_available: true,
+cc_defaults {
+ name: "libfec_default",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-O3",
+ "-D_LARGEFILE64_SOURCE",
+ ],
+
srcs: [
"fec_open.cpp",
"fec_read.cpp",
"fec_verity.cpp",
"fec_process.cpp",
],
- cflags: ["-Wall", "-Werror", "-O3", "-D_LARGEFILE64_SOURCE"],
+
export_include_dirs: ["include"],
// Exported header include/fec/io.h includes crypto_utils headers.
export_shared_lib_headers: ["libcrypto_utils"],
@@ -30,7 +36,10 @@ cc_library {
target: {
host: {
- cflags: ["-D_GNU_SOURCE", "-DFEC_NO_KLOG"]
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-DFEC_NO_KLOG",
+ ],
},
linux_glibc: {
sanitize: {
@@ -39,3 +48,28 @@ cc_library {
},
},
}
+
+cc_library {
+ name: "libfec",
+ defaults: ["libfec_default"],
+ host_supported: true,
+ recovery_available: true,
+
+ target: {
+ linux: {
+ srcs: [
+ "avb_utils.cpp",
+ ],
+ static_libs: [
+ "libavb",
+ ],
+ },
+
+ // libavb isn't available on mac.
+ darwin: {
+ srcs: [
+ "avb_utils_stub.cpp",
+ ],
+ },
+ },
+}
diff --git a/libfec/avb_utils.cpp b/libfec/avb_utils.cpp
new file mode 100644
index 00000000..8913f2a1
--- /dev/null
+++ b/libfec/avb_utils.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "avb_utils.h"
+
+#include <android-base/strings.h>
+#include <libavb/libavb.h>
+
+#include "fec_private.h"
+
+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;
+}
+
+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;
+}
diff --git a/libfec/avb_utils.h b/libfec/avb_utils.h
new file mode 100644
index 00000000..78254508
--- /dev/null
+++ b/libfec/avb_utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <vector>
+
+struct fec_handle;
+
+// Checks if there is a valid AVB footer in the end of the image. If so, parses
+// the contents of vbmeta struct from the given AVB footer. Returns 0 on
+// success.
+int parse_vbmeta_from_footer(fec_handle *f, std::vector<uint8_t> *vbmeta);
+
+// Parses the AVB vbmeta for the information of hashtree and fec data.
+int parse_avb_image(fec_handle *f, const std::vector<uint8_t> &vbmeta);
diff --git a/libfec/avb_utils_stub.cpp b/libfec/avb_utils_stub.cpp
new file mode 100644
index 00000000..0b6061e5
--- /dev/null
+++ b/libfec/avb_utils_stub.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "avb_utils.h"
+
+int parse_vbmeta_from_footer(fec_handle* /* f */,
+ std::vector<uint8_t> * /* vbmeta */) {
+ return -1;
+}
+
+int parse_avb_image(fec_handle* /* f */,
+ const std::vector<uint8_t>& /* vbmeta */) {
+ return -1;
+}
diff --git a/libfec/fec_open.cpp b/libfec/fec_open.cpp
index 18731f21..fe458097 100644
--- a/libfec/fec_open.cpp
+++ b/libfec/fec_open.cpp
@@ -29,6 +29,7 @@
#define fdatasync(fd) fcntl((fd), F_FULLFSYNC)
#endif
+#include "avb_utils.h"
#include "fec_private.h"
/* used by `find_offset'; returns metadata size for a file size `size' and
@@ -546,6 +547,23 @@ 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 */
+ // Don't parse the avb image if FEC_NO_AVB is set. It's used when libavb is
+ // not supported on mac.
+ 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);
}
diff --git a/libfec/fec_private.h b/libfec/fec_private.h
index 62687994..b28c4294 100644
--- a/libfec/fec_private.h
+++ b/libfec/fec_private.h
@@ -124,6 +124,12 @@ struct verity_info {
verity_header ecc_header;
};
+struct avb_info {
+ bool valid = false;
+ std::vector<uint8_t> vbmeta;
+ hashtree_info hashtree;
+};
+
struct fec_handle {
ecc_info ecc;
int fd;
@@ -134,10 +140,12 @@ struct fec_handle {
uint64_t data_size;
uint64_t pos;
uint64_t size;
+ // TODO(xunchang) switch to std::optional
verity_info verity;
+ avb_info avb;
hashtree_info hashtree() const {
- return verity.hashtree;
+ return avb.valid ? avb.hashtree : verity.hashtree;
}
};
diff --git a/libfec/test/Android.bp b/libfec/test/Android.bp
index 1bbad077..75182a27 100644
--- a/libfec/test/Android.bp
+++ b/libfec/test/Android.bp
@@ -24,6 +24,7 @@ cc_test_host {
static_libs: [
"libfec",
"libfec_rs",
+ "libavb",
"libcrypto_utils",
"libcrypto",
"libext4_utils",
@@ -47,12 +48,14 @@ cc_test_host {
gtest: true,
required: [
+ "avbtool",
"fec",
],
static_libs: [
"libverity_tree",
"libfec",
"libfec_rs",
+ "libavb",
"libcrypto_utils",
"libext4_utils",
"libsquashfs_utils",
diff --git a/libfec/test/fec_unittest.cpp b/libfec/test/fec_unittest.cpp
index ca5d91a8..421eb501 100644
--- a/libfec/test/fec_unittest.cpp
+++ b/libfec/test/fec_unittest.cpp
@@ -102,6 +102,20 @@ class FecUnitTest : public ::testing::Test {
ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str()));
}
+ void AddAvbHashtreeFooter(const std::string &image_name,
+ std::string algorithm = "sha256") {
+ salt_ = std::vector<uint8_t>(64, 10);
+ std::vector<std::string> cmd = {
+ "avbtool", "add_hashtree_footer",
+ "--salt", HashTreeBuilder::BytesArrayToString(salt_),
+ "--hash_algorithm", algorithm,
+ "--image", image_name,
+ };
+ ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str()));
+
+ BuildHashtree(algorithm);
+ }
+
std::vector<uint8_t> image_;
std::vector<uint8_t> salt_;
std::vector<uint8_t> root_hash_;
@@ -202,3 +216,85 @@ TEST_F(FecUnitTest, VerityImage_FecRead) {
ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
ASSERT_EQ(std::vector<uint8_t>(1024, 255), read_data);
}
+
+TEST_F(FecUnitTest, LoadAvbImage_HashtreeFooter) {
+ TemporaryFile avb_image;
+ ASSERT_TRUE(
+ android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
+ AddAvbHashtreeFooter(avb_image.path);
+
+ struct fec_handle *handle = nullptr;
+ ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
+ std::unique_ptr<fec_handle> guard(handle);
+
+ ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
+
+ ASSERT_TRUE(handle->avb.valid);
+
+ // check the hashtree.
+ ASSERT_EQ(salt_, handle->hashtree().salt);
+ ASSERT_EQ(1024 * 1024, handle->hashtree().hash_start);
+ // 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_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);
+
+ fec_ecc_metadata ecc_metadata{};
+ ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
+ ASSERT_TRUE(ecc_metadata.valid);
+ ASSERT_EQ(1024 * 1024 + hash_size, ecc_metadata.start);
+ ASSERT_EQ(259, ecc_metadata.blocks);
+}
+
+TEST_F(FecUnitTest, LoadAvbImage_CorrectHashtree) {
+ TemporaryFile avb_image;
+ ASSERT_TRUE(
+ android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
+ AddAvbHashtreeFooter(avb_image.path);
+
+ uint64_t corrupt_offset = 1024 * 1024 + 2 * 4096 + 50;
+ ASSERT_EQ(corrupt_offset, lseek64(avb_image.fd, corrupt_offset, 0));
+ std::vector<uint8_t> corruption(20, 5);
+ ASSERT_TRUE(android::base::WriteFully(avb_image.fd, corruption.data(),
+ corruption.size()));
+
+ struct fec_handle *handle = nullptr;
+ ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
+ std::unique_ptr<fec_handle> guard(handle);
+
+ ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
+ fec_ecc_metadata ecc_metadata{};
+ ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
+ ASSERT_TRUE(ecc_metadata.valid);
+}
+
+TEST_F(FecUnitTest, AvbImage_FecRead) {
+ TemporaryFile avb_image;
+ ASSERT_TRUE(
+ android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
+ AddAvbHashtreeFooter(avb_image.path, "sha1");
+
+ uint64_t corrupt_offset = 4096 * 10;
+ ASSERT_EQ(corrupt_offset, lseek64(avb_image.fd, corrupt_offset, 0));
+ std::vector<uint8_t> corruption(50, 99);
+ ASSERT_TRUE(android::base::WriteFully(avb_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, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
+ std::unique_ptr<fec_handle> guard(handle);
+
+ // Verify the hashtree has the expected content.
+ ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
+ hashtree_content_.end()),
+ handle->hashtree().hash_data);
+
+ // Verify the corruption gets corrected.
+ ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
+ ASSERT_EQ(std::vector<uint8_t>(1024, 10), read_data);
+}
diff --git a/verity/Android.bp b/verity/Android.bp
index 1158c77d..5d0a80c0 100644
--- a/verity/Android.bp
+++ b/verity/Android.bp
@@ -51,6 +51,7 @@ cc_binary_host {
static_libs: [
"libfec",
"libfec_rs",
+ "libavb",
"libcrypto_utils",
"libcrypto",
"libext4_utils",
diff --git a/verity/fec/Android.bp b/verity/fec/Android.bp
index a6bcef95..4bcecb09 100644
--- a/verity/fec/Android.bp
+++ b/verity/fec/Android.bp
@@ -7,6 +7,11 @@ cc_binary_host {
misc_undefined: ["integer"],
},
},
+ linux: {
+ static_libs: [
+ "libavb",
+ ],
+ },
},
srcs: [