blob: 78145616f539e334cc43954f09f82aec0ded05b8 [file] [log] [blame]
// 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.
#include <dirent.h>
#include <stdlib.h>
#include <algorithm>
#include <list>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "imageloader.h"
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/numerics/safe_conversions.h>
#include <base/strings/string_number_conversions.h>
#include <base/values.h>
#include <crypto/secure_hash.h>
#include <crypto/sha2.h>
namespace imageloader {
namespace {
const char kImageLoaderJSON[] =
"{\"image-sha256-hash\":"
"\"71D11CA4E2B4A3F5E71D789F0E64116F49BB13DE6A591505CA6404985E13F6EF\","
"\"version\":\"22.0.0.158\",\"params-sha256-hash\":"
"\"40608D72852DBD223B159FE149CEEE8F8865D46AB28557C2955BC1F02FFECCA7\","
"\"manifest-version\":1}";
const char kImageLoaderSig[] = {
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, 0xc0, 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};
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
};
} // namespace {}
class ImageLoaderTest : public testing::Test {
public:
base::FilePath GetComponentPath() {
const char* src_dir = getenv("CROS_WORKON_SRCROOT");
CHECK(src_dir != nullptr);
base::FilePath component_path(src_dir);
component_path =
component_path.Append("src")
.Append("platform")
.Append("imageloader")
.Append("test")
.Append("22.0.0.158_chromeos_intel64_PepperFlashPlayer");
return component_path;
}
void GetFilesInDir(const base::FilePath& dir, std::list<std::string>* files) {
base::FileEnumerator file_enum(dir, false, base::FileEnumerator::FILES);
for (base::FilePath name = file_enum.Next(); !name.empty();
name = file_enum.Next()) {
files->emplace_back(name.BaseName().value());
}
}
};
TEST_F(ImageLoaderTest, ECVerify) {
EXPECT_TRUE(ImageLoader::ECVerify(
base::StringPiece(kImageLoaderJSON),
base::StringPiece(kImageLoaderSig, sizeof(kImageLoaderSig))));
EXPECT_FALSE(ImageLoader::ECVerify(
base::StringPiece(kImageLoaderJSON),
base::StringPiece(kImageLoaderBadSig, sizeof(kImageLoaderBadSig))));
}
TEST_F(ImageLoaderTest, ManifestFingerPrint) {
const std::string valid_manifest =
"1.3464353b1ed78574e05f3ffe84b52582572b2fe7202f3824a3761e54ace8bb1";
EXPECT_TRUE(ImageLoader::IsValidFingerprintFile(valid_manifest));
const std::string invalid_unicode_manifest = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ ";
EXPECT_FALSE(ImageLoader::IsValidFingerprintFile(invalid_unicode_manifest));
EXPECT_FALSE(ImageLoader::IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
}
TEST_F(ImageLoaderTest, CopyValidComponent) {
const int image_size = 4096 * 4;
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath& temp_dir = scoped_temp_dir.path();
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"));
// Check that all the files are present, except for the manifest.json which
// should be discarded.
std::list<std::string> original_files;
std::list<std::string> copied_files;
GetFilesInDir(GetComponentPath(), &original_files);
GetFilesInDir(component_dest, &copied_files);
EXPECT_THAT(original_files,
testing::UnorderedElementsAre(
"imageloader.json", "imageloader.sig.1", "manifest.json",
"params", "image.squash", "manifest.fingerprint"));
EXPECT_THAT(copied_files,
testing::UnorderedElementsAre(
"imageloader.json", "imageloader.sig.1", "params",
"image.squash", "manifest.fingerprint"));
}
TEST_F(ImageLoaderTest, CopyComponentWithBadManifest) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath& temp_dir = scoped_temp_dir.path();
base::FilePath bad_component_dir = temp_dir.Append("bad-component");
ASSERT_TRUE(base::CopyDirectory(GetComponentPath(), bad_component_dir,
true /* recursive */));
base::FilePath manifest = bad_component_dir.Append("imageloader.json");
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"));
}
TEST_F(ImageLoaderTest, CopyValidImage) {
const int image_size = 4096 * 4;
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath& temp_dir = scoped_temp_dir.path();
base::FilePath image_path = temp_dir.Append("image");
std::vector<char> image(image_size,
0xBB); // large enough to test streaming read.
ASSERT_TRUE(base::WriteFile(image_path, image.data(), image.size()) ==
image_size);
std::vector<uint8_t> hash(crypto::kSHA256Length);
std::unique_ptr<crypto::SecureHash> sha256(
crypto::SecureHash::Create(crypto::SecureHash::SHA256));
sha256->Update(image.data(), image.size());
sha256->Finish(hash.data(), hash.size());
base::FilePath image_dest = temp_dir.Append("image.copied");
ASSERT_TRUE(ImageLoader::CopyAndHashFile(image_path, image_dest, hash));
// Check if the image file actually exists and has the correct contents.
std::string resulting_image;
ASSERT_TRUE(base::ReadFileToStringWithMaxSize(image_dest, &resulting_image,
image_size));
EXPECT_TRUE(memcmp(image.data(), resulting_image.data(), image_size) == 0);
}
TEST_F(ImageLoaderTest, CopyInvalidImage) {
const int image_size = 4096 * 4;
// It doesn't matter what the hash is, because this is testing a mismatch.
std::string hash_str =
"5342065E5D9889739B281D96FD985270A13F2B68A29DD47142ABFA0C2C659AA1";
std::vector<uint8_t> hash;
ASSERT_TRUE(base::HexStringToBytes(hash_str, &hash));
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath& temp_dir = scoped_temp_dir.path();
base::FilePath image_src = temp_dir.Append("bad_image.squash");
base::FilePath image_dest = temp_dir.Append("image.squash");
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));
}
TEST_F(ImageLoaderTest, ParseManifest) {
ImageLoader::Manifest manifest;
ASSERT_TRUE(ImageLoader::VerifyAndParseManifest(kImageLoaderJSON,
kImageLoaderSig,
&manifest));
EXPECT_EQ(1, manifest.manifest_version);
EXPECT_EQ("22.0.0.158", 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));
ImageLoader::Manifest manifest3;
EXPECT_FALSE(ImageLoader::VerifyAndParseManifest(kImageLoaderJSON,
kImageLoaderBadSig,
&manifest3));
}
}