AU: Verify delta payload signature and signed hash.

The signature and hash are verified only if the public key file exists.
This means that currently this feature is desabled until we install a public
key.

BUG=5663
TEST=unit tests, applied a signed delta update on the server

Change-Id: I5be72f7fde88400587f8aae0c7d5745c79fc4428

Review URL: http://codereview.chromium.org/3592008
diff --git a/.gitignore b/.gitignore
index 9764321..946abbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
 /marshal.glibmarshal.c
 /marshal.glibmarshal.h
 /test_http_server
+/unittest_key.pub.pem
 /update_engine
 /update_engine.dbusclient.h
 /update_engine.dbusserver.h
diff --git a/SConstruct b/SConstruct
index 585380a..c42b528 100644
--- a/SConstruct
+++ b/SConstruct
@@ -119,6 +119,44 @@
                                single_source = 1,
                                suffix = 'glibmarshal.c')
 
+# Public key generation
+def PublicKeyEmitter(target, source, env):
+  """ Inputs:
+          target: list of targets to compile to
+          source: list of sources to compile
+          env: the scons environment in which we are compiling
+      Outputs:
+          target: the list of targets we'll emit
+          source: the list of sources we'll compile"""
+  targets = []
+  for source_file in source:
+    output = str(source_file)
+    output = output[0:output.rfind('.pem')]
+    output += '.pub.pem'
+    targets.append(output)
+  return targets, source
+
+def PublicKeyGenerator(source, target, env, for_signature):
+  """ Inputs:
+          source: list of sources to process
+          target: list of targets to generate
+          env: scons environment in which we are working
+          for_signature: unused
+      Outputs: a list of commands to execute to generate the targets from
+               the sources."""
+  commands = []
+  for source_file in source:
+    output = str(source_file)
+    output = output[0:output.rfind('.pem')]
+    output += '.pub.pem'
+    cmd = '/usr/bin/openssl rsa -in %s -pubout -out %s' % (source_file, output)
+    commands.append(cmd)
+  return commands
+
+public_key_builder = Builder(generator = PublicKeyGenerator,
+                             emitter = PublicKeyEmitter,
+                             suffix = '.pub.pem')
+
 env = Environment()
 for key in Split('CC CXX AR RANLIB LD NM'):
   value = os.environ.get(key)
@@ -174,6 +212,7 @@
 env['BUILDERS']['ProtocolBuffer'] = proto_builder
 env['BUILDERS']['DbusBindings'] = dbus_bindings_builder
 env['BUILDERS']['GlibMarshal'] = glib_marshal_builder
+env['BUILDERS']['PublicKey'] = public_key_builder
 
 # Fix issue with scons not passing pkg-config vars through the environment.
 for key in Split('PKG_CONFIG_LIBDIR PKG_CONFIG_PATH'):
@@ -183,6 +222,7 @@
 env.ParseConfig('pkg-config --cflags --libs '
                 'dbus-1 dbus-glib-1 gio-2.0 gio-unix-2.0 glib-2.0')
 env.ProtocolBuffer('update_metadata.pb.cc', 'update_metadata.proto')
+env.PublicKey('unittest_key.pub.pem', 'unittest_key.pem')
 
 env.DbusBindings('update_engine.dbusclient.h', 'update_engine.xml')
 
@@ -285,6 +325,8 @@
 all_sources.extend(client_main)
 all_sources.extend(delta_generator_main)
 for source in all_sources:
+  if source.endswith('_unittest.cc'):
+    env.Depends(source, 'unittest_key.pub.pem')
   if source.endswith('.glibmarshal.c') or source.endswith('.pb.cc'):
     continue
   env.Depends(source, 'update_metadata.pb.cc')
diff --git a/action_processor.h b/action_processor.h
index 98fc853..b2c1392 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -35,6 +35,7 @@
   kActionCodeDownloadTransferError = 9,
   kActionCodeDownloadHashMismatchError = 10,
   kActionCodeDownloadSizeMismatchError = 11,
+  kActionCodeDownloadPayloadVerificationError = 12,
 };
 
 class AbstractAction;
diff --git a/delta_performer.cc b/delta_performer.cc
index 04273f3..2436797 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -3,21 +3,24 @@
 // found in the LICENSE file.
 
 #include "update_engine/delta_performer.h"
