New "properties" sub-command to export payload properties.

The new brillo_update_payload sub-command "properties" dumps a list of
properties for a given signed or unsigned payload. These properties are
normally included in the Omaha response, and extracted from python in
chromite.

This new sub-command helps to encapsulate the properties used by the
server side wehn serving a payload and to let the Android application
pass these required properties.

The properties include the payload and metadata hash and size.

Bug: 26991255
TEST=FEATURES=test emerge-link update_engine
TEST=mmma system/update_engine
TEST=`brillo_update_payload properties` for signed and unsigned payloads.

Change-Id: I4602ea4b8dc269e4cc66df4293ef9765d8dd031d
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index abf479b..d4b3641 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -230,6 +230,19 @@
   LOG(INFO) << "Done applying delta.";
 }
 
+int ExtractProperties(const string& payload_path, const string& props_file) {
+  brillo::KeyValueStore properties;
+  TEST_AND_RETURN_FALSE(
+      PayloadSigner::ExtractPayloadProperties(payload_path, &properties));
+  if (props_file == "-") {
+    printf("%s", properties.SaveToString().c_str());
+  } else {
+    properties.Save(base::FilePath(props_file));
+    LOG(INFO) << "Generated properties file at " << props_file;
+  }
+  return true;
+}
+
 int Main(int argc, char** argv) {
   DEFINE_string(old_image, "", "Path to the old rootfs");
   DEFINE_string(new_image, "", "Path to the new rootfs");
@@ -293,6 +306,9 @@
   DEFINE_int32(minor_version, -1,
                "The minor version of the payload being generated "
                "(-1 means autodetect).");
+  DEFINE_string(properties_file, "",
+                "If passed, dumps the payload properties of the payload passed "
+                "in --in_file and exits.");
 
   DEFINE_string(old_channel, "",
                 "The channel for the old image. 'dev-channel', 'npo-channel', "
@@ -368,6 +384,9 @@
     VerifySignedPayload(FLAGS_in_file, FLAGS_public_key);
     return 0;
   }
+  if (!FLAGS_properties_file.empty()) {
+    return ExtractProperties(FLAGS_in_file, FLAGS_properties_file) ? 0 : 1;
+  }
   if (!FLAGS_in_file.empty()) {
     ApplyDelta(FLAGS_in_file, FLAGS_old_kernel, FLAGS_old_image,
                FLAGS_prefs_dir);
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index a0c61b1..a73b891 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -19,9 +19,12 @@
 #include <endian.h>
 
 #include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <brillo/data_encoding.h>
+#include <brillo/streams/file_stream.h>
+#include <brillo/streams/stream.h>
 #include <openssl/pem.h>
 
 #include "update_engine/common/hash_calculator.h"
@@ -87,13 +90,19 @@
   uint64_t manifest_offset = 20;
   const int kProtobufSizeOffset = 12;
 
-  // Loads the payload.
-  brillo::Blob payload;
   DeltaArchiveManifest manifest;
   uint64_t metadata_size, major_version;
   uint32_t metadata_signature_size;
-  TEST_AND_RETURN_FALSE(PayloadSigner::LoadPayload(payload_path, &payload,
-      &manifest, &major_version, &metadata_size, &metadata_signature_size));
+  TEST_AND_RETURN_FALSE(
+      PayloadSigner::LoadPayloadMetadata(payload_path,
+                                         nullptr,
+                                         &manifest,
+                                         &major_version,
+                                         &metadata_size,
+                                         &metadata_signature_size));
+
+  brillo::Blob payload;
+  TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
 
   if (major_version == kBrilloMajorPayloadVersion) {
     // Write metadata signature size in header.
@@ -188,6 +197,8 @@
     // signature and payload signature.
     HashCalculator calc;
     TEST_AND_RETURN_FALSE(calc.Update(payload.data(), metadata_size));
+    TEST_AND_RETURN_FALSE(signatures_offset >=
+                          metadata_size + metadata_signature_size);
     TEST_AND_RETURN_FALSE(calc.Update(
         payload.data() + metadata_size + metadata_signature_size,
         signatures_offset - metadata_size - metadata_signature_size));
@@ -222,17 +233,25 @@
   }
 }
 
-bool PayloadSigner::LoadPayload(const string& payload_path,
-                                brillo::Blob* out_payload,
-                                DeltaArchiveManifest* out_manifest,
-                                uint64_t* out_major_version,
-                                uint64_t* out_metadata_size,
-                                uint32_t* out_metadata_signature_size) {
-  brillo::Blob payload;
-  TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
-  TEST_AND_RETURN_FALSE(payload.size() >=
-                        DeltaPerformer::kMaxPayloadHeaderSize);
-  const uint8_t* read_pointer = payload.data();
+bool PayloadSigner::LoadPayloadMetadata(const string& payload_path,
+                                        brillo::Blob* out_payload_metadata,
+                                        DeltaArchiveManifest* out_manifest,
+                                        uint64_t* out_major_version,
+                                        uint64_t* out_metadata_size,
+                                        uint32_t* out_metadata_signature_size) {
+  brillo::StreamPtr payload_file =
+      brillo::FileStream::Open(base::FilePath(payload_path),
+                               brillo::Stream::AccessMode::READ,
+                               brillo::FileStream::Disposition::OPEN_EXISTING,
+                               nullptr);
+  TEST_AND_RETURN_FALSE(payload_file);
+  brillo::Blob payload_metadata;
+
+  payload_metadata.resize(DeltaPerformer::kMaxPayloadHeaderSize);
+  TEST_AND_RETURN_FALSE(payload_file->ReadAllBlocking(
+      payload_metadata.data(), payload_metadata.size(), nullptr));
+
+  const uint8_t* read_pointer = payload_metadata.data();
   TEST_AND_RETURN_FALSE(
       memcmp(read_pointer, kDeltaMagic, sizeof(kDeltaMagic)) == 0);
   read_pointer += sizeof(kDeltaMagic);
@@ -261,23 +280,39 @@
   if (out_metadata_signature_size)
     *out_metadata_signature_size = metadata_signature_size;
 
-  *out_metadata_size = read_pointer - payload.data() + manifest_size;
-  TEST_AND_RETURN_FALSE(payload.size() >= *out_metadata_size);
-  if (out_manifest)
-    TEST_AND_RETURN_FALSE(
-        out_manifest->ParseFromArray(read_pointer, manifest_size));
-  *out_payload = std::move(payload);
+  uint64_t header_size = read_pointer - payload_metadata.data();
+  uint64_t metadata_size = header_size + manifest_size;
+  if (out_metadata_size)
+    *out_metadata_size = metadata_size;
+
+  size_t bytes_read = payload_metadata.size();
+  payload_metadata.resize(metadata_size);
+  TEST_AND_RETURN_FALSE(
+      payload_file->ReadAllBlocking(payload_metadata.data() + bytes_read,
+                                    payload_metadata.size() - bytes_read,
+                                    nullptr));
+  if (out_manifest) {
+    TEST_AND_RETURN_FALSE(out_manifest->ParseFromArray(
+        payload_metadata.data() + header_size, manifest_size));
+  }
+  if (out_payload_metadata)
+    *out_payload_metadata = std::move(payload_metadata);
   return true;
 }
 
 bool PayloadSigner::VerifySignedPayload(const string& payload_path,
                                         const string& public_key_path) {
-  brillo::Blob payload;
   DeltaArchiveManifest manifest;
   uint64_t metadata_size;
   uint32_t metadata_signature_size;
-  TEST_AND_RETURN_FALSE(LoadPayload(payload_path, &payload, &manifest, nullptr,
-      &metadata_size, &metadata_signature_size));
+  TEST_AND_RETURN_FALSE(LoadPayloadMetadata(payload_path,
+                                            nullptr,
+                                            &manifest,
+                                            nullptr,
+                                            &metadata_size,
+                                            &metadata_signature_size));
+  brillo::Blob payload;
+  TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
   TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
                         manifest.has_signatures_size());
   uint64_t signatures_offset = metadata_size + metadata_signature_size +
@@ -480,5 +515,38 @@
   return true;
 }
 
+bool PayloadSigner::ExtractPayloadProperties(
+    const string& payload_path, brillo::KeyValueStore* properties) {
+  DeltaArchiveManifest manifest;
+  brillo::Blob payload_metadata;
+  uint64_t major_version, metadata_size;
+  uint32_t metadata_signature_size;
+  uint64_t file_size = utils::FileSize(payload_path);
+
+  TEST_AND_RETURN_FALSE(
+      PayloadSigner::LoadPayloadMetadata(payload_path,
+                                         &payload_metadata,
+                                         &manifest,
+                                         &major_version,
+                                         &metadata_size,
+                                         &metadata_signature_size));
+
+  properties->SetString(kPayloadPropertyFileSize, std::to_string(file_size));
+  properties->SetString(kPayloadPropertyMetadataSize,
+                        std::to_string(metadata_size));
+
+  brillo::Blob file_hash, metadata_hash;
+  TEST_AND_RETURN_FALSE(
+      HashCalculator::RawHashOfFile(payload_path, file_size, &file_hash) ==
+      static_cast<off_t>(file_size));
+  TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfBytes(
+      payload_metadata.data(), payload_metadata.size(), &metadata_hash));
+
+  properties->SetString(kPayloadPropertyFileHash,
+                        brillo::data_encoding::Base64Encode(file_hash));
+  properties->SetString(kPayloadPropertyMetadataHash,
+                        brillo::data_encoding::Base64Encode(metadata_hash));
+  return true;
+}
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.h b/payload_generator/payload_signer.h
index e7351dd..00e32fa 100644
--- a/payload_generator/payload_signer.h
+++ b/payload_generator/payload_signer.h
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include <base/macros.h>
+#include <brillo/key_value_store.h>
 #include <brillo/secure_blob.h>
 
 #include "update_engine/update_metadata.pb.h"
