imageloader: allow Component to search for other keys

This allows us to look for manifest signatures verified with keys
other than the prod key simply by widening the name pattern for
the manifest signature file.

BUG=chromium:697645
TEST=unit tests, platform_ImageLoaderServer, inspect container
  with imageloader.sig.2 and expanded pattern and ensure it parses
  the right key number and finds the signature file

Change-Id: Ie5f635523ac7a81d3bc851b8ae9dfbb2542ba5e1
Reviewed-on: https://chromium-review.googlesource.com/457801
Commit-Ready: Eric Caruso <ejcaruso@chromium.org>
Tested-by: Eric Caruso <ejcaruso@chromium.org>
Reviewed-by: Greg Kerr <kerrnel@chromium.org>
diff --git a/component.cc b/component.cc
index 1afaf62..b7d4a7d 100644
--- a/component.cc
+++ b/component.cc
@@ -8,17 +8,19 @@
 
 #include <algorithm>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <base/files/file.h>
+#include <base/files/file_enumerator.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/files/scoped_file.h>
 #include <base/json/json_string_value_serializer.h>
 #include <base/logging.h>
 #include <base/numerics/safe_conversions.h>
-#include <base/strings/string_number_conversions.h>
 #include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <crypto/secure_hash.h>
 #include <crypto/sha2.h>
@@ -35,7 +37,7 @@
 // The name of the fingerprint file.
 constexpr char kFingerprintName[] = "manifest.fingerprint";
 // The manifest signature.
-constexpr char kManifestSignatureName[] = "imageloader.sig.1";
+constexpr char kManifestSignatureNamePattern[] = "imageloader.sig.1";
 // The current version of the manifest file.
 constexpr int kCurrentManifestVersion = 1;
 // The name of the version field in the manifest.
@@ -57,8 +59,39 @@
   return component_dir.Append(kManifestName);
 }
 
-base::FilePath GetSignaturePath(const base::FilePath& component_dir) {
-  return component_dir.Append(kManifestSignatureName);
+bool GetSignaturePath(const base::FilePath& component_dir,
+                      base::FilePath* signature_path,
+                      int* key_number) {
+  DCHECK(signature_path);
+  DCHECK(key_number);
+
+  base::FileEnumerator files(component_dir,
+                             false,
+                             base::FileEnumerator::FileType::FILES,
+                             kManifestSignatureNamePattern);
+  for (base::FilePath path = files.Next(); !path.empty(); path = files.Next()) {
+    // Extract the key number.
+    std::string key_ext = path.FinalExtension();
+    if (key_ext.empty())
+      continue;
+
+    int ext_number;
+    if (!base::StringToInt(key_ext.substr(1), &ext_number))
+      continue;
+
+    *signature_path = path;
+    *key_number = ext_number;
+    return true;
+  }
+  return false;
+}
+
+base::FilePath GetSignaturePathForKey(const base::FilePath& component_dir,
+                                      int key_number) {
+  std::string signature_name(kManifestSignatureNamePattern);
+  signature_name =
+      signature_name.substr(0, signature_name.find_last_of('.') + 1);
+  return component_dir.Append(signature_name + base::IntToString(key_number));
 }
 
 base::FilePath GetFingerprintPath(const base::FilePath& component_dir) {
@@ -115,13 +148,21 @@
 
 }  // namespace
 
-Component::Component(const base::FilePath& component_dir)
-    : component_dir_(component_dir) {}
+Component::Component(const base::FilePath& component_dir, int key_number)
+    : component_dir_(component_dir), key_number_(key_number) {}
 
 std::unique_ptr<Component> Component::Create(
         const base::FilePath& component_dir,
         const std::vector<uint8_t>& public_key) {
-  std::unique_ptr<Component> component(new Component(component_dir));
+  base::FilePath signature_path;
+  int key_number;
+  if (!GetSignaturePath(component_dir, &signature_path, &key_number)) {
+    LOG(ERROR) << "Could not find manifest signature";
+    return nullptr;
+  }
+
+  std::unique_ptr<Component> component(
+      new Component(component_dir, key_number));
   if (!component->LoadManifest(public_key))
     return nullptr;
   return component;
@@ -219,8 +260,9 @@
     LOG(ERROR) << "Could not read manifest file.";
     return false;
   }
-  if (!base::ReadFileToStringWithMaxSize(GetSignaturePath(component_dir_),
-                                         &manifest_sig_, kMaximumFilesize)) {
+  if (!base::ReadFileToStringWithMaxSize(
+          GetSignaturePathForKey(component_dir_, key_number_),
+          &manifest_sig_, kMaximumFilesize)) {
     LOG(ERROR) << "Could not read signature file.";
     return false;
   }
@@ -248,7 +290,8 @@
 
 bool Component::CopyTo(const base::FilePath& dest_dir) {
   if (!WriteFileToDisk(GetManifestPath(dest_dir), manifest_raw_) ||
-      !WriteFileToDisk(GetSignaturePath(dest_dir), manifest_sig_)) {
+      !WriteFileToDisk(GetSignaturePathForKey(dest_dir, key_number_),
+          manifest_sig_)) {
     LOG(ERROR) << "Could not write manifest and signature to disk.";
     return false;
   }
diff --git a/component.h b/component.h
index 7947678..05680aa 100644
--- a/component.h
+++ b/component.h
@@ -67,7 +67,7 @@
   // Constructs a Component. We want to avoid using this where possible since
   // you need to load the manifest before doing anything anyway, so use the
   // static factory method above.
-  explicit Component(const base::FilePath& component_dir);
+  Component(const base::FilePath& component_dir, int key_number);
 
   // Loads and verifies the manfiest. Returns false on failure. |public_key| is
   // the public key used to check the manifest signature.
@@ -89,6 +89,7 @@
   FRIEND_TEST_ALL_PREFIXES(ComponentTest, CopyValidImage);
 
   const base::FilePath component_dir_;
+  int key_number_;
   std::string manifest_raw_;
   std::string manifest_sig_;
   Manifest manifest_;
diff --git a/component_unittest.cc b/component_unittest.cc
index 16e945c..c0d0508 100644
--- a/component_unittest.cc
+++ b/component_unittest.cc
@@ -238,7 +238,7 @@
   sha256->Update(image.data(), image.size());
   sha256->Finish(hash.data(), hash.size());
 
-  Component component(GetTestComponentPath());
+  Component component(GetTestComponentPath(), 1);
   base::FilePath image_dest = temp_dir_.Append("image.copied");
   ASSERT_TRUE(component.CopyComponentFile(image_path, image_dest, hash));