update_engine: Split payload signing and verification.

Payloads are only signed on server-side code (delta_generator) and
verified on both sides and unittest. This removes the dependency of
payload_generator/ code from delta_performer.cc by spliting the
payload signing and verification in two files.

Currently, both files are still included on all the built files.

This patch also includes some minor linter fixes.

BUG=chromium:394184
TEST=FEATURES="test" emerge-link update_engine; sudo emerge update_engine

Change-Id: Ia4268257f4260902bc37612f429f44ba7e8f65fd
Reviewed-on: https://chromium-review.googlesource.com/208540
Tested-by: Alex Deymo <deymo@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 587892c..1367b17 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -39,8 +39,9 @@
 #include "update_engine/payload_generator/graph_types.h"
 #include "update_engine/payload_generator/graph_utils.h"
 #include "update_engine/payload_generator/metadata.h"
+#include "update_engine/payload_generator/payload_signer.h"
 #include "update_engine/payload_generator/topological_sort.h"
-#include "update_engine/payload_signer.h"
+#include "update_engine/payload_verifier.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/update_metadata.pb.h"
 #include "update_engine/utils.h"
@@ -60,7 +61,7 @@
 const uint64_t kFullUpdateChunkSize = 1024 * 1024;  // bytes
 
 const size_t kBlockSize = 4096;  // bytes
-const string kNonexistentPath = "";
+const char kEmptyPath[] = "";
 
 static const char* kInstallOperationTypes[] = {
   "REPLACE",
@@ -81,7 +82,7 @@
 const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
 
 // Needed for testing purposes, in case we can't use actual filesystem objects.
-// TODO(garnold)(chromium:331965) Replace this hack with a properly injected
+// TODO(garnold) (chromium:331965) Replace this hack with a properly injected
 // parameter in form of a mockable abstract class.
 bool (*get_extents_with_chunk_func)(const std::string&, off_t, off_t,
                                     std::vector<Extent>*) =
@@ -122,7 +123,7 @@
   vector<char> data;
   DeltaArchiveManifest_InstallOperation operation;
 
-  string old_path = (old_root == kNonexistentPath) ? kNonexistentPath :
+  string old_path = (old_root == kEmptyPath) ? kEmptyPath :
       old_root + path;
 
   // If bsdiff breaks again, blacklist the problem file by using:
@@ -243,7 +244,7 @@
                                           blocks,
                                           (should_diff_from_source ?
                                            old_root :
-                                           kNonexistentPath),
+                                           kEmptyPath),
                                           new_root,
                                           fs_iter.GetPartialPath(),
                                           offset,
@@ -470,7 +471,7 @@
                                          new_kernel_part,
                                          0,  // chunk_offset
                                          -1,  // chunk_size
-                                         true, // bsdiff_allowed
+                                         true,  // bsdiff_allowed
                                          &data,
                                          &op,
                                          false));
