Add unittest for load cache manifest success path

Add a unittest that generates a valid manifest, signed with unittest
keys. Then assert that download action can load the cached manifest
correctly.

Since the unittest keys are RSA keys, we add support for RSA keys to
payload_verifier

Test: treehugger
Change-Id: Iebf4bd740ad2c18f4e71527eeee4c12e3f8e7dea
diff --git a/common/download_action.h b/common/download_action.h
index e154856..caa5a7b 100644
--- a/common/download_action.h
+++ b/common/download_action.h
@@ -87,7 +87,7 @@
   std::string Type() const override { return StaticType(); }
 
   // Testing
-  void SetTestFileWriter(FileWriter* writer) { writer_ = writer; }
+  void SetTestFileWriter(DeltaPerformer* writer) { writer_ = writer; }
 
   int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
 
@@ -131,8 +131,8 @@
   bool interactive_;
 
   // The FileWriter that downloaded data should be written to. It will
-  // either point to *decompressing_file_writer_ or *delta_performer_.
-  FileWriter* writer_;
+  // either point to a writer for unittest or *delta_performer_.
+  DeltaPerformer* writer_;
 
   std::unique_ptr<DeltaPerformer> delta_performer_;
 
diff --git a/download_action.cc b/download_action.cc
index 076ee2c..0456298 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -105,11 +105,14 @@
     return false;
   }
 
+  if (writer_ && writer_ != delta_performer_.get()) {
+    LOG(INFO) << "Using writer for test.";
+  }
   ErrorCode error;
   const bool success =
-      delta_performer_->Write(
+      writer_->Write(
           cached_manifest_bytes.data(), cached_manifest_bytes.size(), &error) &&
-      delta_performer_->IsManifestValid();
+      writer_->IsManifestValid();
   if (success) {
     LOG(INFO) << "Successfully parsed cached manifest";
   } else {
@@ -162,9 +165,9 @@
                               manifest_metadata_size + manifest_signature_size);
     }
 
-    // If there're remaining unprocessed data blobs, fetch them. Be careful not
-    // to request data beyond the end of the payload to avoid 416 HTTP response
-    // error codes.
+    // If there're remaining unprocessed data blobs, fetch them. Be careful
+    // not to request data beyond the end of the payload to avoid 416 HTTP
+    // response error codes.
     int64_t next_data_offset = 0;
     prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
     uint64_t resume_offset =
diff --git a/download_action_android_unittest.cc b/download_action_android_unittest.cc
index 7db1c60..c1ad9c2 100644
--- a/download_action_android_unittest.cc
+++ b/download_action_android_unittest.cc
@@ -14,23 +14,30 @@
 // limitations under the License.
 //
 
-#include "common/mock_action_processor.h"
+#include <unistd.h>
+#include <cstdint>
+#include <memory>
+
+#include <gmock/gmock.h>
 #include <gmock/gmock-actions.h>
 #include <gmock/gmock-function-mocker.h>
 #include <gmock/gmock-spec-builders.h>
+#include <gtest/gtest.h>
 