+
 #include <endian.h>
 #include <errno.h>
+
 #include <algorithm>
 #include <cstring>
 #include <string>
 #include <vector>
 
+#include <base/scoped_ptr.h>
+#include <base/string_util.h>
 #include <google/protobuf/repeated_field.h>
 
-#include "base/scoped_ptr.h"
-#include "base/string_util.h"
 #include "update_engine/bzip_extent_writer.h"
 #include "update_engine/delta_diff_generator.h"
 #include "update_engine/extent_writer.h"
 #include "update_engine/graph_types.h"
+#include "update_engine/payload_signer.h"
 #include "update_engine/subprocess.h"
 
 using std::min;
@@ -31,11 +34,8 @@
 
 const int kDeltaVersionLength = 8;
 const int kDeltaProtobufLengthLength = 8;
-
-// Remove count bytes from the beginning of *buffer.
-void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) {
-  buffer->erase(buffer->begin(), buffer->begin() + count);
-}
+const char kUpdatePayloadPublicKeyPath[] =
+    "/usr/share/update_engine/update-payload-key.pub.pem";
 
 // Converts extents to a human-readable string, for use by DumpUpdateProto().
 string ExtentsToString(const RepeatedPtrField<Extent>& extents) {
@@ -136,6 +136,7 @@
     err = errno;
     PLOG(ERROR) << "Unable to close rootfs fd:";
   }
+  LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash.";
   fd_ = -2;  // Set so that isn't not valid AND calls to Open() will fail.
   path_ = "";
   return -err;
@@ -176,10 +177,10 @@
     }
     // Remove protobuf and header info from buffer_, so buffer_ contains
     // just data blobs
-    RemoveBufferHeadBytes(&buffer_,
-                          strlen(kDeltaMagic) +
-                          kDeltaVersionLength +
-                          kDeltaProtobufLengthLength + protobuf_length);
+    DiscardBufferHeadBytes(strlen(kDeltaMagic) +
+                           kDeltaVersionLength +
+                           kDeltaProtobufLengthLength + protobuf_length,
+                           true);  // do_hash
     manifest_valid_ = true;
     block_size_ = manifest_.block_size();
   }
@@ -239,7 +240,7 @@
     LOG(ERROR) << "we threw away data it seems?";
     return false;
   }
-  
+
   return (operation.data_offset() + operation.data_length()) <=
       (buffer_offset_ + buffer_.size());
 }
@@ -256,11 +257,14 @@
   // the data we need should be exactly at the beginning of the buffer.
   CHECK_EQ(buffer_offset_, operation.data_offset());
   CHECK_GE(buffer_.size(), operation.data_length());
-  
+
+  // Don't include the signature data blob in the hash.
+  bool do_hash = !ExtractSignatureMessage(operation);
+
   DirectExtentWriter direct_writer;
   ZeroPadExtentWriter zero_pad_writer(&direct_writer);
   scoped_ptr<BzipExtentWriter> bzip_writer;
-  
+
   // Since bzip decompression is optional, we have a variable writer that will
   // point to one of the ExtentWriter objects above.
   ExtentWriter* writer = NULL;
@@ -285,10 +289,10 @@
   TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_));
   TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length()));
   TEST_AND_RETURN_FALSE(writer->End());
-  
+
   // Update buffer
   buffer_offset_ += operation.data_length();
-  RemoveBufferHeadBytes(&buffer_, operation.data_length());
+  DiscardBufferHeadBytes(operation.data_length(), do_hash);
   return true;
 }
 
@@ -431,8 +435,58 @@
 
   // Update buffer.
   buffer_offset_ += operation.data_length();
-  RemoveBufferHeadBytes(&buffer_, operation.data_length());
+  DiscardBufferHeadBytes(operation.data_length(),
+                         true);  // do_hash
   return true;
 }
 
