Generate verity config.
am: 3a4dfac2bc
Change-Id: Ieed6401900ddbf63f870475a6ebecad87c19eeba
diff --git a/Android.mk b/Android.mk
index ba66d67..4d306c4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -626,10 +626,11 @@
# server-side code. This is used for delta_generator and unittests but not
# for any client code.
ue_libpayload_generator_exported_static_libraries := \
+ libavb \
+ libbrotli \
libbsdiff \
libdivsufsort \
libdivsufsort64 \
- libbrotli \
liblzma \
libpayload_consumer \
libpuffdiff \
@@ -661,6 +662,7 @@
payload_generator/inplace_generator.cc \
payload_generator/mapfile_filesystem.cc \
payload_generator/payload_file.cc \
+ payload_generator/payload_generation_config_android.cc \
payload_generator/payload_generation_config.cc \
payload_generator/payload_signer.cc \
payload_generator/raw_filesystem.cc \
@@ -680,6 +682,7 @@
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := $(ue_common_c_includes)
LOCAL_STATIC_LIBRARIES := \
+ libavb \
libbsdiff \
libdivsufsort \
libdivsufsort64 \
@@ -709,6 +712,7 @@
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := $(ue_common_c_includes)
LOCAL_STATIC_LIBRARIES := \
+ libavb \
libbsdiff \
libdivsufsort \
libdivsufsort64 \
@@ -743,6 +747,7 @@
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := $(ue_common_c_includes)
LOCAL_STATIC_LIBRARIES := \
+ libavb_host_sysdeps \
libpayload_consumer \
libpayload_generator \
$(ue_common_static_libraries) \
diff --git a/payload_consumer/payload_constants.cc b/payload_consumer/payload_constants.cc
index 6e7cd00..dacd9fb 100644
--- a/payload_consumer/payload_constants.cc
+++ b/payload_consumer/payload_constants.cc
@@ -30,6 +30,7 @@
const uint32_t kOpSrcHashMinorPayloadVersion = 3;
const uint32_t kBrotliBsdiffMinorPayloadVersion = 4;
const uint32_t kPuffdiffMinorPayloadVersion = 5;
+const uint32_t kVerityMinorPayloadVersion = 6;
const uint64_t kMinSupportedMajorPayloadVersion = 1;
const uint64_t kMaxSupportedMajorPayloadVersion = 2;
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h
index 0833484..7f76898 100644
--- a/payload_consumer/payload_constants.h
+++ b/payload_consumer/payload_constants.h
@@ -53,6 +53,9 @@
// The minor version that allows PUFFDIFF operation.
extern const uint32_t kPuffdiffMinorPayloadVersion;
+// The minor version that allows Verity hash tree and FEC generation.
+extern const uint32_t kVerityMinorPayloadVersion;
+
// The minimum and maximum supported minor version.
extern const uint32_t kMinSupportedMinorPayloadVersion;
extern const uint32_t kMaxSupportedMinorPayloadVersion;
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 75d1016..9d14474 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -299,6 +299,17 @@
ExtentRanges old_visited_blocks;
ExtentRanges new_visited_blocks;
+ // If verity is enabled, mark those blocks as visited to skip generating
+ // operations for them.
+ if (version.minor >= kVerityMinorPayloadVersion &&
+ !new_part.verity.IsEmpty()) {
+ LOG(INFO) << "Skipping verity hash tree blocks: "
+ << ExtentsToString({new_part.verity.hash_tree_extent});
+ new_visited_blocks.AddExtent(new_part.verity.hash_tree_extent);
+ // TODO(senj): Enable this when we have FEC support.
+ // new_visited_blocks.AddExtent(new_part.verity.fec_extent);
+ }
+
TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(
aops,
old_part.path,
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
index a83cea2..07aa3a8 100644
--- a/payload_generator/delta_diff_utils_unittest.cc
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -161,6 +161,28 @@
ExtentRanges new_visited_blocks_;
};
+TEST_F(DeltaDiffUtilsTest, SkipVerityExtentsTest) {
+ new_part_.verity.hash_tree_extent = ExtentForRange(20, 30);
+
+ BlobFileWriter blob_file(blob_fd_, &blob_size_);
+ EXPECT_TRUE(diff_utils::DeltaReadPartition(
+ &aops_,
+ old_part_,
+ new_part_,
+ -1,
+ -1,
+ PayloadVersion(kMaxSupportedMajorPayloadVersion,
+ kVerityMinorPayloadVersion),
+ &blob_file));
+ for (const auto& aop : aops_) {
+ new_visited_blocks_.AddRepeatedExtents(aop.op.dst_extents());
+ }
+ for (const auto& extent : new_visited_blocks_.extent_set()) {
+ EXPECT_FALSE(ExtentRanges::ExtentsOverlap(
+ extent, new_part_.verity.hash_tree_extent));
+ }
+}
+
TEST_F(DeltaDiffUtilsTest, MoveSmallTest) {
brillo::Blob data_blob(block_size_);
test_utils::FillWithData(&data_blob);
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index b842f33..eecb345 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -589,6 +589,9 @@
payload_config.max_timestamp = FLAGS_max_timestamp;
+ if (payload_config.version.minor >= kVerityMinorPayloadVersion)
+ CHECK(payload_config.target.LoadVerityConfig());
+
LOG(INFO) << "Generating " << (payload_config.is_delta ? "delta" : "full")
<< " update";
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index d5c59ce..0ffd3e2 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -20,6 +20,7 @@
#include <algorithm>
#include <map>
+#include <utility>
#include <base/strings/stringprintf.h>
@@ -91,6 +92,7 @@
part.name = new_conf.name;
part.aops = aops;
part.postinstall = new_conf.postinstall;
+ part.verity = new_conf.verity;
// Initialize the PartitionInfo objects if present.
if (!old_conf.path.empty())
TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(old_conf,
@@ -144,6 +146,22 @@
partition->set_filesystem_type(part.postinstall.filesystem_type);
partition->set_postinstall_optional(part.postinstall.optional);
}
+ if (!part.verity.IsEmpty()) {
+ if (part.verity.hash_tree_extent.num_blocks() != 0) {
+ *partition->mutable_hash_tree_data_extent() =
+ part.verity.hash_tree_data_extent;
+ *partition->mutable_hash_tree_extent() = part.verity.hash_tree_extent;
+ partition->set_hash_tree_algorithm(part.verity.hash_tree_algorithm);
+ if (!part.verity.hash_tree_salt.empty())
+ partition->set_hash_tree_salt(part.verity.hash_tree_salt.data(),
+ part.verity.hash_tree_salt.size());
+ }
+ if (part.verity.fec_extent.num_blocks() != 0) {
+ *partition->mutable_fec_data_extent() = part.verity.fec_data_extent;
+ *partition->mutable_fec_extent() = part.verity.fec_extent;
+ partition->set_fec_roots(part.verity.fec_roots);
+ }
+ }
for (const AnnotatedOperation& aop : part.aops) {
*partition->add_operations() = aop.op;
}
diff --git a/payload_generator/payload_file.h b/payload_generator/payload_file.h
index 7cc792a..9dc80a7 100644
--- a/payload_generator/payload_file.h
+++ b/payload_generator/payload_file.h
@@ -95,6 +95,7 @@
PartitionInfo new_info;
PostInstallConfig postinstall;
+ VerityConfig verity;
};
std::vector<Partition> part_vec_;
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index 5fe56b5..c49fdb5 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -33,6 +33,13 @@
return !run && path.empty() && filesystem_type.empty() && !optional;
}
+bool VerityConfig::IsEmpty() const {
+ return hash_tree_data_extent.num_blocks() == 0 &&
+ hash_tree_extent.num_blocks() == 0 && hash_tree_algorithm.empty() &&
+ hash_tree_salt.empty() && fec_data_extent.num_blocks() == 0 &&
+ fec_extent.num_blocks() == 0 && fec_roots == 0;
+}
+
bool PartitionConfig::ValidateExists() const {
TEST_AND_RETURN_FALSE(!path.empty());
TEST_AND_RETURN_FALSE(utils::FileExists(path.c_str()));
@@ -136,7 +143,8 @@
minor == kSourceMinorPayloadVersion ||
minor == kOpSrcHashMinorPayloadVersion ||
minor == kBrotliBsdiffMinorPayloadVersion ||
- minor == kPuffdiffMinorPayloadVersion);
+ minor == kPuffdiffMinorPayloadVersion ||
+ minor == kVerityMinorPayloadVersion);
return true;
}
@@ -199,8 +207,9 @@
TEST_AND_RETURN_FALSE(part.ValidateExists());
TEST_AND_RETURN_FALSE(part.size % block_size == 0);
}
- // Source partition should not have postinstall.
+ // Source partition should not have postinstall or verity config.
TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
+ TEST_AND_RETURN_FALSE(part.verity.IsEmpty());
}
// If new_image_info is present, old_image_info must be present.
@@ -220,6 +229,8 @@
TEST_AND_RETURN_FALSE(rootfs_partition_size >= part.size);
if (version.major == kChromeOSMajorPayloadVersion)
TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
+ if (version.minor < kVerityMinorPayloadVersion)
+ TEST_AND_RETURN_FALSE(part.verity.IsEmpty());
}
TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 358a76d..38e7b10 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -24,6 +24,7 @@
#include <vector>
#include <brillo/key_value_store.h>
+#include <brillo/secure_blob.h>
#include "update_engine/payload_consumer/payload_constants.h"
#include "update_engine/payload_generator/filesystem_interface.h"
@@ -51,6 +52,34 @@
bool optional = false;
};
+// Data will be written to the payload and used for hash tree and FEC generation
+// at device update time.
+struct VerityConfig {
+ // Whether the verity config is empty.
+ bool IsEmpty() const;
+
+ // The extent for data covered by verity hash tree.
+ Extent hash_tree_data_extent;
+
+ // The extent to store verity hash tree.
+ Extent hash_tree_extent;
+
+ // The hash algorithm used in verity hash tree.
+ std::string hash_tree_algorithm;
+
+ // The salt used for verity hash tree.
+ brillo::Blob hash_tree_salt;
+
+ // The extent for data covered by FEC.
+ Extent fec_data_extent;
+
+ // The extent to store FEC.
+ Extent fec_extent;
+
+ // The number of FEC roots.
+ uint32_t fec_roots = 0;
+};
+
struct PartitionConfig {
explicit PartitionConfig(std::string name) : name(name) {}
@@ -86,6 +115,7 @@
std::string name;
PostInstallConfig postinstall;
+ VerityConfig verity;
};
// The ImageConfig struct describes a pair of binaries kernel and rootfs and the
@@ -104,6 +134,9 @@
// Load postinstall config from a key value store.
bool LoadPostInstallConfig(const brillo::KeyValueStore& store);
+ // Load verity config by parsing the partition images.
+ bool LoadVerityConfig();
+
// Returns whether the |image_info| field is empty.
bool ImageInfoIsEmpty() const;
diff --git a/payload_generator/payload_generation_config_android.cc b/payload_generator/payload_generation_config_android.cc
new file mode 100644
index 0000000..5ffd11e
--- /dev/null
+++ b/payload_generator/payload_generation_config_android.cc
@@ -0,0 +1,96 @@
+//
+// Copyright (C) 2018 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 "update_engine/payload_generator/payload_generation_config.h"
+
+#include <base/logging.h>
+#include <brillo/secure_blob.h>
+#include <libavb/libavb.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+
+namespace chromeos_update_engine {
+
+namespace {
+bool AvbDescriptorCallback(const AvbDescriptor* descriptor, void* user_data) {
+ PartitionConfig* part = static_cast<PartitionConfig*>(user_data);
+ AvbDescriptor desc;
+ TEST_AND_RETURN_FALSE(
+ avb_descriptor_validate_and_byteswap(descriptor, &desc));
+ if (desc.tag != AVB_DESCRIPTOR_TAG_HASHTREE)
+ return true;
+
+ AvbHashtreeDescriptor hashtree;
+ TEST_AND_RETURN_FALSE(avb_hashtree_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbHashtreeDescriptor*>(descriptor), &hashtree));
+ // We only support version 1 right now, will need to introduce a new
+ // payload minor version to support new dm verity version.
+ TEST_AND_RETURN_FALSE(hashtree.dm_verity_version == 1);
+ part->verity.hash_tree_algorithm =
+ reinterpret_cast<const char*>(hashtree.hash_algorithm);
+
+ const uint8_t* salt = reinterpret_cast<const uint8_t*>(descriptor) +
+ sizeof(AvbHashtreeDescriptor) +
+ hashtree.partition_name_len;
+ part->verity.hash_tree_salt.assign(salt, salt + hashtree.salt_len);
+
+ TEST_AND_RETURN_FALSE(hashtree.data_block_size ==
+ part->fs_interface->GetBlockSize());
+ part->verity.hash_tree_data_extent =
+ ExtentForBytes(hashtree.data_block_size, 0, hashtree.image_size);
+
+ TEST_AND_RETURN_FALSE(hashtree.hash_block_size ==
+ part->fs_interface->GetBlockSize());
+ part->verity.hash_tree_extent = ExtentForBytes(
+ hashtree.hash_block_size, hashtree.tree_offset, hashtree.tree_size);
+
+ part->verity.fec_data_extent =
+ ExtentForBytes(hashtree.data_block_size, 0, hashtree.fec_offset);
+ part->verity.fec_extent = ExtentForBytes(
+ hashtree.data_block_size, hashtree.fec_offset, hashtree.fec_size);
+ part->verity.fec_roots = hashtree.fec_num_roots;
+ 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();
+
+ 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));
+ }
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config_chromeos.cc b/payload_generator/payload_generation_config_chromeos.cc
new file mode 100644
index 0000000..bb05aff
--- /dev/null
+++ b/payload_generator/payload_generation_config_chromeos.cc
@@ -0,0 +1,25 @@
+//
+// Copyright (C) 2018 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 "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+bool ImageConfig::LoadVerityConfig() {
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/update_engine.gyp b/update_engine.gyp
index 45b0a84..c90c018 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -411,6 +411,7 @@
'payload_generator/inplace_generator.cc',
'payload_generator/mapfile_filesystem.cc',
'payload_generator/payload_file.cc',
+ 'payload_generator/payload_generation_config_chromeos.cc',
'payload_generator/payload_generation_config.cc',
'payload_generator/payload_signer.cc',
'payload_generator/raw_filesystem.cc',