blob: e49a472ea4bd9f153014cc7b2128976d4aabd86f [file] [log] [blame]
Greg Kerr019d59c2016-11-17 14:28:49 -08001// Copyright 2016 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Chan045849f2017-12-18 17:27:07 -08005#include "imageloader/component.h"
Greg Kerr019d59c2016-11-17 14:28:49 -08006
7#include <stdint.h>
8
9#include <list>
Eric Carusocbe1c5c2017-03-15 14:21:08 -070010#include <memory>
Greg Kerr019d59c2016-11-17 14:28:49 -080011#include <string>
12#include <vector>
13
Greg Kerr019d59c2016-11-17 14:28:49 -080014#include <base/files/file_path.h>
15#include <base/files/file_util.h>
16#include <base/files/scoped_temp_dir.h>
17#include <base/logging.h>
Greg Kerr019d59c2016-11-17 14:28:49 -080018#include <crypto/secure_hash.h>
19#include <crypto/sha2.h>
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
Ben Chan045849f2017-12-18 17:27:07 -080023#include "imageloader/imageloader_impl.h"
24#include "imageloader/mock_helper_process.h"
25#include "imageloader/test_utilities.h"
Eric Caruso0b79bc82017-03-21 13:44:34 -070026
Greg Kerr019d59c2016-11-17 14:28:49 -080027namespace imageloader {
28
29using testing::_;
30
31class ComponentTest : public testing::Test {
32 public:
33 ComponentTest() {
Eric Caruso0b79bc82017-03-21 13:44:34 -070034 keys_.push_back(std::vector<uint8_t>(std::begin(kDevPublicKey),
35 std::end(kDevPublicKey)));
Greg Kerr019d59c2016-11-17 14:28:49 -080036 CHECK(scoped_temp_dir_.CreateUniqueTempDir());
37 temp_dir_ = scoped_temp_dir_.path();
38 CHECK(base::SetPosixFilePermissions(temp_dir_, kComponentDirPerms));
39 }
40
41 bool TestCopyWithCorruptFile(const std::string& component_name,
42 const std::string& file_name) {
43 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
44 if (!base::CreateDirectory(bad_component_dir)) return false;
45 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
46 return false;
47
Eric Carusocbe1c5c2017-03-15 14:21:08 -070048 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070049 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070050 if (!component || !component->CopyTo(bad_component_dir))
Greg Kerr019d59c2016-11-17 14:28:49 -080051 return false;
52
Eric Carusocbe1c5c2017-03-15 14:21:08 -070053 std::unique_ptr<Component> bad_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070054 Component::Create(bad_component_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070055 if (!bad_component) return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080056
57 base::FilePath file = bad_component_dir.Append(file_name);
58 const char data[] = "c";
59 if (!base::AppendToFile(file, data, sizeof(data))) return false;
60
61 base::FilePath bad_component_dest =
62 temp_dir_.Append(component_name + "invalid");
63 if (!base::CreateDirectory(bad_component_dest)) return false;
64
65 if (!base::SetPosixFilePermissions(bad_component_dest, kComponentDirPerms))
66 return false;
Eric Carusocbe1c5c2017-03-15 14:21:08 -070067 return bad_component->CopyTo(bad_component_dest) == false;
Greg Kerr019d59c2016-11-17 14:28:49 -080068 }
69
70 bool TestInitComponentWithCorruptFile(const std::string& component_name,
71 const std::string& file_name) {
72 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
73 if (!base::CreateDirectory(bad_component_dir)) return false;
74 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
75 return false;
76
Eric Carusocbe1c5c2017-03-15 14:21:08 -070077 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070078 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070079 if (!component || !component->CopyTo(bad_component_dir))
Greg Kerr019d59c2016-11-17 14:28:49 -080080 return false;
81
82 base::FilePath file = bad_component_dir.Append(file_name);
83 // Read the file out and change the last byte.
84 std::string file_contents;
85 if (!base::ReadFileToString(file, &file_contents)) return false;
86 file_contents[file_contents.size() - 1] =
87 ~file_contents[file_contents.size() - 1];
88
89 if (!base::WriteFile(file, file_contents.data(), file_contents.size())) {
90 return false;
91 }
92
Eric Carusocbe1c5c2017-03-15 14:21:08 -070093 std::unique_ptr<Component> bad_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070094 Component::Create(bad_component_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070095 return bad_component == nullptr;
Greg Kerr019d59c2016-11-17 14:28:49 -080096 }
97
98 bool CompareFileContents(const base::FilePath& src,
99 const base::FilePath& dest,
100 const std::list<std::string>& filenames) {
101 for (const std::string& name : filenames) {
102 std::string source_file_contents;
103 std::string dest_file_contents;
104 if (!base::ReadFileToString(src.Append(name), &source_file_contents))
105 return false;
106 if (!base::ReadFileToString(dest.Append(name), &dest_file_contents))
107 return false;
108 if (source_file_contents != dest_file_contents) {
109 LOG(ERROR) << "File contents does not match for file: " << name;
110 return false;
111 }
112 }
113 return true;
114 }
115
Eric Caruso0b79bc82017-03-21 13:44:34 -0700116 Keys keys_;
Greg Kerr019d59c2016-11-17 14:28:49 -0800117 base::ScopedTempDir scoped_temp_dir_;
118 base::FilePath temp_dir_;
119};
120
121TEST_F(ComponentTest, InitComponentAndCheckManifest) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700122 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700123 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700124 ASSERT_NE(nullptr, component);
125
126 EXPECT_EQ(1, component->manifest().manifest_version);
127 EXPECT_EQ(kTestDataVersion, component->manifest().version);
Greg Kerr019d59c2016-11-17 14:28:49 -0800128 // Don't hardcode the sha256 hashes, but run some sanity checks.
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700129 EXPECT_EQ(crypto::kSHA256Length, component->manifest().image_sha256.size());
130 EXPECT_EQ(crypto::kSHA256Length, component->manifest().table_sha256.size());
131 EXPECT_NE(component->manifest().image_sha256,
132 component->manifest().table_sha256);
Greg Kerr019d59c2016-11-17 14:28:49 -0800133}
134
Xiaochu Liuc2264342017-08-14 16:37:42 -0700135TEST_F(ComponentTest, TestCopyAndMountComponentExt4) {
136 std::unique_ptr<Component> component =
137 Component::Create(GetTestDataPath("ext4_component"), keys_);
138 ASSERT_NE(nullptr, component);
139
140 const base::FilePath copied_dir = temp_dir_.Append("dest");
141 ASSERT_TRUE(base::CreateDirectory(copied_dir));
142 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
143
144 ASSERT_TRUE(component->CopyTo(copied_dir));
145
146 std::unique_ptr<Component> copied_component =
147 Component::Create(copied_dir, keys_);
148 ASSERT_NE(nullptr, copied_component);
149
150 const base::FilePath mount_dir = temp_dir_.Append("mount");
151 ASSERT_TRUE(base::CreateDirectory(copied_dir));
152 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
153
154 // Note: this fails to test the actual mounting process since it is just a
155 // mock here. The platform_ImageLoader autotest tests the real helper
156 // process running as a dbus service.
Ben Chanea104dd2017-09-29 00:43:04 -0700157 auto helper_mock = std::make_unique<MockHelperProcess>();
Xiaochu Liuc2264342017-08-14 16:37:42 -0700158 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, FileSystem::kExt4, _))
159 .Times(1);
160 ON_CALL(*helper_mock, SendMountCommand(_, _, _, _))
161 .WillByDefault(testing::Return(true));
162 ASSERT_TRUE(copied_component->Mount(helper_mock.get(), mount_dir));
163}
164
165TEST_F(ComponentTest, TestCopyAndMountComponentSquashfs) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700166 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700167 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700168 ASSERT_NE(nullptr, component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800169
170 const base::FilePath copied_dir = temp_dir_.Append("dest");
171 ASSERT_TRUE(base::CreateDirectory(copied_dir));
172 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
173
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700174 ASSERT_TRUE(component->CopyTo(copied_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800175
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700176 std::unique_ptr<Component> copied_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700177 Component::Create(copied_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700178 ASSERT_NE(nullptr, copied_component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800179
180 const base::FilePath mount_dir = temp_dir_.Append("mount");
181 ASSERT_TRUE(base::CreateDirectory(copied_dir));
182 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
183
Greg Kerr9944e242017-01-26 15:09:31 -0800184 // Note: this fails to test the actual mounting process since it is just a
185 // mock here. The platform_ImageLoader autotest tests the real helper
186 // process running as a dbus service.
Ben Chanea104dd2017-09-29 00:43:04 -0700187 auto helper_mock = std::make_unique<MockHelperProcess>();
Xiaochu Liuc2264342017-08-14 16:37:42 -0700188 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, FileSystem::kSquashFS, _))
189 .Times(1);
190 ON_CALL(*helper_mock, SendMountCommand(_, _, _, _))
Greg Kerr9944e242017-01-26 15:09:31 -0800191 .WillByDefault(testing::Return(true));
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700192 ASSERT_TRUE(copied_component->Mount(helper_mock.get(), mount_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800193}
194
195TEST_F(ComponentTest, CheckFilesAfterCopy) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700196 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700197 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700198 ASSERT_NE(nullptr, component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800199
200 const base::FilePath copied_dir = temp_dir_.Append("dest");
201 ASSERT_TRUE(base::CreateDirectory(copied_dir));
202 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
203
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700204 ASSERT_TRUE(component->CopyTo(copied_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800205
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700206 std::unique_ptr<Component> copied_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700207 Component::Create(copied_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700208 ASSERT_NE(nullptr, copied_component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800209
210 // Check that all the files are present, except for the manifest.json which
211 // should be discarded.
212 std::list<std::string> original_files;
213 std::list<std::string> copied_files;
214 GetFilesInDir(GetTestComponentPath(), &original_files);
215 GetFilesInDir(copied_dir, &copied_files);
216
217 EXPECT_THAT(original_files,
218 testing::UnorderedElementsAre(
219 "imageloader.json", "imageloader.sig.1", "manifest.json",
220 "table", "image.squash", "manifest.fingerprint"));
221 ASSERT_THAT(copied_files,
222 testing::UnorderedElementsAre(
223 "imageloader.json", "imageloader.sig.1", "table",
224 "image.squash", "manifest.fingerprint"));
225 EXPECT_TRUE(
226 CompareFileContents(GetTestComponentPath(), copied_dir, copied_files));
227}
228
229TEST_F(ComponentTest, IsValidFingerprintFile) {
Greg Kerr019d59c2016-11-17 14:28:49 -0800230 const std::string valid_manifest =
231 "1.3464353b1ed78574e05f3ffe84b52582572b2fe7202f3824a3761e54ace8bb1";
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700232 EXPECT_TRUE(Component::IsValidFingerprintFile(valid_manifest));
Greg Kerr019d59c2016-11-17 14:28:49 -0800233
234 const std::string invalid_unicode_manifest = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ ";
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700235 EXPECT_FALSE(Component::IsValidFingerprintFile(invalid_unicode_manifest));
Greg Kerr019d59c2016-11-17 14:28:49 -0800236
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700237 EXPECT_FALSE(Component::IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
Greg Kerr019d59c2016-11-17 14:28:49 -0800238}
239
240TEST_F(ComponentTest, InitComponentWithBadFiles) {
241 EXPECT_TRUE(
242 TestInitComponentWithCorruptFile("bad-component1", "imageloader.json"));
243 EXPECT_TRUE(
244 TestInitComponentWithCorruptFile("bad-component2", "imageloader.sig.1"));
245}
246
247// Now corrupt the manifest of an already initialized component to verify that
248// the copy operation fails.
249TEST_F(ComponentTest, CopyWithBadFiles) {
250 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component1", "image.squash"));
251 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component2", "table"));
252 EXPECT_TRUE(
253 TestCopyWithCorruptFile("bad-component3", "manifest.fingerprint"));
254}
255
256TEST_F(ComponentTest, CopyValidImage) {
257 const int image_size = 4096 * 4;
258
259 base::FilePath image_path = temp_dir_.Append("image");
260 std::vector<char> image(image_size,
261 0xBB); // large enough to test streaming read.
262 ASSERT_EQ(image_size,
263 base::WriteFile(image_path, image.data(), image.size()));
264
265 std::vector<uint8_t> hash(crypto::kSHA256Length);
266
267 std::unique_ptr<crypto::SecureHash> sha256(
268 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
269 sha256->Update(image.data(), image.size());
270 sha256->Finish(hash.data(), hash.size());
271
Eric Caruso089bbff2017-03-21 11:34:15 -0700272 Component component(GetTestComponentPath(), 1);
Greg Kerr019d59c2016-11-17 14:28:49 -0800273 base::FilePath image_dest = temp_dir_.Append("image.copied");
274 ASSERT_TRUE(component.CopyComponentFile(image_path, image_dest, hash));
275
276 // Check if the image file actually exists and has the correct contents.
277 std::string resulting_image;
278 ASSERT_TRUE(base::ReadFileToStringWithMaxSize(image_dest, &resulting_image,
279 image_size));
280
Ben Chana92a9f02017-12-18 17:47:23 -0800281 EXPECT_EQ(0, memcmp(image.data(), resulting_image.data(), image_size));
Greg Kerr019d59c2016-11-17 14:28:49 -0800282}
283
284} // namespace imageloader