@@ -1038,19 +1039,20 @@
 
 class SortCutsByTopoOrderLess {
  public:
-  SortCutsByTopoOrderLess(vector<vector<Vertex::Index>::size_type>& table)
+  explicit SortCutsByTopoOrderLess(
+      const vector<vector<Vertex::Index>::size_type>& table)
       : table_(table) {}
   bool operator()(const CutEdgeVertexes& a, const CutEdgeVertexes& b) {
     return table_[a.old_dst] < table_[b.old_dst];
   }
  private:
-  vector<vector<Vertex::Index>::size_type>& table_;
+  const vector<vector<Vertex::Index>::size_type>& table_;
 };
 
 }  // namespace
 
 void DeltaDiffGenerator::GenerateReverseTopoOrderMap(
-    vector<Vertex::Index>& op_indexes,
+    const vector<Vertex::Index>& op_indexes,
     vector<vector<Vertex::Index>::size_type>* reverse_op_indexes) {
   vector<vector<Vertex::Index>::size_type> table(op_indexes.size());
   for (vector<Vertex::Index>::size_type i = 0, e = op_indexes.size();
@@ -1064,8 +1066,9 @@
   reverse_op_indexes->swap(table);
 }
 
-void DeltaDiffGenerator::SortCutsByTopoOrder(vector<Vertex::Index>& op_indexes,
-                                             vector<CutEdgeVertexes>* cuts) {
+void DeltaDiffGenerator::SortCutsByTopoOrder(
+    const vector<Vertex::Index>& op_indexes,
+    vector<CutEdgeVertexes>* cuts) {
   // first, make a reverse lookup table.
   vector<vector<Vertex::Index>::size_type> table;
   GenerateReverseTopoOrderMap(op_indexes, &table);
@@ -1438,7 +1441,7 @@
     TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
                                         cut.old_dst,
                                         NULL,
-                                        kNonexistentPath,
+                                        kEmptyPath,
                                         new_root,
                                         (*graph)[cut.old_dst].file_name,
                                         (*graph)[cut.old_dst].chunk_offset,
@@ -1563,7 +1566,7 @@
         << "Old and new images have different block counts.";
 
     // If new_image_info is present, old_image_info must be present.
-    TEST_AND_RETURN_FALSE((bool)old_image_info == (bool)new_image_info);
+    TEST_AND_RETURN_FALSE(!old_image_info == !new_image_info);
   } else {
     // old_image_info must not be present for a full update.
     TEST_AND_RETURN_FALSE(!old_image_info);
diff --git a/payload_generator/delta_diff_generator.h b/payload_generator/delta_diff_generator.h
index 596d9c0..3c4ad69 100644
--- a/payload_generator/delta_diff_generator.h
+++ b/payload_generator/delta_diff_generator.h
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
-#define CHROMEOS_PLATFORM_UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
 
+#include <set>
 #include <string>
 #include <vector>
 
@@ -167,8 +168,9 @@
 
   // Sorts the vector |cuts| by its |cuts[].old_dest| member. Order is
   // determined by the order of elements in op_indexes.
-  static void SortCutsByTopoOrder(std::vector<Vertex::Index>& op_indexes,
-                                  std::vector<CutEdgeVertexes>* cuts);
+  static void SortCutsByTopoOrder(
+      const std::vector<Vertex::Index>& op_indexes,
+      std::vector<CutEdgeVertexes>* cuts);
 
   // Returns true iff there are no extents in the graph that refer to temp
   // blocks. Temp blocks are in the range [kTempBlockStart, kSparseHole).
@@ -211,7 +213,7 @@
   // which the op is performed -> graph vertex index, and produces the
   // reverse: a mapping from graph vertex index -> op_indexes index.
   static void GenerateReverseTopoOrderMap(
-      std::vector<Vertex::Index>& op_indexes,
+      const std::vector<Vertex::Index>& op_indexes,
       std::vector<std::vector<Vertex::Index>::size_type>* reverse_op_indexes);
 
   // Takes a |graph|, which has edges that must be cut, as listed in
@@ -264,7 +266,7 @@
                              DeltaArchiveManifest* manifest);
 
  private:
- // This should never be constructed
+  // This should never be constructed.
   DISALLOW_IMPLICIT_CONSTRUCTORS(DeltaDiffGenerator);
 };
 
@@ -273,4 +275,4 @@
 
 };  // namespace chromeos_update_engine
 
-#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
diff --git a/payload_generator/delta_diff_generator_unittest.cc b/payload_generator/delta_diff_generator_unittest.cc
index c9ac8cf..977b34d 100644
--- a/payload_generator/delta_diff_generator_unittest.cc
+++ b/payload_generator/delta_diff_generator_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "update_engine/payload_generator/delta_diff_generator.h"
+
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -22,7 +24,6 @@
 #include "update_engine/extent_ranges.h"
 #include "update_engine/payload_constants.h"
 #include "update_engine/payload_generator/cycle_breaker.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/extent_mapper.h"
 #include "update_engine/payload_generator/graph_types.h"
 #include "update_engine/payload_generator/graph_utils.h"
@@ -91,7 +92,7 @@
                                                  new_path(),
                                                  0,  // chunk_offset
                                                  -1,  // chunk_size
-                                                 true, // bsdiff_allowed
+                                                 true,  // bsdiff_allowed
                                                  &data,
                                                  &op,
                                                  true));
@@ -181,7 +182,7 @@
                                                  new_path(),
                                                  0,  // chunk_offset
                                                  -1,  // chunk_size
-                                                 true, // bsdiff_allowed
+                                                 true,  // bsdiff_allowed
                                                  &data,
                                                  &op,
                                                  true));
@@ -245,7 +246,7 @@
                                                  new_path(),
                                                  0,  // chunk_offset
                                                  -1,  // chunk_size