@@ -32,18 +33,19 @@
 
 class PayloadSigner {
  public:
-  // Reads the payload from the given |payload_path| into the |out_payload|
-  // vector. It also parses the manifest protobuf in the payload and returns it
-  // in |out_manifest| if not null, along with the major version of the payload
-  // in |out_major_version| if not null, the size of the entire metadata in
-  // |out_metadata_size| and the size of metadata signature in
-  // |out_metadata_signature_size| if not null.
-  static bool LoadPayload(const std::string& payload_path,
-                          brillo::Blob* out_payload,
-                          DeltaArchiveManifest* out_manifest,
-                          uint64_t* out_major_version,
-                          uint64_t* out_metadata_size,
-                          uint32_t* out_metadata_signature_size);
+  // Reads the payload metadata from the given |payload_path| into the
+  // |out_payload_metadata| vector if not null. It also parses the manifest
+  // protobuf in the payload and returns it in |out_manifest| if not null, along
+  // with the major version of the payload in |out_major_version| if not null,
+  // the size of the entire metadata in |out_metadata_size| and the size of
+  // metadata signature in |out_metadata_signature_size| if not null. Returns
+  // whether a valid payload metadata was found and parsed.
+  static bool LoadPayloadMetadata(const std::string& payload_path,
+                                  brillo::Blob* out_payload_metadata,
+                                  DeltaArchiveManifest* out_manifest,
+                                  uint64_t* out_major_version,
+                                  uint64_t* out_metadata_size,
+                                  uint32_t* out_metadata_signature_size);
 
   // Returns true if the payload in |payload_path| is signed and its hash can be
   // verified using the public key in |public_key_path| with the signature
@@ -132,6 +134,9 @@
                                    const std::string& private_key_path,
                                    std::string* out_signature);
 
