Generate verity config.

A new minor version 6 kVerityMinorPayloadVersion is added, client
having a minor version >= 6 means it support writing verity hash
tree and fec given verity config from payload.

When generating payload in Android, if the source minor version is
>= 6, then we try to find the AVB footer for each partition image
and parse the AVB hashtree descriptor, if found then write verity
config to the payload, the verification of verity config will be
added in a follow up CL.

AVB is an Android specific format, so we will skip this for Chrome
OS for now, if later we decided to enable verity feature in Chrome
OS, we can implement the parsing logic for Chrome OS verity metadata
format in payload_generation_config_chromeos.cc

Bug: 28171891
Test: brillo_update_payload verify
Change-Id: Ic319ea05e9f0095e3a4721df79a6ab516fa8a915
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