-                                                 true, // bsdiff_allowed
+                                                 true,  // bsdiff_allowed
                                                  &data,
                                                  &op,
                                                  true));
@@ -278,7 +279,7 @@
                                                  new_path(),
                                                  0,  // chunk_offset
                                                  -1,  // chunk_size
-                                                 false, // bsdiff_allowed
+                                                 false,  // bsdiff_allowed
                                                  &data,
                                                  &op,
                                                  true));
@@ -304,7 +305,7 @@
                                                  new_path(),
                                                  0,  // chunk_offset
                                                  -1,  // chunk_size
-                                                 false, // bsdiff_allowed
+                                                 false,  // bsdiff_allowed
                                                  &data,
                                                  &op,
                                                  true));
@@ -331,7 +332,7 @@
                                                    new_path(),
                                                    0,  // chunk_offset
                                                    -1,  // chunk_size
-                                                   true, // bsdiff_allowed
+                                                   true,  // bsdiff_allowed
                                                    &data,
                                                    &op,
                                                    true));
@@ -365,7 +366,7 @@
                                                  new_path(),
                                                  0,  // chunk_offset
                                                  -1,  // chunk_size
-                                                 true, // bsdiff_allowed
+                                                 true,  // bsdiff_allowed
                                                  &data,
                                                  &op,
                                                  false));
@@ -398,7 +399,7 @@
   extent->set_start_block(start);
   extent->set_num_blocks(length);
 }