-#include "payload_consumer/install_plan.h"
 #include "update_engine/common/action_pipe.h"
 #include "update_engine/common/boot_control_stub.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/download_action.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/mock_action_processor.h"
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/mock_prefs.h"
 #include "update_engine/common/test_utils.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <memory>
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/payload_file.h"
+#include "update_engine/payload_generator/payload_signer.h"
 
 namespace chromeos_update_engine {
 using testing::_;
@@ -38,6 +45,9 @@
 using testing::Return;
 using testing::SetArgPointee;
 
+extern const char* kUnittestPrivateKeyPath;
+extern const char* kUnittestPublicKeyPath;
+
 class DownloadActionTest : public ::testing::Test {
  public:
   static constexpr int64_t METADATA_SIZE = 1024;
@@ -73,12 +83,8 @@
   action_pipe->set_contents(install_plan);
 
   // takes ownership of passed in HttpFetcher
-  auto download_action =
-      std::make_unique<DownloadAction>(&prefs,
-                                       &boot_control,
-                                       nullptr,
-                                       http_fetcher,
-                                       false /* interactive */);
+  auto download_action = std::make_unique<DownloadAction>(
+      &prefs, &boot_control, nullptr, http_fetcher, false /* interactive */);
   download_action->set_in_pipe(action_pipe);
   MockActionProcessor mock_processor;
   download_action->SetProcessor(&mock_processor);
@@ -86,4 +92,85 @@
   ASSERT_EQ(download_action->http_fetcher()->GetBytesDownloaded(), data.size());
 }
 
+TEST_F(DownloadActionTest, CacheManifestValid) {
+  // Create a valid manifest
+  PayloadGenerationConfig config;
+  config.version.major = kMaxSupportedMajorPayloadVersion;
+  config.version.minor = kMaxSupportedMinorPayloadVersion;
+
+  PayloadFile payload_file;
+  ASSERT_TRUE(payload_file.Init(config));
+  PartitionConfig partition_config{"system"};
+  ScopedTempFile partition_file("part-system-XXXXXX", true);
+  ftruncate(partition_file.fd(), 4096);
+  partition_config.size = 4096;
+  partition_config.path = partition_file.path();
+  ASSERT_TRUE(
+      payload_file.AddPartition(partition_config, partition_config, {}, {}, 0));
+  ScopedTempFile blob_file("Blob-XXXXXX");
+  ScopedTempFile manifest_file("Manifest-XXXXXX");
+  uint64_t metadata_size;
+  std::string private_key =
+      test_utils::GetBuildArtifactsPath(kUnittestPrivateKeyPath);
+  payload_file.WritePayload(
+      manifest_file.path(), blob_file.path(), private_key, &metadata_size);
+  uint64_t signature_blob_length = 0;
+  ASSERT_TRUE(PayloadSigner::SignatureBlobLength({private_key},
+                                                 &signature_blob_length));
+  std::string data;
+  ASSERT_TRUE(utils::ReadFile(manifest_file.path(), &data));
+  data.resize(metadata_size + signature_blob_length);
+
+  // Setup the prefs so that manifest is cached
+  MockPrefs prefs;
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStatePayloadIndex, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(0L), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsManifestMetadataSize, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(metadata_size), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsManifestSignatureSize, _))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<1>(signature_blob_length), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextDataOffset, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(0L), Return(true)));
+  EXPECT_CALL(prefs, GetString(kPrefsManifestBytes, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(data), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStatePayloadIndex, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(true)));
+
+  BootControlStub boot_control;
+  MockHttpFetcher* http_fetcher =
+      new MockHttpFetcher(data.data(), data.size(), nullptr);
+  http_fetcher->set_delay(false);
+  InstallPlan install_plan;
+  auto& payload = install_plan.payloads.emplace_back();
+  install_plan.download_url = "http://fake_url.invalid";
+  payload.size = data.size();
+  payload.payload_urls.emplace_back("http://fake_url.invalid");
+  install_plan.is_resume = true;
+  action_pipe->set_contents(install_plan);
+
+  // takes ownership of passed in HttpFetcher
+  auto download_action = std::make_unique<DownloadAction>(
+      &prefs, &boot_control, nullptr, http_fetcher, false /* interactive */);
+
+  FakeHardware hardware;
+  DeltaPerformer delta_performer(&prefs,
+                                 &boot_control,
+                                 &hardware,
+                                 nullptr,
+                                 &install_plan,
+                                 &payload,
+                                 false);
+  delta_performer.set_public_key_path(kUnittestPublicKeyPath);
+  download_action->SetTestFileWriter(&delta_performer);
+  download_action->set_in_pipe(action_pipe);
+  MockActionProcessor mock_processor;
+  download_action->SetProcessor(&mock_processor);
+  download_action->PerformAction();
+
+  // Manifest is cached, so no data should be downloaded from http fetcher.
+  ASSERT_EQ(download_action->http_fetcher()->GetBytesDownloaded(), 0UL);
+}
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 85902c8..8a3ea65 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -172,9 +172,7 @@
       if (padded_hash_data == sig_hash_data) {
         return true;
       }
-    }
-
-    if (key_type == EVP_PKEY_EC) {
+    } else if (key_type == EVP_PKEY_EC) {
       EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(public_key.get());
       TEST_AND_RETURN_FALSE(ec_key != nullptr);
       if (ECDSA_verify(0,
@@ -185,10 +183,10 @@
                        ec_key) == 1) {
         return true;
       }
+    } else {
+      LOG(ERROR) << "Unsupported key type " << key_type;
+      return false;
     }
-
-    LOG(ERROR) << "Unsupported key type " << key_type;
-    return false;
   }
   LOG(INFO) << "Failed to verify the signature with " << public_keys_.size()
             << " keys.";