+  static bool ExtractPayloadProperties(const std::string& payload_path,
+                                       brillo::KeyValueStore* properties);
+
  private:
   // This should never be constructed
   DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadSigner);
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index b302f52..eadbc59 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -131,12 +131,16 @@
     uint64_t metadata_size;
     EXPECT_TRUE(
         payload.WritePayload(payload_path, "/dev/null", "", &metadata_size));
-    brillo::Blob payload_blob;
+    brillo::Blob payload_metadata_blob;
     DeltaArchiveManifest manifest;
     uint64_t load_metadata_size, load_major_version;
-    EXPECT_TRUE(PayloadSigner::LoadPayload(payload_path, &payload_blob,
-        &manifest, &load_major_version, &load_metadata_size, nullptr));
-    EXPECT_EQ(utils::FileSize(payload_path), payload_blob.size());
+    EXPECT_TRUE(PayloadSigner::LoadPayloadMetadata(payload_path,
+                                                   &payload_metadata_blob,
+                                                   &manifest,
+                                                   &load_major_version,
+                                                   &load_metadata_size,
+                                                   nullptr));
+    EXPECT_EQ(metadata_size, payload_metadata_blob.size());
     EXPECT_EQ(config.major_version, load_major_version);
     EXPECT_EQ(metadata_size, load_metadata_size);
   }