+bool DeltaPerformer::ExtractSignatureMessage(
+    const DeltaArchiveManifest_InstallOperation& operation) {
+  if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
+      !manifest_.has_signatures_offset() ||
+      manifest_.signatures_offset() != operation.data_offset()) {
+    return false;
+  }
+  TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() &&
+                        manifest_.signatures_size() == operation.data_length());
+  TEST_AND_RETURN_FALSE(signatures_message_data_.empty());
+  TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset());
+  TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size());
+  signatures_message_data_.insert(
+      signatures_message_data_.begin(),
+      buffer_.begin(),
+      buffer_.begin() + manifest_.signatures_size());
+  LOG(INFO) << "Extracted signature data of size "
+            << manifest_.signatures_size() << " at "
+            << manifest_.signatures_offset();
+  return true;
+}
+
+bool DeltaPerformer::VerifyPayload(const string& public_key_path) {
+  string key_path = public_key_path;
+  if (key_path.empty()) {
+    key_path = kUpdatePayloadPublicKeyPath;
+  }
+  LOG(INFO) << "Verifying delta payload. Public key path: " << key_path;
+  if (!utils::FileExists(key_path.c_str())) {
+    LOG(WARNING) << "Not verifying delta payload due to missing public key.";
+    return true;
+  }
+  TEST_AND_RETURN_FALSE(!signatures_message_data_.empty());
+  vector<char> signed_hash_data;
+  TEST_AND_RETURN_FALSE(PayloadSigner::VerifySignature(signatures_message_data_,
+                                                       key_path,
+                                                       &signed_hash_data));
+  const vector<char>& hash_data = hash_calculator_.raw_hash();
+  TEST_AND_RETURN_FALSE(!hash_data.empty());
+  return hash_data == signed_hash_data;
+}
+
+void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) {
+  if (do_hash) {
+    hash_calculator_.Update(&buffer_[0], count);
+  }
+  buffer_.erase(buffer_.begin(), buffer_.begin() + count);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/delta_performer.h b/delta_performer.h
index cf75a67..82f4d90 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -6,9 +6,13 @@
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_PERFORMER_H__
 
 #include <inttypes.h>
+
 #include <vector>
+
 #include <google/protobuf/repeated_field.h>
+
 #include "update_engine/file_writer.h"
+#include "update_engine/omaha_hash_calculator.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -25,7 +29,7 @@
         next_operation_num_(0),
         buffer_offset_(0),
         block_size_(0) {}
-  
+
   // Opens the kernel. Should be called before or after Open(), but before
   // Write(). The kernel file will be close()d when Close() is called.
   bool OpenKernel(const char* kernel_path);
@@ -41,7 +45,15 @@
   // Wrapper around close. Returns 0 on success or -errno on error.
   // Closes both 'path' given to Open() and the kernel path.
   int Close();
-  
+
+  // Verifies the downloaded payload against the signed hash included in the
+  // payload and returns true on success, false on failure. This method should
+  // be called after closing the stream. Note this method returns true if the
+  // public key is unavailable; it returns false if the public key is available
+  // but the delta payload doesn't include a signature. If |public_key_path| is
+  // an empty string, uses the default public key path.
+  bool VerifyPayload(const std::string& public_key_path);
+
   // Converts an ordered collection of Extent objects which contain data of
   // length full_length to a comma-separated string. For each Extent, the
   // string will have the start offset and then the length in bytes.
@@ -62,11 +74,11 @@
   // to be able to perform a given install operation.
   bool CanPerformInstallOperation(
       const DeltaArchiveManifest_InstallOperation& operation);
-  
+
   // Returns true on success.
   bool PerformInstallOperation(
       const DeltaArchiveManifest_InstallOperation& operation);
-  
+
   // These perform a specific type of operation and return true on success.
   bool PerformReplaceOperation(
       const DeltaArchiveManifest_InstallOperation& operation,
@@ -78,18 +90,27 @@
       const DeltaArchiveManifest_InstallOperation& operation,
       bool is_kernel_partition);
 
+  // Returns true if the payload signature message has been extracted from
+  // |operation|, false otherwise.
+  bool ExtractSignatureMessage(
+      const DeltaArchiveManifest_InstallOperation& operation);
+
+  // Discard |count| bytes from the beginning of buffer_. If |do_hash| is true,
+  // updates the hash calculator with these bytes before discarding them.
+  void DiscardBufferHeadBytes(size_t count, bool do_hash);
+
   // File descriptor of open device.
   int fd_;
-  
+
   // File descriptor of the kernel device
   int kernel_fd_;
-  
+
   std::string path_;  // Path that fd_ refers to.
   std::string kernel_path_;  // Path that kernel_fd_ refers to.
-  
+
   DeltaArchiveManifest manifest_;
   bool manifest_valid_;
-  
+
   // Index of the next operation to perform in the manifest.
   int next_operation_num_;
 
@@ -100,10 +121,16 @@
   std::vector<char> buffer_;
   // Offset of buffer_ in the binary blobs section of the update.
   uint64_t buffer_offset_;
-  
+
   // The block size (parsed from the manifest).
   uint32_t block_size_;
-  
+
+  // Calculate the payload hash to verify against the signed hash.
+  OmahaHashCalculator hash_calculator_;
+
+  // Signatures message blob extracted directly from the payload.
+  std::vector<char> signatures_message_data_;
+
   DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
 };
 
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 09a4c7d..8c7d2cd 100755
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -29,6 +29,7 @@
 using std::vector;
 
 extern const char* kUnittestPrivateKeyPath;
