Add unit tests for public interfaces.
This refactors the code to unit test the public interfaces of the
ImageLoader class. It also fixes a bug in GetComponentVersion, caught by
the new tests.
BUG=chromium:630421
TEST=FEATURES="test" emerge-${BOARD} imageloader, and run the client
Change-Id: I62657ee9127586389677bf5a3b1a65766ec2a038
Reviewed-on: https://chromium-review.googlesource.com/363520
Commit-Ready: Greg Kerr <kerrnel@chromium.org>
Tested-by: Greg Kerr <kerrnel@chromium.org>
Reviewed-by: Ricky Zhou <rickyz@chromium.org>
diff --git a/.presubmitignore b/.presubmitignore
index a09690d..28815b7 100644
--- a/.presubmitignore
+++ b/.presubmitignore
@@ -1,2 +1,4 @@
test/22.0.0.158_chromeos_intel64_PepperFlashPlayer/params
test/22.0.0.158_chromeos_intel64_PepperFlashPlayer/hash
+test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/params
+test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/hash
diff --git a/imageloadclient-glue.xml b/imageloadclient-glue.xml
index 68058d2..d7cbcd5 100644
--- a/imageloadclient-glue.xml
+++ b/imageloadclient-glue.xml
@@ -20,9 +20,5 @@
<arg type="s" name="name" direction="in" />
<arg type="s" name="mount_point" direction="out" />
</method>
- <method name="UnloadComponent">
- <arg type="s" name="name" direction="in" />
- <arg type="b" name="success" direction="out" />
- </method>
</interface>
</node>
diff --git a/imageloadclient.cc b/imageloadclient.cc
index 42620c1..a3c3de6 100644
--- a/imageloadclient.cc
+++ b/imageloadclient.cc
@@ -51,16 +51,6 @@
}
}
-void ImageLoadClient::UnloadComponentCallback(const bool& success,
- const ::DBus::Error& err,
- void*) {
- if (success) {
- LOG(INFO) << "Success.";
- } else {
- LOG(INFO) << "Failure.";
- }
-}
-
namespace {
void *TestCalls(void *arg) {
@@ -86,9 +76,6 @@
} else if (inp == "lc") { // LoadComponent
std::cin >> name;
client->LoadComponentAsync(name, NULL);
- } else if (inp == "uc") { // UnloadComponent
- std::cin >> name;
- client->UnloadComponentAsync(name, NULL);
}
}
return NULL;
diff --git a/imageloader-glue.xml b/imageloader-glue.xml
index 68058d2..d7cbcd5 100644
--- a/imageloader-glue.xml
+++ b/imageloader-glue.xml
@@ -20,9 +20,5 @@
<arg type="s" name="name" direction="in" />
<arg type="s" name="mount_point" direction="out" />
</method>
- <method name="UnloadComponent">
- <arg type="s" name="name" direction="in" />
- <arg type="b" name="success" direction="out" />
- </method>
</interface>
</node>
diff --git a/imageloader.gyp b/imageloader.gyp
index cde30dd..cbced4b 100644
--- a/imageloader.gyp
+++ b/imageloader.gyp
@@ -54,7 +54,7 @@
'libimageloader_common',
],
'sources': [
- 'imageloader.cc',
+ 'imageloader_impl.cc',
'imageloader.h',
],
},
diff --git a/imageloader.h b/imageloader.h
index fd962ae..865b3b9 100644
--- a/imageloader.h
+++ b/imageloader.h
@@ -4,15 +4,14 @@
#ifndef IMAGELOADER_IMAGELOADER_H_
#define IMAGELOADER_IMAGELOADER_H_
-#include <map>
#include <string>
-#include <utility>
-#include <base/files/file_path.h>
-#include <base/gtest_prod_util.h>
+#include <base/macros.h>
#include <dbus-c++/dbus.h>
+#include "imageloader_common.h"
#include "imageloader-glue.h"
+#include "imageloader_impl.h"
namespace imageloader {
@@ -23,73 +22,30 @@
public DBus::ObjectAdaptor {
public:
// Instantiate a D-Bus Helper Instance
- explicit ImageLoader(DBus::Connection* conn);
+ ImageLoader(DBus::Connection* conn, const ImageLoaderConfig& config)
+ : DBus::ObjectAdaptor(*conn, kImageLoaderPath), impl_(config) {}
// Register a component.
bool RegisterComponent(const std::string& name, const std::string& version,
const std::string& component_folder_abs_path,
- ::DBus::Error& err);
+ ::DBus::Error& err) {
+ return impl_.RegisterComponent(name, version, component_folder_abs_path);
+ }
// Get component version given component name.
- std::string GetComponentVersion(const std::string& name, ::DBus::Error& err);
+ std::string GetComponentVersion(const std::string& name, ::DBus::Error& err) {
+ return impl_.GetComponentVersion(name);
+ }
// Load the specified component.
- std::string LoadComponent(const std::string& name, ::DBus::Error& err);
- std::string LoadComponentUtil(const std::string& name);
-
- // Unload the specified component.
- bool UnloadComponent(const std::string& name, ::DBus::Error& err);
- bool UnloadComponentUtil(const std::string& name);
+ std::string LoadComponent(const std::string& name, ::DBus::Error& err) {
+ return impl_.LoadComponent(name);
+ }
private:
- struct Manifest {
- int manifest_version;
- std::vector<uint8_t> image_sha256;
- std::vector<uint8_t> params_sha256;
- std::string version;
- };
+ ImageLoaderImpl impl_;
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, ECVerify);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, ManifestFingerPrint);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyValidComponent);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyComponentWithBadManifest);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyValidImage);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyInvalidImage);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyInvalidHash);
- FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, ParseManifest);
-
- // Verify the data with the RSA (PKCS #1 v1.5) signature.
- static bool ECVerify(const base::StringPiece data,
- const base::StringPiece sig);
-
- // Copy the component directory from a user controlled location to an
- // imageloader controlled location. Do not copy unless it verifies.
- static bool CopyComponentDirectory(const base::FilePath& component_path,
- const base::FilePath& destination_folder,
- const std::string& version);
- // Check the string contents to see if it matches the format of a
- // manifest.fingerprint file.
- static bool IsValidFingerprintFile(const std::string& contents);
- // Verify the imageloader.json manifest file and parse the file information
- // out of it.
- static bool VerifyAndParseManifest(const std::string& manifest_str,
- const std::string& signature,
- Manifest* manifest);
- // Copies files over and checks their hash in the process. The copy fails if
- // the hashes do not match.
- static bool CopyAndHashFile(const base::FilePath& src_path,
- const base::FilePath& dest_path,
- const std::vector<uint8_t>& known_hash);
- // Check if the client created a manifest.fingerprint, and preserve it.
- static bool CopyFingerprintFile(const base::FilePath& src,
- const base::FilePath& dest);
-
- // "mounts" keeps track of what has been mounted.
- // mounts = (name, (mount_point, device_path))
- std::map<std::string, std::pair<base::FilePath, base::FilePath>> mounts;
- // "reg" keeps track of registered components.
- // reg = (name, (version, fs_image_abs_path))
- std::map<std::string, std::pair<std::string, base::FilePath>> reg;
+ DISALLOW_COPY_AND_ASSIGN(ImageLoader);
};
} // namespace imageloader
diff --git a/imageloader.cc b/imageloader_impl.cc
similarity index 66%
rename from imageloader.cc
rename to imageloader_impl.cc
index 1e70ae8..8fa572e 100644
--- a/imageloader.cc
+++ b/imageloader_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "imageloader.h"
+#include "imageloader_impl.h"
#include <fcntl.h>
#include <linux/loop.h>
@@ -44,13 +44,6 @@
using imageloader::kBadResult;
-// Generate a good enough (unique) mount point.
-base::FilePath GenerateMountPoint(const char prefix[]) {
- return base::FilePath(prefix + base::GenerateGUID());
-}
-
-// The path where the components are stored on the device.
-const char kComponentsPath[] = "/mnt/stateful_partition/encrypted/imageloader";
// The name of the fingerprint file.
const char kFingerprintName[] = "manifest.fingerprint";
// The name of the imageloader manifest file.
@@ -78,22 +71,9 @@
// The maximum size of any file to read into memory.
const size_t kMaximumFilesize = 4096 * 10;
-// TODO(kerrnel): Switch to the prod keys before shipping this feature.
-const uint8_t kDevPublicKey[] = {
- 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
- 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
- 0x42, 0x00, 0x04, 0x7a, 0xaa, 0x2b, 0xf9, 0x3d, 0x7a, 0xbe, 0x35, 0x9a,
- 0xfc, 0x9f, 0x39, 0x2d, 0x2d, 0x37, 0x07, 0xd4, 0x19, 0x67, 0x67, 0x30,
- 0xbb, 0x5c, 0x74, 0x22, 0xd5, 0x02, 0x07, 0xaf, 0x6b, 0x12, 0x9d, 0x12,
- 0xf0, 0x34, 0xfd, 0x1a, 0x7f, 0x02, 0xd8, 0x46, 0x2b, 0x25, 0xca, 0xa0,
- 0x6e, 0x2b, 0x54, 0x41, 0xee, 0x92, 0xa2, 0x0f, 0xa2, 0x2a, 0xc0, 0x30,
- 0xa6, 0x8c, 0xd1, 0x16, 0x0a, 0x48, 0xca
-};
-
-bool AssertComponentDirPerms() {
+bool AssertComponentDirPerms(const base::FilePath& path) {
int mode;
- base::FilePath components_dir(kComponentsPath);
- if (!GetPosixFilePermissions(components_dir, &mode)) return false;
+ if (!GetPosixFilePermissions(path, &mode)) return false;
return mode == kComponentDirPerms;
}
@@ -123,16 +103,14 @@
// CRX. The file is used for delta updates. Since Chrome OS doesn't rely on it
// for security of the disk image, we are fine with sanity checking the contents
// and then preserving the unsigned file.
-// static
-bool ImageLoader::IsValidFingerprintFile(const std::string& contents) {
+bool ImageLoaderImpl::IsValidFingerprintFile(const std::string& contents) {
return contents.size() <= 256 &&
std::find_if_not(contents.begin(), contents.end(), [](char ch) {
return base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch) || ch == '.';
}) == contents.end();
}
-// static
-bool ImageLoader::CopyFingerprintFile(const base::FilePath& src,
+bool ImageLoaderImpl::CopyFingerprintFile(const base::FilePath& src,
const base::FilePath& dest) {
base::FilePath fingerprint_path = src.Append(kFingerprintName);
if (base::PathExists(fingerprint_path)) {
@@ -151,15 +129,15 @@
return true;
}
-bool ImageLoader::CopyAndHashFile(const base::FilePath& src_path,
- const base::FilePath& dest_path,
- const std::vector<uint8_t>& expected_hash) {
+bool ImageLoaderImpl::CopyAndHashFile(
+ const base::FilePath& src_path, const base::FilePath& dest_path,
+ const std::vector<uint8_t>& expected_hash) {
base::File file(src_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) return false;
- base::ScopedFD dest(HANDLE_EINTR(open(dest_path.value().c_str(),
- O_CREAT | O_WRONLY | O_EXCL,
- kComponentFilePerms)));
+ base::ScopedFD dest(
+ HANDLE_EINTR(open(dest_path.value().c_str(), O_CREAT | O_WRONLY | O_EXCL,
+ kComponentFilePerms)));
if (!dest.is_valid()) return false;
base::File out_file(dest.release());
@@ -171,7 +149,7 @@
size_t bytes_read = 0;
int rv = 0;
- char buf[4096];
+ char buf[4096];
do {
int remaining = size - bytes_read;
int bytes_to_read =
@@ -184,8 +162,7 @@
bytes_read += rv;
sha256->Update(buf, rv);
- if (out_file.WriteAtCurrentPos(buf, rv) != rv)
- return false;
+ if (out_file.WriteAtCurrentPos(buf, rv) != rv) return false;
} while (bytes_read <= size);
if (bytes_read != size) return false;
@@ -200,8 +177,7 @@
return true;
}
-// static
-bool ImageLoader::VerifyAndParseManifest(const std::string& manifest_contents,
+bool ImageLoaderImpl::VerifyAndParseManifest(const std::string& manifest_contents,
const std::string& signature,
Manifest* manifest) {
// Verify the manifest before trusting any of its contents.
@@ -271,8 +247,7 @@
return true;
}
-// static
-bool ImageLoader::CopyComponentDirectory(
+bool ImageLoaderImpl::CopyComponentDirectory(
const base::FilePath& component_path,
const base::FilePath& destination_folder, const std::string& version) {
if (mkdir(destination_folder.value().c_str(), kComponentDirPerms) != 0) {
@@ -339,16 +314,16 @@
return true;
}
-// static
-bool ImageLoader::ECVerify(const base::StringPiece data,
- const base::StringPiece sig) {
+bool ImageLoaderImpl::ECVerify(const base::StringPiece data,
+ const base::StringPiece sig) {
crypto::SignatureVerifier verifier;
- if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256,
- reinterpret_cast<const uint8_t*>(sig.data()),
- base::checked_cast<int>(sig.size()),
- reinterpret_cast<const uint8_t*>(kDevPublicKey),
- base::checked_cast<int>(sizeof(kDevPublicKey)))) {
+ if (!verifier.VerifyInit(
+ crypto::SignatureVerifier::ECDSA_SHA256,
+ reinterpret_cast<const uint8_t*>(sig.data()),
+ base::checked_cast<int>(sig.size()),
+ config_.key.data(),
+ base::checked_cast<int>(config_.key.size()))) {
return false;
}
@@ -359,98 +334,15 @@
}
// Mount component at location generated.
-std::string ImageLoader::LoadComponentUtil(const std::string& name) {
- base::FilePath mount_point = GenerateMountPoint("/mnt/");
- // Is this somehow taken up by any other name or mount?
- for (auto it = mounts.begin(); it != mounts.end(); ++it) {
- if ((it->second).first == mount_point) {
- return kBadResult;
- }
- }
- if (PathExists(mount_point)) {
- LOG(INFO) << "Generated mount_point is already stat-able : "
- << mount_point.value();
- return kBadResult;
- }
- // The mount point is not yet taken, so go ahead.
- base::ScopedFD loopctl_fd(open("/dev/loop-control", O_RDONLY | O_CLOEXEC));
- if (!loopctl_fd.is_valid()) {
- PLOG(ERROR) << "loopctl_fd";
- return kBadResult;
- }
- int device_free_number = ioctl(loopctl_fd.get(), LOOP_CTL_GET_FREE);
- if (device_free_number < 0) {
- PLOG(ERROR) << "ioctl : LOOP_CTL_GET_FREE";
- return kBadResult;
- }
- std::ostringstream device_path;
- device_path << "/dev/loop" << device_free_number;
- base::ScopedFD device_path_fd(
- open(device_path.str().c_str(), O_RDONLY | O_CLOEXEC));
- if (!device_path_fd.is_valid()) {
- PLOG(ERROR) << "device_path_fd";
- return kBadResult;
- }
- base::ScopedFD fs_image_fd(
- open(reg[name].second.value().c_str(), O_RDONLY | O_CLOEXEC));
- if (!fs_image_fd.is_valid()) {
- PLOG(ERROR) << "fs_image_fd";
- return kBadResult;
- }
- if (ioctl(device_path_fd.get(), LOOP_SET_FD, fs_image_fd.get()) < 0) {
- PLOG(ERROR) << "ioctl: LOOP_SET_FD";
- return kBadResult;
- }
- if (!base::CreateDirectory(mount_point)) {
- PLOG(ERROR) << "CreateDirectory : " << mount_point.value();
- ioctl(device_path_fd.get(), LOOP_CLR_FD, 0);
- return kBadResult;
- }
- if (mount(device_path.str().c_str(), mount_point.value().c_str(), "squashfs",
- MS_RDONLY | MS_NOSUID | MS_NODEV, "") < 0) {
- PLOG(ERROR) << "mount";
- ioctl(device_path_fd.get(), LOOP_CLR_FD, 0);
- return kBadResult;
- }
- mounts[name] = std::make_pair(mount_point, base::FilePath(device_path.str()));
- return mount_point.value();
+std::string ImageLoaderImpl::LoadComponentUtil(const std::string& name) {
+ // Not implemented yet.
+ return kBadResult;
}
-// Unmount the given component.
-bool ImageLoader::UnloadComponentUtil(const std::string& name) {
- std::string device_path = mounts[name].second.value();
- if (umount(mounts[name].first.value().c_str()) < 0) {
- PLOG(ERROR) << "umount";
- return false;
- }
- const base::FilePath fp_mount_point(mounts[name].first);
- if (!DeleteFile(fp_mount_point, false)) {
- PLOG(ERROR) << "DeleteFile : " << fp_mount_point.value();
- return false;
- }
- base::ScopedFD device_path_fd(
- open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
- if (!device_path_fd.is_valid()) {
- PLOG(ERROR) << "device_path_fd";
- return false;
- }
- if (ioctl(device_path_fd.get(), LOOP_CLR_FD, 0) < 0) {
- PLOG(ERROR) << "ioctl: LOOP_CLR_FD";
- return false;
- }
- mounts.erase(mounts.find(name));
- return true;
-}
-
-// Following functions are required directly for the DBus functionality.
-
-ImageLoader::ImageLoader(DBus::Connection* conn)
- : DBus::ObjectAdaptor(*conn, kImageLoaderPath) {}
-
-bool ImageLoader::RegisterComponent(
+bool ImageLoaderImpl::RegisterComponent(
const std::string& name, const std::string& version,
- const std::string& component_folder_abs_path, ::DBus::Error& err) {
- base::FilePath components_dir(kComponentsPath);
+ const std::string& component_folder_abs_path) {
+ base::FilePath components_dir(config_.storage_dir);
if (!base::PathExists(components_dir)) {
if (mkdir(components_dir.value().c_str(), kComponentDirPerms) != 0) {
PLOG(ERROR) << "Could not create the ImageLoader components directory.";
@@ -458,7 +350,7 @@
}
}
- if (!AssertComponentDirPerms()) return false;
+ if (!AssertComponentDirPerms(config_.storage_dir)) return false;
base::FilePath component_root = components_dir.Append(name);
@@ -518,9 +410,9 @@
return true;
}
-std::string ImageLoader::GetComponentVersion(const std::string& name,
- ::DBus::Error& err) {
- base::FilePath component_path = base::FilePath(kComponentsPath).Append(name);
+std::string ImageLoaderImpl::GetComponentVersion(const std::string& name) {
+ base::FilePath component_path =
+ base::FilePath(config_.storage_dir).Append(name);
if (!base::PathExists(component_path)) {
LOG(ERROR) << "Component does not exist.";
return kBadResult;
@@ -533,10 +425,11 @@
return kBadResult;
}
+ base::FilePath versioned_path = component_path.Append(current_version_hint);
// The version can be security sensitive (i.e. which flash player does Chrome
// load), so check the signed manfiest as the final answer.
std::string manifest_contents;
- if (!base::ReadFileToStringWithMaxSize(component_path.Append(kManifestName),
+ if (!base::ReadFileToStringWithMaxSize(versioned_path.Append(kManifestName),
&manifest_contents,
kMaximumFilesize)) {
return kBadResult;
@@ -545,7 +438,7 @@
// Read in the manifest signature.
std::string manifest_sig;
base::FilePath manifest_sig_path =
- component_path.Append(kManifestSignatureName);
+ versioned_path.Append(kManifestSignatureName);
if (!base::ReadFileToStringWithMaxSize(manifest_sig_path, &manifest_sig,
kMaximumFilesize)) {
return kBadResult;
@@ -561,32 +454,14 @@
: kBadResult;
}
-std::string ImageLoader::LoadComponent(const std::string& name,
- ::DBus::Error& err) {
- if (reg.find(name) != reg.end()) {
- if (mounts.find(name) != mounts.end()) {
- LOG(ERROR) << "Already mounted at " << mounts[name].first.value() << ".";
- return kBadResult;
- }
- std::string mount_point = LoadComponentUtil(name);
- if (mount_point == kBadResult) {
- LOG(ERROR) << "Unable to mount : " << mount_point;
- return kBadResult;
- }
- LOG(INFO) << "Mounted successfully at " << mount_point << ".";
- return mount_point;
+std::string ImageLoaderImpl::LoadComponent(const std::string& name) {
+ std::string mount_point = LoadComponentUtil(name);
+ if (mount_point == kBadResult) {
+ LOG(ERROR) << "Unable to mount : " << mount_point;
+ return kBadResult;
}
- LOG(ERROR) << "Entry not found : " << name;
- return kBadResult;
-}
-
-bool ImageLoader::UnloadComponent(const std::string& name, ::DBus::Error& err) {
- if (UnloadComponentUtil(name)) {
- LOG(INFO) << "Unmount " << name << " successful.";
- return true;
- }
- LOG(ERROR) << "Unmount " << name << " unsucessful.";
- return false;
+ LOG(INFO) << "Mounted successfully at " << mount_point << ".";
+ return mount_point;
}
} // namespace imageloader
diff --git a/imageloader_impl.h b/imageloader_impl.h
new file mode 100644
index 0000000..60640ac
--- /dev/null
+++ b/imageloader_impl.h
@@ -0,0 +1,95 @@
+// Copyright 2016 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 IMAGELOADER_IMAGELOADER_UTILITY_H_
+#define IMAGELOADER_IMAGELOADER_UTILITY_H_
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/gtest_prod_util.h>
+#include <base/macros.h>
+
+namespace imageloader {
+
+struct ImageLoaderConfig {
+ ImageLoaderConfig(const std::vector<uint8_t> key, const char* path)
+ : key(key), storage_dir(path) {}
+ std::vector<uint8_t> key;
+ base::FilePath storage_dir;
+};
+
+class ImageLoaderImpl {
+ public:
+ // Instantiate an object with a configuration object.
+ explicit ImageLoaderImpl(const ImageLoaderConfig& config) : config_(config) {}
+
+ // Register a component.
+ bool RegisterComponent(const std::string& name, const std::string& version,
+ const std::string& component_folder_abs_path);
+
+ // Get component version given component name.
+ std::string GetComponentVersion(const std::string& name);
+
+ // Load the specified component.
+ std::string LoadComponent(const std::string& name);
+
+ private:
+ // This is a parsed version of the imageloader.json manifest.
+ struct Manifest {
+ int manifest_version;
+ std::vector<uint8_t> image_sha256;
+ std::vector<uint8_t> params_sha256;
+ std::string version;
+ };
+
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, ECVerify);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, ManifestFingerPrint);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyValidComponent);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyComponentWithBadManifest);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyValidImage);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyInvalidImage);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, CopyInvalidHash);
+ FRIEND_TEST_ALL_PREFIXES(ImageLoaderTest, ParseManifest);
+
+ // Do the work to verify and mount components.
+ std::string LoadComponentUtil(const std::string& name);
+
+ // Verify the data with the RSA (PKCS #1 v1.5) signature.
+ bool ECVerify(const base::StringPiece data, const base::StringPiece sig);
+
+ // Copy the component directory from a user controlled location to an
+ // imageloader controlled location. Do not copy unless it verifies.
+ bool CopyComponentDirectory(const base::FilePath& component_path,
+ const base::FilePath& destination_folder,
+ const std::string& version);
+
+ // Check the string contents to see if it matches the format of a
+ // manifest.fingerprint file.
+ bool IsValidFingerprintFile(const std::string& contents);
+
+ // Verify the imageloader.json manifest file and parse the file information
+ // out of it.
+ bool VerifyAndParseManifest(const std::string& manifest_str,
+ const std::string& signature, Manifest* manifest);
+
+ // Copies files over and checks their hash in the process. The copy fails if
+ // the hashes do not match.
+ bool CopyAndHashFile(const base::FilePath& src_path,
+ const base::FilePath& dest_path,
+ const std::vector<uint8_t>& known_hash);
+
+ // Check if the client created a manifest.fingerprint, and preserve it.
+ bool CopyFingerprintFile(const base::FilePath& src,
+ const base::FilePath& dest);
+
+ // The configuration traits.
+ ImageLoaderConfig config_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageLoaderImpl);
+};
+
+} // namespace imageloader
+
+#endif // IMAGELOADER_IMAGELOADER_UTILITY_H_
diff --git a/imageloader_main.cc b/imageloader_main.cc
index de9cf57..cd863fa 100644
--- a/imageloader_main.cc
+++ b/imageloader_main.cc
@@ -9,6 +9,20 @@
#include "imageloader.h"
#include "imageloader_common.h"
+// TODO(kerrnel): Switch to the prod keys before shipping this feature.
+const uint8_t kDevPublicKey[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00, 0x04, 0x7a, 0xaa, 0x2b, 0xf9, 0x3d, 0x7a, 0xbe, 0x35, 0x9a,
+ 0xfc, 0x9f, 0x39, 0x2d, 0x2d, 0x37, 0x07, 0xd4, 0x19, 0x67, 0x67, 0x30,
+ 0xbb, 0x5c, 0x74, 0x22, 0xd5, 0x02, 0x07, 0xaf, 0x6b, 0x12, 0x9d, 0x12,
+ 0xf0, 0x34, 0xfd, 0x1a, 0x7f, 0x02, 0xd8, 0x46, 0x2b, 0x25, 0xca, 0xa0,
+ 0x6e, 0x2b, 0x54, 0x41, 0xee, 0x92, 0xa2, 0x0f, 0xa2, 0x2a, 0xc0, 0x30,
+ 0xa6, 0x8c, 0xd1, 0x16, 0x0a, 0x48, 0xca};
+
+// The path where the components are stored on the device.
+const char kComponentsPath[] = "/mnt/stateful_partition/encrypted/imageloader";
+
int main(int argc, char** argv) {
signal(SIGTERM, imageloader::OnQuit);
signal(SIGINT, imageloader::OnQuit);
@@ -23,7 +37,11 @@
DBus::default_dispatcher = &dispatcher;
DBus::Connection conn = DBus::Connection::SystemBus();
conn.request_name(imageloader::kImageLoaderName);
- imageloader::ImageLoader helper(&conn);
+
+ std::vector<uint8_t> key(std::begin(kDevPublicKey), std::end(kDevPublicKey));
+
+ imageloader::ImageLoaderConfig config(key, kComponentsPath);
+ imageloader::ImageLoader helper(&conn, config);
if (FLAGS_o) {
dispatcher.dispatch_pending();
diff --git a/imageloader_unittest.cc b/imageloader_unittest.cc
index 7814561..2e00544 100644
--- a/imageloader_unittest.cc
+++ b/imageloader_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 "imageloader_impl.h"
+
#include <dirent.h>
#include <stdlib.h>
@@ -11,7 +13,6 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "imageloader.h"
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
@@ -28,6 +29,17 @@
namespace {
+// Test data always uses the dev key.
+const uint8_t kDevPublicKey[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00, 0x04, 0x7a, 0xaa, 0x2b, 0xf9, 0x3d, 0x7a, 0xbe, 0x35, 0x9a,
+ 0xfc, 0x9f, 0x39, 0x2d, 0x2d, 0x37, 0x07, 0xd4, 0x19, 0x67, 0x67, 0x30,
+ 0xbb, 0x5c, 0x74, 0x22, 0xd5, 0x02, 0x07, 0xaf, 0x6b, 0x12, 0x9d, 0x12,
+ 0xf0, 0x34, 0xfd, 0x1a, 0x7f, 0x02, 0xd8, 0x46, 0x2b, 0x25, 0xca, 0xa0,
+ 0x6e, 0x2b, 0x54, 0x41, 0xee, 0x92, 0xa2, 0x0f, 0xa2, 0x2a, 0xc0, 0x30,
+ 0xa6, 0x8c, 0xd1, 0x16, 0x0a, 0x48, 0xca};
+
const char kImageLoaderJSON[] =
"{\"image-sha256-hash\":"
"\"71D11CA4E2B4A3F5E71D789F0E64116F49BB13DE6A591505CA6404985E13F6EF\","
@@ -44,19 +56,36 @@
0xe8, 0xed, 0x6a, 0x45, 0x38, 0x53, 0x54, 0xd2, 0xb1, 0x97};
const char kImageLoaderBadSig[] = {
- 0x30, 0x44, 0x02, 0x20, 0x0a, 0x75, 0x49, 0xaf, 0x01, 0x3b, 0x48, 0x51,
- 0x45, 0x74, 0x8b, 0x41, 0x64, 0x21, 0x83, 0xce, 0xf1, 0x78, 0x1d, 0xd0,
- 0xa8, 0xd6, 0xae, 0x84, 0xf3, 0xc1, 0x3c, 0x3a, 0xee, 0xb4, 0x35, 0xb7,
- 0x02, 0x20, 0x34, 0xeb, 0xdc, 0x68, 0x2d, 0x8b, 0x4f, 0x64, 0x94, 0x64,
- 0xa3, 0xd5, 0xde, 0xab, 0xf9, 0xa0, 0xbd, 0xcc, 0xc1, 0x2f, 0x78, 0xd4,
- 0xe8, 0xed, 0x6a, 0x45, 0x38, 0x53, 0x54, 0xd2, 0xb1, 0x97
-};
+ 0x30, 0x44, 0x02, 0x20, 0x0a, 0x75, 0x49, 0xaf, 0x01, 0x3b, 0x48, 0x51,
+ 0x45, 0x74, 0x8b, 0x41, 0x64, 0x21, 0x83, 0xce, 0xf1, 0x78, 0x1d, 0xd0,
+ 0xa8, 0xd6, 0xae, 0x84, 0xf3, 0xc1, 0x3c, 0x3a, 0xee, 0xb4, 0x35, 0xb7,
+ 0x02, 0x20, 0x34, 0xeb, 0xdc, 0x68, 0x2d, 0x8b, 0x4f, 0x64, 0x94, 0x64,
+ 0xa3, 0xd5, 0xde, 0xab, 0xf9, 0xa0, 0xbd, 0xcc, 0xc1, 0x2f, 0x78, 0xd4,
+ 0xe8, 0xed, 0x6a, 0x45, 0x38, 0x53, 0x54, 0xd2, 0xb1, 0x97};
+
+// This is the name of the component used in the test data.
+const char kTestComponentName[] = "PepperFlashPlayer";
+// This is the version of flash player used in the test data.
+const char kTestDataVersion[] = "22.0.0.158";
+// This is the version of the updated flash player in the test data.
+const char kTestUpdatedVersion[] = "22.0.0.256";
} // namespace {}
class ImageLoaderTest : public testing::Test {
public:
+ ImageLoaderConfig GetConfig(const char* path) {
+ std::vector<uint8_t> key(std::begin(kDevPublicKey),
+ std::end(kDevPublicKey));
+ ImageLoaderConfig config(key, path);
+ return config;
+ }
+
base::FilePath GetComponentPath() {
+ return GetComponentPath(kTestDataVersion);
+ }
+
+ base::FilePath GetComponentPath(const std::string& version) {
const char* src_dir = getenv("CROS_WORKON_SRCROOT");
CHECK(src_dir != nullptr);
base::FilePath component_path(src_dir);
@@ -65,7 +94,7 @@
.Append("platform")
.Append("imageloader")
.Append("test")
- .Append("22.0.0.158_chromeos_intel64_PepperFlashPlayer");
+ .Append(version + "_chromeos_intel64_PepperFlashPlayer");
return component_path;
}
@@ -78,28 +107,97 @@
}
};
+// Test the RegisterComponent public interface.
+TEST_F(ImageLoaderTest, RegisterComponentAndGetVersion) {
+ base::ScopedTempDir scoped_temp_dir;
+ ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+ const base::FilePath& temp_dir = scoped_temp_dir.path();
+ // Delete the directory so that the ImageLoader can recreate it with the
+ // correct permissions.
+ base::DeleteFile(temp_dir, /*recursive=*/true);
+
+ ImageLoaderImpl loader(GetConfig(temp_dir.value().c_str()));
+ ASSERT_TRUE(loader.RegisterComponent(kTestComponentName, kTestDataVersion,
+ GetComponentPath().value()));
+
+ base::FilePath comp_dir = temp_dir.Append(kTestComponentName);
+ ASSERT_TRUE(base::DirectoryExists(comp_dir));
+
+ base::FilePath hint_file = comp_dir.Append(kTestComponentName);
+ ASSERT_TRUE(base::PathExists(hint_file));
+
+ std::string hint_file_contents;
+ ASSERT_TRUE(
+ base::ReadFileToStringWithMaxSize(hint_file, &hint_file_contents, 4096));
+ EXPECT_EQ(kTestDataVersion, hint_file_contents);
+
+ base::FilePath version_dir = comp_dir.Append(kTestDataVersion);
+ ASSERT_TRUE(base::DirectoryExists(version_dir));
+
+ std::list<std::string> files;
+ GetFilesInDir(version_dir, &files);
+ EXPECT_THAT(files, testing::UnorderedElementsAre(
+ "imageloader.json", "imageloader.sig.1", "params",
+ "image.squash", "manifest.fingerprint"));
+
+ // Reject a component if the version already exists.
+ EXPECT_FALSE(loader.RegisterComponent(kTestComponentName, kTestDataVersion,
+ GetComponentPath().value()));
+
+ EXPECT_EQ(kTestDataVersion, loader.GetComponentVersion(kTestComponentName));
+
+ // Now copy a new version into place.
+ EXPECT_TRUE(loader.RegisterComponent(kTestComponentName, kTestUpdatedVersion,
+ GetComponentPath(kTestUpdatedVersion).value()));
+
+ std::string hint_file_contents2;
+ ASSERT_TRUE(
+ base::ReadFileToStringWithMaxSize(hint_file, &hint_file_contents2, 4096));
+ EXPECT_EQ(kTestUpdatedVersion, hint_file_contents2);
+
+ base::FilePath version_dir2 = comp_dir.Append(kTestUpdatedVersion);
+ ASSERT_TRUE(base::DirectoryExists(version_dir2));
+
+ std::list<std::string> files2;
+ GetFilesInDir(version_dir2, &files2);
+ EXPECT_THAT(files2, testing::UnorderedElementsAre("imageloader.json",
+ "imageloader.sig.1",
+ "params", "image.squash"));
+
+ EXPECT_EQ(kTestUpdatedVersion, loader.GetComponentVersion(kTestComponentName));
+
+ // Reject rollback to an older version.
+ EXPECT_FALSE(loader.RegisterComponent(kTestComponentName, kTestDataVersion,
+ GetComponentPath().value()));
+
+ EXPECT_EQ(kTestUpdatedVersion, loader.GetComponentVersion(kTestComponentName));
+}
+
TEST_F(ImageLoaderTest, ECVerify) {
- EXPECT_TRUE(ImageLoader::ECVerify(
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
+ EXPECT_TRUE(loader.ECVerify(
base::StringPiece(kImageLoaderJSON),
base::StringPiece(kImageLoaderSig, sizeof(kImageLoaderSig))));
- EXPECT_FALSE(ImageLoader::ECVerify(
+ EXPECT_FALSE(loader.ECVerify(
base::StringPiece(kImageLoaderJSON),
base::StringPiece(kImageLoaderBadSig, sizeof(kImageLoaderBadSig))));
}
TEST_F(ImageLoaderTest, ManifestFingerPrint) {
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
const std::string valid_manifest =
"1.3464353b1ed78574e05f3ffe84b52582572b2fe7202f3824a3761e54ace8bb1";
- EXPECT_TRUE(ImageLoader::IsValidFingerprintFile(valid_manifest));
+ EXPECT_TRUE(loader.IsValidFingerprintFile(valid_manifest));
const std::string invalid_unicode_manifest = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ ";
- EXPECT_FALSE(ImageLoader::IsValidFingerprintFile(invalid_unicode_manifest));
+ EXPECT_FALSE(loader.IsValidFingerprintFile(invalid_unicode_manifest));
- EXPECT_FALSE(ImageLoader::IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
+ EXPECT_FALSE(loader.IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
}
TEST_F(ImageLoaderTest, CopyValidComponent) {
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
const int image_size = 4096 * 4;
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
@@ -107,8 +205,8 @@
std::vector<char> image(image_size, 0xBB);
base::FilePath component_dest = temp_dir.Append("copied-component");
- ASSERT_TRUE(ImageLoader::CopyComponentDirectory(
- GetComponentPath(), component_dest, "22.0.0.158"));
+ ASSERT_TRUE(loader.CopyComponentDirectory(GetComponentPath(), component_dest,
+ kTestDataVersion));
// Check that all the files are present, except for the manifest.json which
// should be discarded.
@@ -141,8 +239,9 @@
const char data[] = "c";
ASSERT_TRUE(base::AppendToFile(manifest, data, sizeof(data)));
- EXPECT_FALSE(ImageLoader::CopyComponentDirectory(
- bad_component_dir, temp_dir.Append("copied-component"), "22.0.0.158"));
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
+ EXPECT_FALSE(loader.CopyComponentDirectory(
+ bad_component_dir, temp_dir.Append("copied-component"), kTestDataVersion));
}
TEST_F(ImageLoaderTest, CopyValidImage) {
@@ -165,8 +264,9 @@
sha256->Update(image.data(), image.size());
sha256->Finish(hash.data(), hash.size());
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
base::FilePath image_dest = temp_dir.Append("image.copied");
- ASSERT_TRUE(ImageLoader::CopyAndHashFile(image_path, image_dest, hash));
+ ASSERT_TRUE(loader.CopyAndHashFile(image_path, image_dest, hash));
// Check if the image file actually exists and has the correct contents.
std::string resulting_image;
@@ -194,29 +294,28 @@
std::vector<char> file(image_size, 0xAA);
ASSERT_EQ(image_size, base::WriteFile(image_src, file.data(), image_size));
- EXPECT_FALSE(ImageLoader::CopyAndHashFile(image_src, image_dest, hash));
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
+ EXPECT_FALSE(loader.CopyAndHashFile(image_src, image_dest, hash));
}
TEST_F(ImageLoaderTest, ParseManifest) {
- ImageLoader::Manifest manifest;
- ASSERT_TRUE(ImageLoader::VerifyAndParseManifest(kImageLoaderJSON,
- kImageLoaderSig,
- &manifest));
+ ImageLoaderImpl loader(GetConfig("/nonexistant"));
+ ImageLoaderImpl::Manifest manifest;
+ ASSERT_TRUE(loader.VerifyAndParseManifest(kImageLoaderJSON, kImageLoaderSig,
+ &manifest));
EXPECT_EQ(1, manifest.manifest_version);
- EXPECT_EQ("22.0.0.158", manifest.version);
+ EXPECT_EQ(kTestDataVersion, manifest.version);
EXPECT_EQ(32, manifest.image_sha256.size());
EXPECT_EQ(32, manifest.params_sha256.size());
std::string bad_manifest = "{\"foo\":\"128.0.0.9\"}";
- ImageLoader::Manifest manifest2;
- EXPECT_FALSE(ImageLoader::VerifyAndParseManifest(bad_manifest,
- kImageLoaderSig,
- &manifest2));
+ ImageLoaderImpl::Manifest manifest2;
+ EXPECT_FALSE(
+ loader.VerifyAndParseManifest(bad_manifest, kImageLoaderSig, &manifest2));
- ImageLoader::Manifest manifest3;
- EXPECT_FALSE(ImageLoader::VerifyAndParseManifest(kImageLoaderJSON,
- kImageLoaderBadSig,
- &manifest3));
+ ImageLoaderImpl::Manifest manifest3;
+ EXPECT_FALSE(loader.VerifyAndParseManifest(kImageLoaderJSON,
+ kImageLoaderBadSig, &manifest3));
}
-}
+} // namespace imageloader
diff --git a/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/image.squash b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/image.squash
new file mode 100644
index 0000000..8f18ea7
--- /dev/null
+++ b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/image.squash
Binary files differ
diff --git a/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/imageloader.json b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/imageloader.json
new file mode 100644
index 0000000..a143fb7
--- /dev/null
+++ b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/imageloader.json
@@ -0,0 +1 @@
+{"image-sha256-hash":"2470C8EBE59C532535FB9F02BC900A02434B356559FBF6373B9428BF241C11C5","version":"22.0.0.256","params-sha256-hash":"D42F5F340A13ED293EBD7E7C753BCA4EA4D471DB78D97906EE4587D04695083B","manifest-version":1}
\ No newline at end of file
diff --git a/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/imageloader.sig.1 b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/imageloader.sig.1
new file mode 100644
index 0000000..437c754
--- /dev/null
+++ b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/imageloader.sig.1
@@ -0,0 +1 @@
+0D Emy¨>ÕInÝÍð¨ö)|.gûJ9í®·ÔýKu DºÑo2Y¥¤ïÄæô30ËÙ¨éÙ¦ï:Uuâ
\ No newline at end of file
diff --git a/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/manifest.json b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/manifest.json
new file mode 100644
index 0000000..4677d41
--- /dev/null
+++ b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/manifest.json
@@ -0,0 +1,5 @@
+{
+ "manifest_version": 2,
+ "name": "PepperFlashPlayer",
+ "version": "22.0.0.256"
+}
\ No newline at end of file
diff --git a/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/params b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/params
new file mode 100644
index 0000000..eb22597
--- /dev/null
+++ b/test/22.0.0.256_chromeos_intel64_PepperFlashPlayer/params
@@ -0,0 +1 @@
+0 24 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=24 alg=sha256 root_hexdigest=fcd810fe1c1849f636334b18ca7bfd931d895e7a0d4661da32751e61507c772d salt=9893b9f2b4ae18dde95c417a0be00530c6fe036205ed6fe98840c7aa02430ece