Verify AU payload manifest signature if present.

In order to support downloads over http for a number of reasons, we need
to secure http downloads. The first step in this process is to
verify the signature of the manifest itself before parsing. This can be
done even for https-based downloads in order to provide defense-in-depth
against a SSL attack. This CL adds the required verification logic in
update_engine, if such a manifest signature is present in the Omaha
response.

Until the delta generator is modified in a subsequent check-in to update
the manifest and payload with the required signature, none of this new
code will have any effect.

The delta generator change to populate non-zero values for these new
fields will follow in subsequent CLs.

BUG=chromium-os:33602
TEST=Tested on ZGB to make sure existing functionality works fine.
     Added new unit tests.
Change-Id: I2d8b09c23faf87049893b1dee97a34e1f300aded
Reviewed-on: https://gerrit.chromium.org/gerrit/32844
Commit-Ready: Jay Srinivasan <jaysri@chromium.org>
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
diff --git a/payload_signer.cc b/payload_signer.cc
index 88188a2..19a9230 100644
--- a/payload_signer.cc
+++ b/payload_signer.cc
@@ -56,7 +56,7 @@
   uint32_t version = kSignatureMessageOriginalVersion;
   LOG_IF(WARNING, kSignatureMessageCurrentVersion -
          kSignatureMessageOriginalVersion + 1 < signatures.size())
-      << "You may want to support clients in the rage ["
+      << "You may want to support clients in the range ["
       << kSignatureMessageOriginalVersion << ", "
       << kSignatureMessageCurrentVersion << "] inclusive, but you only "
       << "provided " << signatures.size() << " signatures.";
@@ -86,8 +86,11 @@
   // 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) ==
+  ActionExitCode error = kActionCodeSuccess;
+  InstallPlan install_plan;
+  DeltaPerformer delta_performer(NULL, &install_plan);
+  TEST_AND_RETURN_FALSE(delta_performer.ParsePayloadMetadata(
+      payload, out_manifest, out_metadata_size, &error) ==
                         DeltaPerformer::kMetadataParseSuccess);
   LOG(INFO) << "Metadata size: " << *out_metadata_size;
   out_payload->swap(payload);
@@ -139,6 +142,7 @@
 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("/tmp/signature.XXXXXX", &sig_path, NULL));
@@ -217,11 +221,11 @@
 bool PayloadSigner::VerifySignature(const std::vector<char>& signature_blob,
                                     const std::string& public_key_path,
                                     std::vector<char>* out_hash_data) {
-  return VerifySignatureVersion(signature_blob, public_key_path,
+  return VerifySignatureBlob(signature_blob, public_key_path,
                                 kSignatureMessageCurrentVersion, out_hash_data);
 }
 
-bool PayloadSigner::VerifySignatureVersion(
+bool PayloadSigner::VerifySignatureBlob(
     const std::vector<char>& signature_blob,
     const std::string& public_key_path,
     uint32_t client_version,
@@ -229,6 +233,7 @@
   TEST_AND_RETURN_FALSE(!public_key_path.empty());
 
   Signatures signatures;
+  LOG(INFO) << "signature size = " <<  signature_blob.size();
   TEST_AND_RETURN_FALSE(signatures.ParseFromArray(&signature_blob[0],
                                                   signature_blob.size()));
 
@@ -244,7 +249,17 @@
   TEST_AND_RETURN_FALSE(sig_index < signatures.signatures_size());
 
   const Signatures_Signature& signature = signatures.signatures(sig_index);
-  const string& sig_data = signature.data();
+  vector<char> sig_data(signature.data().begin(), signature.data().end());
+
+  return GetRawHashFromSignature(sig_data, public_key_path, out_hash_data);
+}
+
+
+bool PayloadSigner::GetRawHashFromSignature(
+    const std::vector<char>& sig_data,
+    const std::string& public_key_path,
+    std::vector<char>* out_hash_data) {
+  TEST_AND_RETURN_FALSE(!public_key_path.empty());
 
   // The code below executes the equivalent of:
   //
@@ -253,7 +268,11 @@
 
   // Loads the public key.
   FILE* fpubkey = fopen(public_key_path.c_str(), "rb");
-  TEST_AND_RETURN_FALSE(fpubkey != NULL);
+  if (!fpubkey) {
+    LOG(ERROR) << "Unable to open public key file: " << public_key_path;
+    return false;
+  }
+
   char dummy_password[] = { ' ', 0 };  // Ensure no password is read from stdin.
   RSA* rsa = PEM_read_RSA_PUBKEY(fpubkey, NULL, NULL, dummy_password);
   fclose(fpubkey);
@@ -298,7 +317,7 @@
       payload.begin() + metadata_size + manifest.signatures_offset(),
       payload.end());
   vector<char> signed_hash;
-  TEST_AND_RETURN_FALSE(VerifySignatureVersion(
+  TEST_AND_RETURN_FALSE(VerifySignatureBlob(
       signature_blob, public_key_path, client_key_check_version, &signed_hash));
   TEST_AND_RETURN_FALSE(!signed_hash.empty());
   vector<char> hash;
@@ -369,4 +388,27 @@
   return true;
 }
 
+bool PayloadSigner::GetManifestSignature(const char* manifest,
+                                         size_t manifest_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> manifest_hash;
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(manifest,
+                                                            manifest_size,
+                                                            &manifest_hash));
+
+  vector<char> signature;
+  TEST_AND_RETURN_FALSE(SignHash(manifest_hash,
+                                 private_key_path,
+                                 &signature));
+
+  TEST_AND_RETURN_FALSE(OmahaHashCalculator::Base64Encode(&signature[0],
+                                                          signature.size(),
+                                                          out_signature));
+  return true;
+}
+
+
 }  // namespace chromeos_update_engine