-}
+}  // namespace
 
 TEST_F(DeltaDiffGeneratorTest, SubstituteBlocksTest) {
   vector<Extent> remove_blocks;
@@ -498,8 +499,8 @@
   cycle_breaker.BreakCycles(graph, &cut_edges);
 
   EXPECT_EQ(1, cut_edges.size());
-  EXPECT_TRUE(cut_edges.end() != cut_edges.find(make_pair<Vertex::Index>(1,
-                                                                         0)));
+  EXPECT_TRUE(cut_edges.end() != cut_edges.find(
+      std::pair<Vertex::Index, Vertex::Index>(1, 0)));
 
   vector<CutEdgeVertexes> cuts;
   EXPECT_TRUE(DeltaDiffGenerator::CutEdges(&graph, cut_edges, &cuts));
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 89bb2b9..a2b6b3c 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -21,7 +21,8 @@
 
 #include "update_engine/delta_performer.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
-#include "update_engine/payload_signer.h"
+#include "update_engine/payload_generator/payload_signer.h"
+#include "update_engine/payload_verifier.h"
 #include "update_engine/prefs.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/terminator.h"
@@ -142,7 +143,6 @@
                     const string& build_channel,
                     const string& build_version,
                     ImageInfo* image_info) {
-
   // All of these arguments should be present or missing.
   bool empty = channel.empty();
 
@@ -237,8 +237,8 @@
       << "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,
-                                           FLAGS_public_key_version));
+  CHECK(PayloadVerifier::VerifySignedPayload(FLAGS_in_file, FLAGS_public_key,
+                                             FLAGS_public_key_version));
   LOG(INFO) << "Done verifying signed payload.";
 }
 
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
new file mode 100644
index 0000000..4bbb155
--- /dev/null
+++ b/payload_generator/payload_signer.cc
@@ -0,0 +1,331 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/payload_generator/payload_signer.h"
+
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <openssl/pem.h>
+
+#include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_verifier.h"
+#include "update_engine/subprocess.h"
+#include "update_engine/update_metadata.pb.h"
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Given raw |signatures|, packs them into a protobuf and serializes it into a
+// binary blob. Returns true on success, false otherwise.
+bool ConvertSignatureToProtobufBlob(const vector<vector<char> >& signatures,
+                                    vector<char>* out_signature_blob) {
+  // Pack it into a protobuf
+  Signatures out_message;
+  uint32_t version = kSignatureMessageOriginalVersion;
+  LOG_IF(WARNING, kSignatureMessageCurrentVersion -
+         kSignatureMessageOriginalVersion + 1 < signatures.size())
+      << "You may want to support clients in the range ["
+      << kSignatureMessageOriginalVersion << ", "
+      << kSignatureMessageCurrentVersion << "] inclusive, but you only "
+      << "provided " << signatures.size() << " signatures.";
+  for (vector<vector<char> >::const_iterator it = signatures.begin(),
+           e = signatures.end(); it != e; ++it) {
+    const vector<char>& signature = *it;
+    Signatures_Signature* sig_message = out_message.add_signatures();
+    sig_message->set_version(version++);
+    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. It populates |out_metadata_size| with the size of the final
+// manifest after adding the dummy signature operation, and
+// |out_signatures_offset| with the expected offset for the new blob. Returns
+// true on success, false otherwise.
+bool AddSignatureOpToPayload(const string& payload_path,
+                             uint64_t signature_blob_size,
+                             vector<char>* out_payload,
+                             uint64_t* out_metadata_size,
+                             uint64_t* out_signatures_offset) {
+  const int kProtobufOffset = 20;
+  const int kProtobufSizeOffset = 12;
+
+  // Loads the payload.
+  vector<char> payload;
+  DeltaArchiveManifest manifest;
+  uint64_t metadata_size;
+  TEST_AND_RETURN_FALSE(PayloadVerifier::LoadPayload(
+      payload_path, &payload, &manifest, &metadata_size));
+
+  // Is there already a signature op in place?
+  if (manifest.has_signatures_size()) {
+    // The signature op is tied to the size of the signature blob, but not it's
+    // contents. We don't allow the manifest to change if there is already an op
+    // present, because that might invalidate previously generated
+    // hashes/signatures.
+    if (manifest.signatures_size() != signature_blob_size) {
+      LOG(ERROR) << "Attempt to insert different signature sized blob. "
+                 << "(current:" << manifest.signatures_size()
+                 << "new:" << signature_blob_size << ")";
+      return false;
+    }
+
+    LOG(INFO) << "Matching signature sizes already present.";
+  } else {
+    // 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));
+    metadata_size = serialized_manifest.size() + kProtobufOffset;
+
+    LOG(INFO) << "Updated payload size: " << payload.size();
+    LOG(INFO) << "Updated metadata size: " << metadata_size;
+  }
+
+  out_payload->swap(payload);
+  *out_metadata_size = metadata_size;
+  *out_signatures_offset = metadata_size + manifest.signatures_offset();
+  LOG(INFO) << "Signature Blob Offset: " << *out_signatures_offset;
+  return true;
+}
+}  // namespace
+
+bool PayloadSigner::SignHash(const vector<char>& hash,
+                             const string& private_key_path,
+                             vector<char>* out_signature) {
+  LOG(INFO) << "Signing hash with private key: " << private_key_path;
+  string sig_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("signature.XXXXXX", &sig_path, NULL));
+  ScopedPathUnlinker sig_path_unlinker(sig_path);
+
+  string hash_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("hash.XXXXXX", &hash_path, NULL));
+  ScopedPathUnlinker hash_path_unlinker(hash_path);
+  // We expect unpadded SHA256 hash coming in
+  TEST_AND_RETURN_FALSE(hash.size() == 32);
+  vector<char> padded_hash(hash);
+  PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash);
+  TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(),
+                                         padded_hash.data(),
+                                         padded_hash.size()));
+
+  // This runs on the server, so it's okay to cop out and call openssl
+  // executable rather than properly use the library
+  vector<string> cmd;
+  base::SplitString("openssl rsautl -raw -sign -inkey x -in x -out x",
+                    ' ',
+                    &cmd);
+  cmd[cmd.size() - 5] = private_key_path;
+  cmd[cmd.size() - 3] = hash_path;
+  cmd[cmd.size() - 1] = sig_path;
+
+  // When running unittests, we need to use the openssl version from the
+  // SYSROOT instead of the one on the $PATH (host).
+  cmd[0] = utils::GetPathOnBoard("openssl");
+
+  int return_code = 0;
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code, NULL));
+  TEST_AND_RETURN_FALSE(return_code == 0);
+
+  vector<char> signature;
+  TEST_AND_RETURN_FALSE(utils::ReadFile(sig_path, &signature));
+  out_signature->swap(signature);
+  return true;
+}
+
+bool PayloadSigner::SignPayload(const string& unsigned_payload_path,
+                                const vector<string>& private_key_paths,
+                                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));
+
+  vector<vector<char> > signatures;
+  for (vector<string>::const_iterator it = private_key_paths.begin(),
+           e = private_key_paths.end(); it != e; ++it) {
+    vector<char> signature;
+    TEST_AND_RETURN_FALSE(SignHash(hash_data, *it, &signature));
+    signatures.push_back(signature);
+  }
+  TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
+                                                       out_signature_blob));
+  return true;
+}
+
+bool PayloadSigner::SignatureBlobLength(const vector<string>& private_key_paths,
+                                        uint64_t* out_length) {
+  DCHECK(out_length);
+
+  string x_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("signed_data.XXXXXX", &x_path, NULL));
+  ScopedPathUnlinker x_path_unlinker(x_path);
+  TEST_AND_RETURN_FALSE(utils::WriteFile(x_path.c_str(), "x", 1));
+
+  vector<char> sig_blob;
+  TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(x_path,
+                                                   private_key_paths,
+                                                   &sig_blob));
+  *out_length = sig_blob.size();
+  return true;
+}
+
+bool PayloadSigner::PrepPayloadForHashing(
+        const string& payload_path,
+        const vector<int>& signature_sizes,
+        vector<char>* payload_out,
+        uint64_t* metadata_size_out,
+        uint64_t* signatures_offset_out) {
+  // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory.
+
+  // Loads the payload and adds the signature op to it.
+  vector<vector<char> > signatures;
+  for (vector<int>::const_iterator it = signature_sizes.begin(),
+           e = signature_sizes.end(); it != e; ++it) {
+    vector<char> signature(*it, 0);
+    signatures.push_back(signature);
+  }
+  vector<char> signature_blob;
+  TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
+                                                       &signature_blob));
+  TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path,
+                                                signature_blob.size(),
+                                                payload_out,
+                                                metadata_size_out,
+                                                signatures_offset_out));
+
+  return true;
+}
+
+bool PayloadSigner::HashPayloadForSigning(const string& payload_path,
+                                          const vector<int>& signature_sizes,
+                                          vector<char>* out_hash_data) {
+  vector<char> payload;
+  uint64_t metadata_size;
+  uint64_t signatures_offset;
+
+  TEST_AND_RETURN_FALSE(PrepPayloadForHashing(payload_path,
+                                              signature_sizes,
+                                              &payload,
+                                              &metadata_size,
+                                              &signatures_offset));
+
+  // Calculates the hash on the updated payload. Note that we stop calculating
+  // before we reach the signature information.
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(&payload[0],
+                                                            signatures_offset,
+                                                            out_hash_data));
+  return true;
+}
+
+bool PayloadSigner::HashMetadataForSigning(const string& payload_path,
+                                           const vector<int>& signature_sizes,
+                                           vector<char>* out_metadata_hash) {
+  vector<char> payload;
+  uint64_t metadata_size;
+  uint64_t signatures_offset;
+
+  TEST_AND_RETURN_FALSE(PrepPayloadForHashing(payload_path,
+                                              signature_sizes,
+                                              &payload,
+                                              &metadata_size,
+                                              &signatures_offset));
+
+  // Calculates the hash on the manifest.
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(&payload[0],
+                                                            metadata_size,
+                                                            out_metadata_hash));
+  return true;
+}
+
+bool PayloadSigner::AddSignatureToPayload(
+    const string& payload_path,
+    const vector<vector<char> >& signatures,
+    const string& signed_payload_path,
+    uint64_t *out_metadata_size) {
+  // 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(signatures,
+                                                       &signature_blob));
+  vector<char> payload;
+  uint64_t signatures_offset;
+  TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path,
+                                                signature_blob.size(),
+                                                &payload,
+                                                out_metadata_size,
+                                                &signatures_offset));
+  // Appends the signature blob to the end of the payload and writes the new
+  // payload.
+  LOG(INFO) << "Payload size before signatures: " << payload.size();
+  payload.resize(signatures_offset);
+  payload.insert(payload.begin() + signatures_offset,
+                 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;
+}
+
+bool PayloadSigner::GetMetadataSignature(const char* const metadata,
+                                         size_t metadata_size,
+                                         const string& private_key_path,
+                                         string* out_signature) {
+  // 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.
+  vector<char> metadata_hash;
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(metadata,
+                                                            metadata_size,
+                                                            &metadata_hash));
+
+  vector<char> signature;
+  TEST_AND_RETURN_FALSE(SignHash(metadata_hash,
+                                 private_key_path,
+                                 &signature));
+
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::Base64Encode(&signature[0],
+                                                          signature.size(),
+                                                          out_signature));
+  return true;
+}
+
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.h b/payload_generator/payload_signer.h
new file mode 100644
index 0000000..0c540e6
--- /dev/null
+++ b/payload_generator/payload_signer.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_SIGNER_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_SIGNER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+#include "update_engine/update_metadata.pb.h"
+
+// This class encapsulates methods used for payload signing.
+// See update_metadata.proto for more info.
+
+namespace chromeos_update_engine {
+
+class PayloadSigner {
+ public:
+  // Given a raw |hash| and a private key in |private_key_path| calculates the
+  // raw signature in |out_signature|. Returns true on success, false otherwise.
+  static bool SignHash(const std::vector<char>& hash,
+                       const std::string& private_key_path,
+                       std::vector<char>* out_signature);
+
+  // Given an unsigned payload in |unsigned_payload_path| and private keys in
+  // |private_key_path|, calculates the signature blob into
+  // |out_signature_blob|. Note that the payload must already have an updated
+  // manifest that includes the dummy signature op. Returns true on success,
+  // false otherwise.
+  static bool SignPayload(const std::string& unsigned_payload_path,
+                          const std::vector<std::string>& private_key_paths,
+                          std::vector<char>* out_signature_blob);
+
+  // Returns the length of out_signature_blob that will result in a call
+  // to SignPayload with the given private keys. Returns true on success.
+  static bool SignatureBlobLength(
+      const std::vector<std::string>& private_key_paths,
+      uint64_t* out_length);
+
+  // This is a helper method for HashPayloadforSigning and
+  // HashMetadataForSigning. It loads the payload into memory, and inserts
+  // signature placeholders if Signatures aren't already present.
+  static bool PrepPayloadForHashing(
+        const std::string& payload_path,
+        const std::vector<int>& signature_sizes,
+        std::vector<char>* payload_out,
+        uint64_t* metadata_size_out,
+        uint64_t* signatures_offset_out);
+
+  // Given an unsigned payload in |payload_path|,
+  // this method does two things:
+  // 1. Uses PrepPayloadForHashing to inserts placeholder signature operations
+  //    to make the manifest match what the final signed payload will look
+  //    like based on |signatures_sizes|, if needed.
+  // 2. It calculates the raw SHA256 hash of the payload in |payload_path|
+  //    (except signatures) and returns the result in |out_hash_data|.
+  //
+  // The dummy signatures are not preserved or written to disk.
+  static bool HashPayloadForSigning(const std::string& payload_path,
+                                    const std::vector<int>& signature_sizes,
+                                    std::vector<char>* out_hash_data);
+
+  // Given an unsigned payload in |payload_path|,
+  // this method does two things:
+  // 1. Uses PrepPayloadForHashing to inserts placeholder signature operations
+  //    to make the manifest match what the final signed payload will look
+  //    like based on |signatures_sizes|, if needed.
+  // 2. It calculates the raw SHA256 hash of the metadata from the payload in
+  //    |payload_path| (except signatures) and returns the result in
+  //    |out_metadata_hash|.
+  //
+  // The dummy signatures are not preserved or written to disk.
+  static bool HashMetadataForSigning(const std::string& payload_path,
+                                     const std::vector<int>& signature_sizes,
+                                     std::vector<char>* out_metadata_hash);
+
+  // Given an unsigned payload in |payload_path| (with no dummy signature op)
+  // and the raw |signatures| updates the payload to include the signature thus
+  // turning it into a signed payload. The new payload is stored in
+  // |signed_payload_path|. |payload_path| and |signed_payload_path| can point
+  // to the same file. Populates |out_metadata_size| with the size of the
+  // metadata after adding the signature operation in the manifest.Returns true
+  // on success, false otherwise.
+  static bool AddSignatureToPayload(
+      const std::string& payload_path,
+      const std::vector<std::vector<char> >& signatures,
+      const std::string& signed_payload_path,
+      uint64_t* out_metadata_size);
+
+  // Computes the SHA256 hash of the first metadata_size bytes of |metadata|
+  // and signs the hash with the given private_key_path and writes the signed
+  // hash in |out_signature|. Returns true if successful or false if there was
+  // any error in the computations.
+  static bool GetMetadataSignature(const char* const metadata,
+                                   size_t metadata_size,
+                                   const std::string& private_key_path,
+                                   std::string* out_signature);
+
+ private:
+  // This should never be constructed
+  DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadSigner);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_SIGNER_H_
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
new file mode 100644
index 0000000..00df88c
--- /dev/null
+++ b/payload_generator/payload_signer_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/payload_generator/payload_signer.h"
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/payload_verifier.h"
+#include "update_engine/update_metadata.pb.h"
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+// Note: the test key was generated with the following command:
+// openssl genrsa -out unittest_key.pem 2048
+// The public-key version is created by the build system.
+
+namespace chromeos_update_engine {
+
+const char* kUnittestPrivateKeyPath = "unittest_key.pem";
+const char* kUnittestPublicKeyPath = "unittest_key.pub.pem";
+const char* kUnittestPrivateKey2Path = "unittest_key2.pem";
+const char* kUnittestPublicKey2Path = "unittest_key2.pub.pem";
+
+// Some data and its corresponding hash and signature:
+const char kDataToSign[] = "This is some data to sign.";
+
+// Generated by:
+// echo -n 'This is some data to sign.' | openssl dgst -sha256 -binary |
+//   hexdump -v -e '" " 8/1 "0x%02x, " "\n"'
+const unsigned char kDataHash[] = {
+  0x7a, 0x07, 0xa6, 0x44, 0x08, 0x86, 0x20, 0xa6,
+  0xc1, 0xf8, 0xd9, 0x02, 0x05, 0x63, 0x0d, 0xb7,
+  0xfc, 0x2b, 0xa0, 0xa9, 0x7c, 0x9d, 0x1d, 0x8c,
+  0x01, 0xf5, 0x78, 0x6d, 0xc5, 0x11, 0xb4, 0x06
+};
+
+// Generated with openssl 1.0, which at the time of this writing, you need
+// to download and install yourself. Here's my command:
+// echo -n 'This is some data to sign.' | openssl dgst -sha256 -binary |
+//    ~/local/bin/openssl pkeyutl -sign -inkey unittest_key.pem -pkeyopt
+//    digest:sha256 | hexdump -v -e '" " 8/1 "0x%02x, " "\n"'
+const unsigned char kDataSignature[] = {
+  0x9f, 0x86, 0x25, 0x8b, 0xf3, 0xcc, 0xe3, 0x95,
+  0x5f, 0x45, 0x83, 0xb2, 0x66, 0xf0, 0x2a, 0xcf,
+  0xb7, 0xaa, 0x52, 0x25, 0x7a, 0xdd, 0x9d, 0x65,
+  0xe5, 0xd6, 0x02, 0x4b, 0x37, 0x99, 0x53, 0x06,
+  0xc2, 0xc9, 0x37, 0x36, 0x25, 0x62, 0x09, 0x4f,
+  0x6b, 0x22, 0xf8, 0xb3, 0x89, 0x14, 0x98, 0x1a,
+  0xbc, 0x30, 0x90, 0x4a, 0x43, 0xf5, 0xea, 0x2e,
+  0xf0, 0xa4, 0xba, 0xc3, 0xa7, 0xa3, 0x44, 0x70,
+  0xd6, 0xc4, 0x89, 0xd8, 0x45, 0x71, 0xbb, 0xee,
+  0x59, 0x87, 0x3d, 0xd5, 0xe5, 0x40, 0x22, 0x3d,
+  0x73, 0x7e, 0x2a, 0x58, 0x93, 0x8e, 0xcb, 0x9c,
+  0xf2, 0xbb, 0x4a, 0xc9, 0xd2, 0x2c, 0x52, 0x42,
+  0xb0, 0xd1, 0x13, 0x22, 0xa4, 0x78, 0xc7, 0xc6,
+  0x3e, 0xf1, 0xdc, 0x4c, 0x7b, 0x2d, 0x40, 0xda,
+  0x58, 0xac, 0x4a, 0x11, 0x96, 0x3d, 0xa0, 0x01,
+  0xf6, 0x96, 0x74, 0xf6, 0x6c, 0x0c, 0x49, 0x69,
+  0x4e, 0xc1, 0x7e, 0x9f, 0x2a, 0x42, 0xdd, 0x15,
+  0x6b, 0x37, 0x2e, 0x3a, 0xa7, 0xa7, 0x6d, 0x91,
+  0x13, 0xe8, 0x59, 0xde, 0xfe, 0x99, 0x07, 0xd9,
+  0x34, 0x0f, 0x17, 0xb3, 0x05, 0x4c, 0xd2, 0xc6,
+  0x82, 0xb7, 0x38, 0x36, 0x63, 0x1d, 0x9e, 0x21,
+  0xa6, 0x32, 0xef, 0xf1, 0x65, 0xe6, 0xed, 0x95,
+  0x25, 0x9b, 0x61, 0xe0, 0xba, 0x86, 0xa1, 0x7f,
+  0xf8, 0xa5, 0x4a, 0x32, 0x1f, 0x15, 0x20, 0x8a,
+  0x41, 0xc5, 0xb0, 0xd9, 0x4a, 0xda, 0x85, 0xf3,
+  0xdc, 0xa0, 0x98, 0x5d, 0x1d, 0x18, 0x9d, 0x2e,
+  0x42, 0xea, 0x69, 0x13, 0x74, 0x3c, 0x74, 0xf7,
+  0x6d, 0x43, 0xb0, 0x63, 0x90, 0xdb, 0x04, 0xd5,
+  0x05, 0xc9, 0x73, 0x1f, 0x6c, 0xd6, 0xfa, 0x46,
+  0x4e, 0x0f, 0x33, 0x58, 0x5b, 0x0d, 0x1b, 0x55,
+  0x39, 0xb9, 0x0f, 0x43, 0x37, 0xc0, 0x06, 0x0c,
+  0x29, 0x93, 0x43, 0xc7, 0x43, 0xb9, 0xab, 0x7d
+};
+
+namespace {
+void SignSampleData(vector<char>* out_signature_blob) {
+  string data_path;
+  ASSERT_TRUE(
+      utils::MakeTempFile("data.XXXXXX", &data_path, NULL));
+  ScopedPathUnlinker data_path_unlinker(data_path);
+  ASSERT_TRUE(utils::WriteFile(data_path.c_str(),
+                               kDataToSign,
+                               strlen(kDataToSign)));
+  uint64_t length = 0;
+  EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
+      vector<string> (1, kUnittestPrivateKeyPath),
+      &length));
+  EXPECT_GT(length, 0);
+  EXPECT_TRUE(PayloadSigner::SignPayload(
+      data_path,
+      vector<string>(1, kUnittestPrivateKeyPath),
+      out_signature_blob));
+  EXPECT_EQ(length, out_signature_blob->size());
+}
+}  // namespace
+
+TEST(PayloadSignerTest, SimpleTest) {
+  vector<char> signature_blob;
+  SignSampleData(&signature_blob);
+
+  // Check the signature itself
+  Signatures signatures;
+  EXPECT_TRUE(signatures.ParseFromArray(&signature_blob[0],
+                                        signature_blob.size()));
+  EXPECT_EQ(1, signatures.signatures_size());
+  const Signatures_Signature& signature = signatures.signatures(0);
+  EXPECT_EQ(kSignatureMessageOriginalVersion, signature.version());
+  const string sig_data = signature.data();
+  ASSERT_EQ(arraysize(kDataSignature), sig_data.size());
+  for (size_t i = 0; i < arraysize(kDataSignature); i++) {
+    EXPECT_EQ(static_cast<char>(kDataSignature[i]), sig_data[i]);
+  }
+}
+
+TEST(PayloadSignerTest, VerifySignatureTest) {
+  vector<char> signature_blob;
+  SignSampleData(&signature_blob);
+
+  vector<char> hash_data;
+  EXPECT_TRUE(PayloadVerifier::VerifySignature(signature_blob,
+                                             kUnittestPublicKeyPath,
+                                             &hash_data));
+  vector<char> padded_hash_data(reinterpret_cast<const char *>(kDataHash),
+                                reinterpret_cast<const char *>(kDataHash +
+                                                         sizeof(kDataHash)));
+  PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash_data);
+  ASSERT_EQ(padded_hash_data.size(), hash_data.size());
+  for (size_t i = 0; i < padded_hash_data.size(); i++) {
+    EXPECT_EQ(padded_hash_data[i], hash_data[i]);
+  }
+}
+
+}  // namespace chromeos_update_engine