+extern const char* kUnittestPublicKeyPath;
 
 class DeltaPerformerTest : public ::testing::Test { };
 
@@ -255,6 +256,9 @@
   EXPECT_TRUE(utils::ReadFile(old_kernel, &updated_kernel_partition));
   EXPECT_EQ(0, strncmp(&updated_kernel_partition[0], new_data_string,
                        strlen(new_data_string)));
+
+  EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+  EXPECT_TRUE(performer.VerifyPayload(kUnittestPublicKeyPath));
 }
 
 }  // namespace chromeos_update_engine
diff --git a/download_action.cc b/download_action.cc
index 6b27722..608eeb9 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -150,6 +150,11 @@
                  << " failed. Expected size " << install_plan_.size
                  << " but got size " << bytes_received_;
       code = kActionCodeDownloadSizeMismatchError;
+    } else if (!install_plan_.is_full_update &&
+               !delta_performer_->VerifyPayload("")) {
+      LOG(ERROR) << "Download of " << install_plan_.download_url
+                 << " failed due to payload verification error.";
+      code = kActionCodeDownloadPayloadVerificationError;
     }
   }
 
diff --git a/omaha_hash_calculator.cc b/omaha_hash_calculator.cc
index 88bfc6d..0df83bc 100644
--- a/omaha_hash_calculator.cc
+++ b/omaha_hash_calculator.cc
@@ -36,10 +36,13 @@
 bool OmahaHashCalculator::Finalize() {
   bool success = true;
   TEST_AND_RETURN_FALSE(hash_.empty());
-  unsigned char md[SHA256_DIGEST_LENGTH];
-  TEST_AND_RETURN_FALSE(SHA256_Final(md, &ctx_) == 1);
+  TEST_AND_RETURN_FALSE(raw_hash_.empty());
+  raw_hash_.resize(SHA256_DIGEST_LENGTH);
+  TEST_AND_RETURN_FALSE(
+      SHA256_Final(reinterpret_cast<unsigned char*>(&raw_hash_[0]),
+                   &ctx_) == 1);
 
-  // Convert md to base64 encoding and store it in hash_
+  // Convert raw_hash_ to base64 encoding and store it in hash_.
   BIO *b64 = BIO_new(BIO_f_base64());
   if (!b64)
     LOG(INFO) << "BIO_new(BIO_f_base64()) failed";
@@ -48,7 +51,9 @@
     LOG(INFO) << "BIO_new(BIO_s_mem()) failed";
   if (b64 && bmem) {
     b64 = BIO_push(b64, bmem);
-    success = (BIO_write(b64, md, sizeof(md)) == sizeof(md));
+    success =
+        (BIO_write(b64, &raw_hash_[0], raw_hash_.size()) ==
+         static_cast<int>(raw_hash_.size()));
     if (success)
       success = (BIO_flush(b64) == 1);
 
diff --git a/omaha_hash_calculator.h b/omaha_hash_calculator.h
index 208fd01..52ac1b7 100644
--- a/omaha_hash_calculator.h
+++ b/omaha_hash_calculator.h
@@ -40,6 +40,11 @@
     return hash_;
   }
 
+  const std::vector<char>& raw_hash() const {
+    DCHECK(!raw_hash_.empty()) << "Call Finalize() first";
+    return raw_hash_;
+  }
+
   static bool RawHashOfData(const std::vector<char>& data,
                             std::vector<char>* out_hash);
 
@@ -49,9 +54,10 @@
   static std::string OmahaHashOfData(const std::vector<char>& data);
 
  private:
-  // If non-empty, the final base64 encoded hash. Will only be set to
-  // non-empty when Finalize is called.
+  // If non-empty, the final base64 encoded hash and the raw hash. Will only be
+  // set to non-empty when Finalize is called.
   std::string hash_;
+  std::vector<char> raw_hash_;
 
   // Init success
   bool valid_;
diff --git a/omaha_hash_calculator_unittest.cc b/omaha_hash_calculator_unittest.cc
index d228f12..449cb90 100644
--- a/omaha_hash_calculator_unittest.cc
+++ b/omaha_hash_calculator_unittest.cc
@@ -4,22 +4,40 @@
 
 #include <math.h>
 #include <unistd.h>
+
+#include <vector>
+
 #include <glib.h>
 #include <gtest/gtest.h>
+
 #include "update_engine/libcurl_http_fetcher.h"
 #include "update_engine/omaha_hash_calculator.h"
 
+using std::vector;
+
 namespace chromeos_update_engine {
 
 class OmahaHashCalculatorTest : public ::testing::Test { };
 
+// Generated by running this on a linux shell:
+// $ echo -n hi | openssl dgst -sha256 -binary | openssl base64
+static const char kExpectedHash[] =
+    "j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=";
+static const char kExpectedRawHash[] = {
+  0x8f, 0x43, 0x43, 0x46, 0x64, 0x8f, 0x6b, 0x96,
+  0xdf, 0x89, 0xdd, 0xa9, 0x01, 0xc5, 0x17, 0x6b,
+  0x10, 0xa6, 0xd8, 0x39, 0x61, 0xdd, 0x3c, 0x1a,
+  0xc8, 0x8b, 0x59, 0xb2, 0xdc, 0x32, 0x7a, 0xa4
+};
+
 TEST(OmahaHashCalculatorTest, SimpleTest) {
   OmahaHashCalculator calc;
   calc.Update("hi", 2);
   calc.Finalize();
-  // Generated by running this on a linux shell:
-  // $ echo -n hi | openssl dgst -sha256 -binary | openssl base64
-  EXPECT_EQ("j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=", calc.hash());
+  EXPECT_EQ(kExpectedHash, calc.hash());
+  vector<char> raw_hash(kExpectedRawHash,
+                        kExpectedRawHash + arraysize(kExpectedRawHash));
+  EXPECT_TRUE(raw_hash == calc.raw_hash());
 }
 
 TEST(OmahaHashCalculatorTest, MultiUpdateTest) {
@@ -27,9 +45,10 @@
   calc.Update("h", 1);
   calc.Update("i", 1);
   calc.Finalize();
-  // Generated by running this on a linux shell:
-  // $ echo -n hi | openssl dgst -sha256 -binary | openssl base64
-  EXPECT_EQ("j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=", calc.hash());
+  EXPECT_EQ(kExpectedHash, calc.hash());
+  vector<char> raw_hash(kExpectedRawHash,
+                        kExpectedRawHash + arraysize(kExpectedRawHash));
+  EXPECT_TRUE(raw_hash == calc.raw_hash());
 }
 
 TEST(OmahaHashCalculatorTest, BigTest) {
diff --git a/payload_signer.cc b/payload_signer.cc
index 5b0c267..de80a5b 100644
--- a/payload_signer.cc
+++ b/payload_signer.cc
@@ -94,4 +94,56 @@
   return true;
 }
 
+bool PayloadSigner::VerifySignature(const std::vector<char>& signature_blob,
+                                    const std::string& public_key_path,
+                                    std::vector<char>* out_hash_data) {
+  TEST_AND_RETURN_FALSE(!public_key_path.empty());
+
+  Signatures signatures;
+  TEST_AND_RETURN_FALSE(signatures.ParseFromArray(&signature_blob[0],
+                                                  signature_blob.size()));
+
+  // Finds a signature that matches the current version.
+  int sig_index = 0;
+  for (; sig_index < signatures.signatures_size(); sig_index++) {
+    const Signatures_Signature& signature = signatures.signatures(sig_index);
+    if (signature.has_version() &&
+        signature.version() == kSignatureMessageVersion) {
+      break;
+    }
+  }
+  TEST_AND_RETURN_FALSE(sig_index < signatures.signatures_size());
+
+  const Signatures_Signature& signature = signatures.signatures(sig_index);
+  const string sig_data = signature.data();
+  string sig_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("/var/run/signature.XXXXXX", &sig_path, NULL));
+  ScopedPathUnlinker sig_path_unlinker(sig_path);
+  TEST_AND_RETURN_FALSE(utils::WriteFile(sig_path.c_str(),
+                                         &sig_data[0],
+                                         sig_data.size()));
+  string hash_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("/var/run/hash.XXXXXX", &hash_path, NULL));
+  ScopedPathUnlinker hash_path_unlinker(hash_path);
+
+  // TODO(petkov): This runs on the client so it will be cleaner if it uses
+  // direct openssl library calls.
+  vector<string> cmd;
+  SplitString("/usr/bin/openssl rsautl -verify -pubin -inkey x -in x -out x",
+              ' ',
+              &cmd);
+  cmd[cmd.size() - 5] = public_key_path;
+  cmd[cmd.size() - 3] = sig_path;
+  cmd[cmd.size() - 1] = hash_path;
+
+  int return_code = 0;
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code));
+  TEST_AND_RETURN_FALSE(return_code == 0);
+
+  TEST_AND_RETURN_FALSE(utils::ReadFile(hash_path, out_hash_data));
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_signer.h b/payload_signer.h
index 781a513..59affd3 100644
--- a/payload_signer.h
+++ b/payload_signer.h
@@ -28,6 +28,12 @@
   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);
