Index: payload_signer.cc |
diff --git a/payload_signer.cc b/payload_signer.cc |
index 40bfcfcdaf1da40aa3efc1eb6833b256be3e8768..cb113dae7cfbbe97d3bcd68abfb8601964ddd52c 100644 |
--- a/payload_signer.cc |
+++ b/payload_signer.cc |
@@ -8,6 +8,8 @@ |
#include <base/string_util.h> |
#include <openssl/pem.h> |
+#include "update_engine/delta_diff_generator.h" |
+#include "update_engine/delta_performer.h" |
#include "update_engine/omaha_hash_calculator.h" |
#include "update_engine/subprocess.h" |
#include "update_engine/update_metadata.pb.h" |
@@ -20,9 +22,76 @@ namespace chromeos_update_engine { |
const uint32_t kSignatureMessageVersion = 1; |
-bool PayloadSigner::SignPayload(const string& unsigned_payload_path, |
- const string& private_key_path, |
- vector<char>* out_signature_blob) { |
+namespace { |
+// Given a raw |signature|, packs it into a protobuf and serializes it into a |
+// binary blob. Returns true on success, false otherwise. |
+bool ConvertSignatureToProtobufBlob(const vector<char> signature, |
+ vector<char>* out_signature_blob) { |
+ // Pack it into a protobuf |
+ Signatures out_message; |
+ Signatures_Signature* sig_message = out_message.add_signatures(); |
+ sig_message->set_version(kSignatureMessageVersion); |
+ sig_message->set_data(signature.data(), signature.size()); |
+ |
+ // Serialize protobuf |
+ string serialized; |
+ TEST_AND_RETURN_FALSE(out_message.AppendToString(&serialized)); |
+ out_signature_blob->insert(out_signature_blob->end(), |
+ serialized.begin(), |
+ serialized.end()); |
+ LOG(INFO) << "Signature blob size: " << out_signature_blob->size(); |
+ return true; |
+} |
+ |
+// Given an unsigned payload under |payload_path| and the |signature_blob_size| |
+// generates an updated payload that includes a dummy signature op in its |
+// manifest. Returns true on success, false otherwise. |
+bool AddSignatureOpToPayload(const std::string& payload_path, |
+ int signature_blob_size, |
+ vector<char>* out_payload) { |
+ const int kProtobufOffset = 20; |
+ const int kProtobufSizeOffset = 12; |
+ |
+ vector<char> payload; |
+ // Loads the payload and parses the manifest. |
+ TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); |
+ LOG(INFO) << "Original payload size: " << payload.size(); |
+ uint64_t metadata_size; |
+ DeltaArchiveManifest manifest; |
+ TEST_AND_RETURN_FALSE(DeltaPerformer::ParsePayloadMetadata( |
+ payload, &manifest, &metadata_size) == |
+ DeltaPerformer::kMetadataParseSuccess); |
+ LOG(INFO) << "Metadata size: " << metadata_size; |
+ TEST_AND_RETURN_FALSE(!manifest.has_signatures_offset() && |
+ !manifest.has_signatures_size()); |
+ |
+ // Updates the manifest to include the signature operation. |
+ DeltaDiffGenerator::AddSignatureOp(payload.size() - metadata_size, |
+ signature_blob_size, |
+ &manifest); |
+ |
+ // Updates the payload to include the new manifest. |
+ string serialized_manifest; |
+ TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest)); |
+ LOG(INFO) << "Updated protobuf size: " << serialized_manifest.size(); |
+ payload.erase(payload.begin() + kProtobufOffset, |
+ payload.begin() + metadata_size); |
+ payload.insert(payload.begin() + kProtobufOffset, |
+ serialized_manifest.begin(), |
+ serialized_manifest.end()); |
+ |
+ // Updates the protobuf size. |
+ uint64_t size_be = htobe64(serialized_manifest.size()); |
+ memcpy(&payload[kProtobufSizeOffset], &size_be, sizeof(size_be)); |
+ LOG(INFO) << "Updated payload size: " << payload.size(); |
+ out_payload->swap(payload); |
+ return true; |
+} |
+} // namespace {} |
+ |
+bool PayloadSigner::SignHash(const vector<char>& hash, |
+ const string& private_key_path, |
+ vector<char>* out_signature) { |
string sig_path; |
TEST_AND_RETURN_FALSE( |
utils::MakeTempFile("/tmp/signature.XXXXXX", &sig_path, NULL)); |
@@ -32,18 +101,9 @@ bool PayloadSigner::SignPayload(const string& unsigned_payload_path, |
TEST_AND_RETURN_FALSE( |
utils::MakeTempFile("/tmp/hash.XXXXXX", &hash_path, NULL)); |
ScopedPathUnlinker hash_path_unlinker(hash_path); |
- |
- vector<char> hash_data; |
- { |
- vector<char> payload; |
- // TODO(adlr): Read file in chunks. Not urgent as this runs on the server. |
- TEST_AND_RETURN_FALSE(utils::ReadFile(unsigned_payload_path, &payload)); |
- TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfData(payload, |
- &hash_data)); |
- } |
TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(), |
- &hash_data[0], |
- hash_data.size())); |
+ hash.data(), |
+ hash.size())); |
// This runs on the server, so it's okay to cop out and call openssl |
// executable rather than properly use the library |
@@ -61,19 +121,22 @@ bool PayloadSigner::SignPayload(const string& unsigned_payload_path, |
vector<char> signature; |
TEST_AND_RETURN_FALSE(utils::ReadFile(sig_path, &signature)); |
+ out_signature->swap(signature); |
+ return true; |
+} |
- // Pack it into a protobuf |
- Signatures out_message; |
- Signatures_Signature* sig_message = out_message.add_signatures(); |
- sig_message->set_version(kSignatureMessageVersion); |
- sig_message->set_data(signature.data(), signature.size()); |
+bool PayloadSigner::SignPayload(const string& unsigned_payload_path, |
+ const string& private_key_path, |
+ vector<char>* out_signature_blob) { |
+ vector<char> hash_data; |
+ TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfFile( |
+ unsigned_payload_path, -1, &hash_data) == |
+ utils::FileSize(unsigned_payload_path)); |
- // Serialize protobuf |
- string serialized; |
- TEST_AND_RETURN_FALSE(out_message.AppendToString(&serialized)); |
- out_signature_blob->insert(out_signature_blob->end(), |
- serialized.begin(), |
- serialized.end()); |
+ vector<char> signature; |
+ TEST_AND_RETURN_FALSE(SignHash(hash_data, private_key_path, &signature)); |
+ TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signature, |
+ out_signature_blob)); |
return true; |
} |
@@ -154,4 +217,48 @@ bool PayloadSigner::VerifySignature(const std::vector<char>& signature_blob, |
return true; |
} |
+bool PayloadSigner::HashPayloadForSigning(const std::string& payload_path, |
+ int signature_size, |
+ vector<char>* out_hash_data) { |
+ // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory. |
+ |
+ // Loads the payload and adds the signature op to it. |
+ vector<char> signature(signature_size, 0); |
+ vector<char> signature_blob; |
+ TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signature, |
+ &signature_blob)); |
+ vector<char> payload; |
+ TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path, |
+ signature_blob.size(), |
+ &payload)); |
+ // Calculates the hash on the updated payload. Note that the payload includes |
+ // the signature op but doesn't include the signature blob at the end. |
+ TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfData(payload, |
+ out_hash_data)); |
+ return true; |
+} |
+ |
+bool PayloadSigner::AddSignatureToPayload(const string& payload_path, |
+ const vector<char>& signature, |
+ const string& signed_payload_path) { |
+ // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory. |
+ |
+ // Loads the payload and adds the signature op to it. |
+ vector<char> signature_blob; |
+ TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signature, |
+ &signature_blob)); |
+ vector<char> payload; |
+ TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path, |
+ signature_blob.size(), |
+ &payload)); |
+ // Appends the signature blob to the end of the payload and writes the new |
+ // payload. |
+ payload.insert(payload.end(), signature_blob.begin(), signature_blob.end()); |
+ LOG(INFO) << "Signed payload size: " << payload.size(); |
+ TEST_AND_RETURN_FALSE(utils::WriteFile(signed_payload_path.c_str(), |
+ payload.data(), |
+ payload.size())); |
+ return true; |
+} |
+ |
} // namespace chromeos_update_engine |