AU: Support signed payload verification through the delta generator.

The following command will verify a signed payload:

./delta_generator -in_file <payload> -public_key <key.pem>

Add a unit test for signature verification.

BUG=chromium-os:10936
TEST=unit tests

Change-Id: I3bb464608c22229141f9819b27503b6de778c495

Review URL: http://codereview.chromium.org/6271003
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 0d71a6e..bd13512 100755
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -238,6 +238,8 @@
     ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(delta_path,
                                                      signature,
                                                      delta_path));
+    EXPECT_TRUE(PayloadSigner::VerifySignedPayload(delta_path,
+                                                   kUnittestPublicKeyPath));
   }
 
   // Read delta into memory.
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 9fc7fd7..0f791e0 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -40,6 +40,7 @@
 DEFINE_string(out_file, "", "Path to output delta payload file");
 DEFINE_string(out_hash_file, "", "Path to output hash file");
 DEFINE_string(private_key, "", "Path to private key in .pem format");
+DEFINE_string(public_key, "", "Path to public key in .pem format");
 DEFINE_string(prefs_dir, "/tmp/update_engine_prefs",
               "Preferences directory, used with apply_delta");
 DEFINE_int32(signature_size, 0, "Raw signature size used for hash calculation");
@@ -94,6 +95,16 @@
   LOG(INFO) << "Done signing payload.";
 }
 
+void VerifySignedPayload() {
+  LOG(INFO) << "Verifying signed payload.";
+  LOG_IF(FATAL, FLAGS_in_file.empty())
+      << "Must pass --in_file to verify signed payload.";
+  LOG_IF(FATAL, FLAGS_public_key.empty())
+      << "Must pass --public_key to verify signed payload.";
+  CHECK(PayloadSigner::VerifySignedPayload(FLAGS_in_file, FLAGS_public_key));
+  LOG(INFO) << "Done verifying signed payload.";
+}
+
 void ApplyDelta() {
   LOG(INFO) << "Applying delta.";
   LOG_IF(FATAL, FLAGS_old_image.empty())
@@ -154,6 +165,10 @@
     SignPayload();
     return 0;
   }
+  if (!FLAGS_public_key.empty()) {
+    VerifySignedPayload();
+    return 0;
+  }
   if (!FLAGS_in_file.empty()) {
     ApplyDelta();
     return 0;
diff --git a/omaha_hash_calculator.cc b/omaha_hash_calculator.cc
index cf583a9..de70d10 100644
--- a/omaha_hash_calculator.cc
+++ b/omaha_hash_calculator.cc
@@ -105,21 +105,23 @@
   return Base64Encode(&raw_hash_[0], raw_hash_.size(), &hash_);;
 }
 