+
  private:
   // This should never be constructed
   DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadSigner);
diff --git a/payload_signer_unittest.cc b/payload_signer_unittest.cc
index ff31f02..8121cae 100644
--- a/payload_signer_unittest.cc
+++ b/payload_signer_unittest.cc
@@ -19,50 +19,62 @@
 namespace chromeos_update_engine {
 
 const char* kUnittestPrivateKeyPath = "unittest_key.pem";
+const char* kUnittestPublicKeyPath = "unittest_key.pub.pem";
+
+// Some data and its corresponding hash and signature:
+const char kDataToSign[] = "This is some data to sign.";
+const 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
+};
+const char kDataSignature[] = {
+  0xa4, 0xbc, 0x8f, 0xeb, 0x81, 0x05, 0xaa, 0x56,
+  0x1b, 0x56, 0xe5, 0xcb, 0x9b, 0x1a, 0x00, 0xd7,
+  0x1d, 0x87, 0x8e, 0xda, 0x5e, 0x90, 0x09, 0xb8,
+  0x15, 0xf4, 0x25, 0x97, 0x2f, 0x3c, 0xa1, 0xf3,
+  0x02, 0x75, 0xcd, 0x67, 0x4b, 0x0c, 0x1f, 0xf5,
+  0x6e, 0xf1, 0x58, 0xd7, 0x0d, 0x8c, 0x18, 0x91,
+  0x52, 0x30, 0x98, 0x64, 0x58, 0xc0, 0xe2, 0xb5,
+  0x77, 0x3b, 0x96, 0x8f, 0x05, 0xc4, 0x7f, 0x7a,
+  0x9a, 0x44, 0x0f, 0xc7, 0x1b, 0x90, 0x83, 0xf8,
+  0x69, 0x05, 0xa8, 0x02, 0x57, 0xcd, 0x2e, 0x5b,
+  0x96, 0xc7, 0x77, 0xa6, 0x1f, 0x97, 0x97, 0x05,
+  0xb3, 0x30, 0x1c, 0x27, 0xd7, 0x2d, 0x31, 0x60,
+  0x84, 0x7e, 0x99, 0x00, 0xe6, 0xe1, 0x39, 0xa6,
+  0xf3, 0x3a, 0x72, 0xba, 0xc4, 0xfe, 0x68, 0xa9,
+  0x08, 0xfa, 0xbc, 0xa8, 0x44, 0x66, 0xa0, 0x60,
+  0xde, 0xc9, 0xb2, 0xba, 0xbc, 0x80, 0xb5, 0x55
+};
 
 //class PayloadSignerTest : public ::testing::Test {};
 
