Add support for parsing Verified Boot 1.0 metadata.
This is for devices that uses A/B but not AVB.
Now libfec is used on host as well.
Bug: 28171891
Test: update_engine_unittests
Test: brillo_update_payload generate
Test: brillo_update_payload verify
Change-Id: I71ac5868b8eb53cb688fe939c298965d59b574ea
diff --git a/Android.bp b/Android.bp
index 01ce29e..906731f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,7 +31,6 @@
"-DUSE_BINDER=1",
"-DUSE_CHROME_NETWORK_PROXY=0",
"-DUSE_CHROME_KIOSK_APP=0",
- "-DUSE_FEC=1",
"-DUSE_HWID_OVERRIDE=0",
"-DUSE_MTD=0",
"-DUSE_OMAHA=0",
@@ -69,6 +68,16 @@
},
target: {
+ android: {
+ cflags: [
+ "-DUSE_FEC=1",
+ ],
+ },
+ host: {
+ cflags: [
+ "-DUSE_FEC=0",
+ ],
+ },
darwin: {
enabled: false,
},
@@ -120,13 +129,8 @@
shared_libs: [
"libbase",
"libcrypto",
+ "libfec",
],
-
- target: {
- android: {
- shared_libs: ["libfec"],
- },
- },
}
cc_library_static {
@@ -177,16 +181,6 @@
"payload_consumer/xz_extent_writer.cc",
"payload_consumer/fec_file_descriptor.cc",
],
-
- target: {
- host: {
- cflags: [
- "-UUSE_FEC",
- "-DUSE_FEC=0",
- ],
- shared_libs: ["libfec"],
- },
- },
}
// libupdate_engine_boot_control (type: static_library)
diff --git a/payload_generator/payload_generation_config_android.cc b/payload_generator/payload_generation_config_android.cc
index e1dee58..90c053f 100644
--- a/payload_generator/payload_generation_config_android.cc
+++ b/payload_generator/payload_generation_config_android.cc
@@ -17,7 +17,10 @@
#include "update_engine/payload_generator/payload_generation_config.h"
#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
#include <brillo/secure_blob.h>
+#include <fec/io.h>
#include <libavb/libavb.h>
#include <verity/hash_tree_builder.h>
@@ -57,46 +60,9 @@
TEST_AND_RETURN_FALSE(hashtree.hash_block_size ==
part->fs_interface->GetBlockSize());
-
- // Generate hash tree based on the descriptor and verify that it matches
- // the hash tree stored in the image.
- auto hash_function =
- HashTreeBuilder::HashFunction(part->verity.hash_tree_algorithm);
- TEST_AND_RETURN_FALSE(hash_function != nullptr);
- HashTreeBuilder hash_tree_builder(hashtree.data_block_size, hash_function);
- TEST_AND_RETURN_FALSE(hash_tree_builder.Initialize(
- hashtree.image_size, part->verity.hash_tree_salt));
- TEST_AND_RETURN_FALSE(hash_tree_builder.CalculateSize(hashtree.image_size) ==
- hashtree.tree_size);
-
- brillo::Blob buffer;
- for (uint64_t offset = 0; offset < hashtree.image_size;) {
- constexpr uint64_t kBufferSize = 1024 * 1024;
- size_t bytes_to_read = std::min(kBufferSize, hashtree.image_size - offset);
- TEST_AND_RETURN_FALSE(
- utils::ReadFileChunk(part->path, offset, bytes_to_read, &buffer));
- TEST_AND_RETURN_FALSE(
- hash_tree_builder.Update(buffer.data(), buffer.size()));
- offset += buffer.size();
- buffer.clear();
- }
- TEST_AND_RETURN_FALSE(hash_tree_builder.BuildHashTree());
- TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
- part->path, hashtree.tree_offset, hashtree.tree_size, &buffer));
- TEST_AND_RETURN_FALSE(hash_tree_builder.CheckHashTree(buffer));
-
part->verity.hash_tree_extent = ExtentForBytes(
hashtree.hash_block_size, hashtree.tree_offset, hashtree.tree_size);
- TEST_AND_RETURN_FALSE(VerityWriterAndroid::EncodeFEC(part->path,
- 0 /* data_offset */,
- hashtree.fec_offset,
- hashtree.fec_offset,
- hashtree.fec_size,
- hashtree.fec_num_roots,
- hashtree.data_block_size,
- true /* verify_mode */));
-
part->verity.fec_data_extent =
ExtentForBytes(hashtree.data_block_size, 0, hashtree.fec_offset);
part->verity.fec_extent = ExtentForBytes(
@@ -104,30 +70,154 @@
part->verity.fec_roots = hashtree.fec_num_roots;
return true;
}
+
+// Generate hash tree and FEC based on the verity config and verify that it
+// matches the hash tree and FEC stored in the image.
+bool VerifyVerityConfig(const PartitionConfig& part) {
+ const size_t block_size = part.fs_interface->GetBlockSize();
+ if (part.verity.hash_tree_extent.num_blocks() != 0) {
+ auto hash_function =
+ HashTreeBuilder::HashFunction(part.verity.hash_tree_algorithm);
+ TEST_AND_RETURN_FALSE(hash_function != nullptr);
+ HashTreeBuilder hash_tree_builder(block_size, hash_function);
+ uint64_t data_size =
+ part.verity.hash_tree_data_extent.num_blocks() * block_size;
+ uint64_t tree_size = hash_tree_builder.CalculateSize(data_size);
+ TEST_AND_RETURN_FALSE(
+ tree_size == part.verity.hash_tree_extent.num_blocks() * block_size);
+ TEST_AND_RETURN_FALSE(
+ hash_tree_builder.Initialize(data_size, part.verity.hash_tree_salt));
+
+ brillo::Blob buffer;
+ for (uint64_t offset = part.verity.hash_tree_data_extent.start_block() *
+ block_size,
+ data_end = offset + data_size;
+ offset < data_end;) {
+ constexpr uint64_t kBufferSize = 1024 * 1024;
+ size_t bytes_to_read = std::min(kBufferSize, data_end - offset);
+ TEST_AND_RETURN_FALSE(
+ utils::ReadFileChunk(part.path, offset, bytes_to_read, &buffer));
+ TEST_AND_RETURN_FALSE(
+ hash_tree_builder.Update(buffer.data(), buffer.size()));
+ offset += buffer.size();
+ buffer.clear();
+ }
+ TEST_AND_RETURN_FALSE(hash_tree_builder.BuildHashTree());
+ TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
+ part.path,
+ part.verity.hash_tree_extent.start_block() * block_size,
+ tree_size,
+ &buffer));
+ TEST_AND_RETURN_FALSE(hash_tree_builder.CheckHashTree(buffer));
+ }
+
+ if (part.verity.fec_extent.num_blocks() != 0) {
+ TEST_AND_RETURN_FALSE(VerityWriterAndroid::EncodeFEC(
+ part.path,
+ part.verity.fec_data_extent.start_block() * block_size,
+ part.verity.fec_data_extent.num_blocks() * block_size,
+ part.verity.fec_extent.start_block() * block_size,
+ part.verity.fec_extent.num_blocks() * block_size,
+ part.verity.fec_roots,
+ block_size,
+ true /* verify_mode */));
+ }
+ return true;
+}
} // namespace
bool ImageConfig::LoadVerityConfig() {
for (PartitionConfig& part : partitions) {
- if (part.size < sizeof(AvbFooter))
- continue;
- uint64_t footer_offset = part.size - sizeof(AvbFooter);
- brillo::Blob buffer;
- TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
- part.path, footer_offset, sizeof(AvbFooter), &buffer));
- if (memcmp(buffer.data(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0)
- continue;
- LOG(INFO) << "Parsing verity config from AVB footer for " << part.name;
- AvbFooter footer;
- TEST_AND_RETURN_FALSE(avb_footer_validate_and_byteswap(
- reinterpret_cast<const AvbFooter*>(buffer.data()), &footer));
- buffer.clear();
+ // Parse AVB devices.
+ if (part.size > sizeof(AvbFooter)) {
+ uint64_t footer_offset = part.size - sizeof(AvbFooter);
+ brillo::Blob buffer;
+ TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
+ part.path, footer_offset, sizeof(AvbFooter), &buffer));
+ if (memcmp(buffer.data(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) == 0) {
+ LOG(INFO) << "Parsing verity config from AVB footer for " << part.name;
+ AvbFooter footer;
+ TEST_AND_RETURN_FALSE(avb_footer_validate_and_byteswap(
+ reinterpret_cast<const AvbFooter*>(buffer.data()), &footer));
+ buffer.clear();
- TEST_AND_RETURN_FALSE(footer.vbmeta_offset + sizeof(AvbVBMetaImageHeader) <=
- part.size);
- TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
- part.path, footer.vbmeta_offset, footer.vbmeta_size, &buffer));
- TEST_AND_RETURN_FALSE(avb_descriptor_foreach(
- buffer.data(), buffer.size(), AvbDescriptorCallback, &part));
+ TEST_AND_RETURN_FALSE(
+ footer.vbmeta_offset + sizeof(AvbVBMetaImageHeader) <= part.size);
+ TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
+ part.path, footer.vbmeta_offset, footer.vbmeta_size, &buffer));
+ TEST_AND_RETURN_FALSE(avb_descriptor_foreach(
+ buffer.data(), buffer.size(), AvbDescriptorCallback, &part));
+ }
+ }
+
+ // Parse VB1.0 devices with FEC metadata, devices with hash tree without
+ // FEC will be skipped for now.
+ if (part.verity.IsEmpty() && part.size > FEC_BLOCKSIZE) {
+ brillo::Blob fec_metadata;
+ TEST_AND_RETURN_FALSE(utils::ReadFileChunk(part.path,
+ part.size - FEC_BLOCKSIZE,
+ sizeof(fec_header),
+ &fec_metadata));
+ const fec_header* header =
+ reinterpret_cast<const fec_header*>(fec_metadata.data());
+ if (header->magic == FEC_MAGIC) {
+ LOG(INFO)
+ << "Parsing verity config from Verified Boot 1.0 metadata for "
+ << part.name;
+ const size_t block_size = part.fs_interface->GetBlockSize();
+ // FEC_VERITY_DISABLE skips verifying verity hash tree, because we will
+ // verify it ourselves later.
+ fec::io fh(part.path, O_RDONLY, FEC_VERITY_DISABLE);
+ TEST_AND_RETURN_FALSE(fh);
+ fec_verity_metadata verity_data;
+ if (fh.get_verity_metadata(verity_data)) {
+ auto verity_table = base::SplitString(verity_data.table,
+ " ",
+ base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+ TEST_AND_RETURN_FALSE(verity_table.size() == 10);
+ size_t data_block_size = 0;
+ TEST_AND_RETURN_FALSE(
+ base::StringToSizeT(verity_table[3], &data_block_size));
+ TEST_AND_RETURN_FALSE(block_size == data_block_size);
+ size_t hash_block_size = 0;
+ TEST_AND_RETURN_FALSE(
+ base::StringToSizeT(verity_table[4], &hash_block_size));
+ TEST_AND_RETURN_FALSE(block_size == hash_block_size);
+ uint64_t num_data_blocks = 0;
+ TEST_AND_RETURN_FALSE(
+ base::StringToUint64(verity_table[5], &num_data_blocks));
+ part.verity.hash_tree_data_extent =
+ ExtentForRange(0, num_data_blocks);
+ uint64_t hash_start_block = 0;
+ TEST_AND_RETURN_FALSE(
+ base::StringToUint64(verity_table[6], &hash_start_block));
+ part.verity.hash_tree_algorithm = verity_table[7];
+ TEST_AND_RETURN_FALSE(base::HexStringToBytes(
+ verity_table[9], &part.verity.hash_tree_salt));
+ auto hash_function =
+ HashTreeBuilder::HashFunction(part.verity.hash_tree_algorithm);
+ TEST_AND_RETURN_FALSE(hash_function != nullptr);
+ HashTreeBuilder hash_tree_builder(block_size, hash_function);
+ uint64_t tree_size =
+ hash_tree_builder.CalculateSize(num_data_blocks * block_size);
+ part.verity.hash_tree_extent =
+ ExtentForRange(hash_start_block, tree_size / block_size);
+ }
+ fec_ecc_metadata ecc_data;
+ if (fh.get_ecc_metadata(ecc_data) && ecc_data.valid) {
+ TEST_AND_RETURN_FALSE(block_size == FEC_BLOCKSIZE);
+ part.verity.fec_data_extent = ExtentForRange(0, ecc_data.blocks);
+ part.verity.fec_extent =
+ ExtentForBytes(block_size, ecc_data.start, header->fec_size);
+ part.verity.fec_roots = ecc_data.roots;
+ }
+ }
+ }
+
+ if (!part.verity.IsEmpty()) {
+ TEST_AND_RETURN_FALSE(VerifyVerityConfig(part));
+ }
}
return true;
}