-bool OmahaHashCalculator::RawHashOfData(const vector<char>& data,
-                                        vector<char>* out_hash) {
+bool OmahaHashCalculator::RawHashOfBytes(const char* data,
+                                         size_t length,
+                                         vector<char>* out_hash) {
   OmahaHashCalculator calc;
-  calc.Update(&data[0], data.size());
-
-  out_hash->resize(out_hash->size() + SHA256_DIGEST_LENGTH);
-  TEST_AND_RETURN_FALSE(
-      SHA256_Final(reinterpret_cast<unsigned char*>(&(*(out_hash->end() -
-                                                        SHA256_DIGEST_LENGTH))),
-                   &calc.ctx_) == 1);
+  TEST_AND_RETURN_FALSE(calc.Update(data, length));
+  TEST_AND_RETURN_FALSE(calc.Finalize());
+  *out_hash = calc.raw_hash();
   return true;
 }
 
-off_t OmahaHashCalculator::RawHashOfFile(const std::string& name, off_t length,
-                                         std::vector<char>* out_hash) {
+bool OmahaHashCalculator::RawHashOfData(const vector<char>& data,
+                                        vector<char>* out_hash) {
+  return RawHashOfBytes(data.data(), data.size(), out_hash);
+}
+
+off_t OmahaHashCalculator::RawHashOfFile(const string& name, off_t length,
+                                         vector<char>* out_hash) {
   OmahaHashCalculator calc;
   off_t res = calc.UpdateFile(name, length);
   if (res < 0) {
diff --git a/omaha_hash_calculator.h b/omaha_hash_calculator.h
index 762b8d4..9735ac8 100644
--- a/omaha_hash_calculator.h
+++ b/omaha_hash_calculator.h
@@ -60,6 +60,9 @@
   // success, and false otherwise.
   bool SetContext(const std::string& context);
 
+  static bool RawHashOfBytes(const char* data,
+                             size_t length,
+                             std::vector<char>* out_hash);
   static bool RawHashOfData(const std::vector<char>& data,
                             std::vector<char>* out_hash);
   static off_t RawHashOfFile(const std::string& name, off_t length,
diff --git a/payload_signer.cc b/payload_signer.cc
index cb113da..60a80d1 100644
--- a/payload_signer.cc
+++ b/payload_signer.cc
@@ -43,25 +43,37 @@
   return true;
 }
 
+bool LoadPayload(const string& payload_path,
+                 vector<char>* out_payload,
+                 DeltaArchiveManifest* out_manifest,
+                 uint64_t* out_metadata_size) {
+  vector<char> payload;
+  // Loads the payload and parses the manifest.
+  TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
+  LOG(INFO) << "Payload size: " << payload.size();
+  TEST_AND_RETURN_FALSE(DeltaPerformer::ParsePayloadMetadata(
+      payload, out_manifest, out_metadata_size) ==
+                        DeltaPerformer::kMetadataParseSuccess);
+  LOG(INFO) << "Metadata size: " << *out_metadata_size;
+  out_payload->swap(payload);
+  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,
+bool AddSignatureOpToPayload(const string& payload_path,
                              int signature_blob_size,
                              vector<char>* out_payload) {
   const int kProtobufOffset = 20;
   const int kProtobufSizeOffset = 12;
 
+  // Loads the payload.
   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;
+  uint64_t metadata_size;
+  TEST_AND_RETURN_FALSE(LoadPayload(
+      payload_path, &payload, &manifest, &metadata_size));
   TEST_AND_RETURN_FALSE(!manifest.has_signatures_offset() &&
                         !manifest.has_signatures_size());
 
@@ -217,6 +229,32 @@
   return true;
 }
 
+bool PayloadSigner::VerifySignedPayload(const std::string& payload_path,
+                                        const std::string& public_key_path) {
+  vector<char> payload;
+  DeltaArchiveManifest manifest;
+  uint64_t metadata_size;
+  TEST_AND_RETURN_FALSE(LoadPayload(
+      payload_path, &payload, &manifest, &metadata_size));
+  TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
+                        manifest.has_signatures_size());
+  CHECK_EQ(payload.size(),
+           metadata_size + manifest.signatures_offset() +
+           manifest.signatures_size());
+  vector<char> signature_blob(
+      payload.begin() + metadata_size + manifest.signatures_offset(),
+      payload.end());
+  vector<char> signed_hash;
+  TEST_AND_RETURN_FALSE(VerifySignature(
+      signature_blob, public_key_path, &signed_hash));
+  TEST_AND_RETURN_FALSE(!signed_hash.empty());
+  vector<char> hash;
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(
+      payload.data(), metadata_size + manifest.signatures_offset(), &hash));
+  TEST_AND_RETURN_FALSE(hash == signed_hash);
+  return true;
+}
+
 bool PayloadSigner::HashPayloadForSigning(const std::string& payload_path,
                                           int signature_size,
                                           vector<char>* out_hash_data) {
diff --git a/payload_signer.h b/payload_signer.h
index 5ebba19..eb0663d 100644
--- a/payload_signer.h
+++ b/payload_signer.h
@@ -39,13 +39,6 @@
   static bool SignatureBlobLength(const std::string& private_key_path,
                                   uint64_t* out_length);
 
-  // Returns false if the payload signature can't be verified. Returns true
-  // otherwise and sets |out_hash| to the signed payload hash.
-  static bool VerifySignature(const std::vector<char>& signature_blob,
-                              const std::string& public_key_path,
-                              std::vector<char>* out_hash_data);
-
-
   // Given an unsigned payload in |payload_path| (with no dummy signature op)
   // and the raw |signature_size| calculates the raw hash that needs to be
   // signed in |out_hash_data|. Returns true on success, false otherwise.
@@ -62,6 +55,18 @@
                                     const std::vector<char>& signature,
                                     const std::string& signed_payload_path);
 
+  // Returns false if the payload signature can't be verified. Returns true
+  // otherwise and sets |out_hash| to the signed payload hash.
+  static bool VerifySignature(const std::vector<char>& signature_blob,
+                              const std::string& public_key_path,
+                              std::vector<char>* out_hash_data);
+
+  // Returns true if the payload in |payload_path| is signed and its hash can be
+  // verified using the public key in |public_key_path|. Returns false
+  // otherwise.
+  static bool VerifySignedPayload(const std::string& payload_path,
+                                  const std::string& public_key_path);
+
  private:
   // This should never be constructed
   DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadSigner);