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);
}