-TEST(PayloadSignerTest, SimpleTest) {
-  // Some data and its corresponding signature:
-  const string kDataToSign = "This is some data to sign.";
-  const char kExpectedSignature[] = {
-    0xa4, 0xbc, 0x8f, 0xeb, 0x81, 0x05, 0xaa, 0x56,
-    0x1b, 0x56, 0xe5, 0xcb, 0x9b, 0x1a, 0x00, 0xd7,
-    0x1d, 0x87, 0x8e, 0xda, 0x5e, 0x90, 0x09, 0xb8,
-    0x15, 0xf4, 0x25, 0x97, 0x2f, 0x3c, 0xa1, 0xf3,
-    0x02, 0x75, 0xcd, 0x67, 0x4b, 0x0c, 0x1f, 0xf5,
-    0x6e, 0xf1, 0x58, 0xd7, 0x0d, 0x8c, 0x18, 0x91,
-    0x52, 0x30, 0x98, 0x64, 0x58, 0xc0, 0xe2, 0xb5,
-    0x77, 0x3b, 0x96, 0x8f, 0x05, 0xc4, 0x7f, 0x7a,
-    0x9a, 0x44, 0x0f, 0xc7, 0x1b, 0x90, 0x83, 0xf8,
-    0x69, 0x05, 0xa8, 0x02, 0x57, 0xcd, 0x2e, 0x5b,
-    0x96, 0xc7, 0x77, 0xa6, 0x1f, 0x97, 0x97, 0x05,
-    0xb3, 0x30, 0x1c, 0x27, 0xd7, 0x2d, 0x31, 0x60,
-    0x84, 0x7e, 0x99, 0x00, 0xe6, 0xe1, 0x39, 0xa6,
-    0xf3, 0x3a, 0x72, 0xba, 0xc4, 0xfe, 0x68, 0xa9,
-    0x08, 0xfa, 0xbc, 0xa8, 0x44, 0x66, 0xa0, 0x60,
-    0xde, 0xc9, 0xb2, 0xba, 0xbc, 0x80, 0xb5, 0x55
-  };
-
+namespace {
+void SignSampleData(vector<char>* out_signature_blob) {
   string data_path;
   ASSERT_TRUE(
       utils::MakeTempFile("/tmp/data.XXXXXX", &data_path, NULL));
   ScopedPathUnlinker data_path_unlinker(data_path);
   ASSERT_TRUE(utils::WriteFile(data_path.c_str(),
-                               kDataToSign.data(),
-                               kDataToSign.size()));
+                               kDataToSign,
+                               strlen(kDataToSign)));
   uint64_t length = 0;
   EXPECT_TRUE(PayloadSigner::SignatureBlobLength(kUnittestPrivateKeyPath,
                                                  &length));
   EXPECT_GT(length, 0);
-  vector<char> signature_blob;
   EXPECT_TRUE(PayloadSigner::SignPayload(data_path,
                                          kUnittestPrivateKeyPath,
-                                         &signature_blob));
-  EXPECT_EQ(length, signature_blob.size());
+                                         out_signature_blob));
+  EXPECT_EQ(length, out_signature_blob->size());
+}
+}
+
+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()));
@@ -70,9 +82,23 @@
   const Signatures_Signature& signature = signatures.signatures(0);
   EXPECT_EQ(kSignatureMessageVersion, signature.version());
   const string sig_data = signature.data();
-  ASSERT_EQ(sizeof(kExpectedSignature), sig_data.size());
-  for (size_t i = 0; i < sizeof(kExpectedSignature); i++) {
-    EXPECT_EQ(kExpectedSignature[i], sig_data[i]);
+  ASSERT_EQ(arraysize(kDataSignature), sig_data.size());
+  for (size_t i = 0; i < arraysize(kDataSignature); i++) {
+    EXPECT_EQ(kDataSignature[i], sig_data[i]);
+  }
+}
+
+TEST(PayloadSignerTest, RunAsRootVerifySignatureTest) {
+  vector<char> signature_blob;
+  SignSampleData(&signature_blob);
+
+  vector<char> hash_data;
+  EXPECT_TRUE(PayloadSigner::VerifySignature(signature_blob,
+                                             kUnittestPublicKeyPath,
+                                             &hash_data));
+  ASSERT_EQ(arraysize(kDataHash), hash_data.size());
+  for (size_t i = 0; i < arraysize(kDataHash); i++) {
+    EXPECT_EQ(kDataHash[i], hash_data[i]);
   }
 }