blob: d19403819a8c062e9f1f95b7d07e25b89b468250 [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
5#include "component.h"
6
7#include <stdint.h>
8
9#include <list>
10#include <string>
11#include <vector>
12
Greg Kerr9944e242017-01-26 15:09:31 -080013#include "mock_helper_process.h"
Greg Kerr019d59c2016-11-17 14:28:49 -080014#include "test_utilities.h"
Greg Kerr019d59c2016-11-17 14:28:49 -080015
16#include <base/files/file_path.h>
17#include <base/files/file_util.h>
18#include <base/files/scoped_temp_dir.h>
19#include <base/logging.h>
20#include <base/memory/ptr_util.h>
21#include <crypto/secure_hash.h>
22#include <crypto/sha2.h>
23#include <gmock/gmock.h>
24#include <gtest/gtest.h>
25
26namespace imageloader {
27
28using testing::_;
29
30class ComponentTest : public testing::Test {
31 public:
32 ComponentTest() {
33 key_ = std::vector<uint8_t>(std::begin(kDevPublicKey),
34 std::end(kDevPublicKey));
35 CHECK(scoped_temp_dir_.CreateUniqueTempDir());
36 temp_dir_ = scoped_temp_dir_.path();
37 CHECK(base::SetPosixFilePermissions(temp_dir_, kComponentDirPerms));
38 }
39
40 bool TestCopyWithCorruptFile(const std::string& component_name,
41 const std::string& file_name) {
42 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
43 if (!base::CreateDirectory(bad_component_dir)) return false;
44 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
45 return false;
46
47 Component component(GetTestComponentPath());
48 if (!component.Init(key_) || !component.CopyTo(bad_component_dir))
49 return false;
50
51 Component bad_component(bad_component_dir);
52 if (!bad_component.Init(key_)) return false;
53
54 base::FilePath file = bad_component_dir.Append(file_name);
55 const char data[] = "c";
56 if (!base::AppendToFile(file, data, sizeof(data))) return false;
57
58 base::FilePath bad_component_dest =
59 temp_dir_.Append(component_name + "invalid");
60 if (!base::CreateDirectory(bad_component_dest)) return false;
61
62 if (!base::SetPosixFilePermissions(bad_component_dest, kComponentDirPerms))
63 return false;
64 return bad_component.CopyTo(bad_component_dest) == false;
65 }
66
67 bool TestInitComponentWithCorruptFile(const std::string& component_name,
68 const std::string& file_name) {
69 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
70 if (!base::CreateDirectory(bad_component_dir)) return false;
71 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
72 return false;
73
74 Component component(GetTestComponentPath());
75 if (!component.Init(key_) || !component.CopyTo(bad_component_dir))
76 return false;
77
78 base::FilePath file = bad_component_dir.Append(file_name);
79 // Read the file out and change the last byte.
80 std::string file_contents;
81 if (!base::ReadFileToString(file, &file_contents)) return false;
82 file_contents[file_contents.size() - 1] =
83 ~file_contents[file_contents.size() - 1];
84
85 if (!base::WriteFile(file, file_contents.data(), file_contents.size())) {
86 return false;
87 }
88
89 Component bad_component(bad_component_dir);
90 return bad_component.Init(key_) == false;
91 }
92
93 bool CompareFileContents(const base::FilePath& src,
94 const base::FilePath& dest,
95 const std::list<std::string>& filenames) {
96 for (const std::string& name : filenames) {
97 std::string source_file_contents;
98 std::string dest_file_contents;
99 if (!base::ReadFileToString(src.Append(name), &source_file_contents))
100 return false;
101 if (!base::ReadFileToString(dest.Append(name), &dest_file_contents))
102 return false;
103 if (source_file_contents != dest_file_contents) {
104 LOG(ERROR) << "File contents does not match for file: " << name;
105 return false;
106 }
107 }
108 return true;
109 }
110
111 std::vector<uint8_t> key_;
112 base::ScopedTempDir scoped_temp_dir_;
113 base::FilePath temp_dir_;
114};
115
116TEST_F(ComponentTest, InitComponentAndCheckManifest) {
117 Component component(GetTestComponentPath());
118 ASSERT_TRUE(component.Init(key_));
119 EXPECT_EQ(1, component.manifest().manifest_version);
120 EXPECT_EQ(kTestDataVersion, component.manifest().version);
121 // Don't hardcode the sha256 hashes, but run some sanity checks.
122 EXPECT_EQ(crypto::kSHA256Length, component.manifest().image_sha256.size());
123 EXPECT_EQ(crypto::kSHA256Length, component.manifest().table_sha256.size());
124 EXPECT_NE(component.manifest().image_sha256,
125 component.manifest().table_sha256);
126}
127
128TEST_F(ComponentTest, TestCopyAndMountComponent) {
129 Component component(GetTestComponentPath());
130 ASSERT_TRUE(component.Init(key_));
131
132 const base::FilePath copied_dir = temp_dir_.Append("dest");
133 ASSERT_TRUE(base::CreateDirectory(copied_dir));
134 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
135
136 ASSERT_TRUE(component.CopyTo(copied_dir));
137
138 Component copied_component(copied_dir);
139 ASSERT_TRUE(copied_component.Init(key_));
140
141 const base::FilePath mount_dir = temp_dir_.Append("mount");
142 ASSERT_TRUE(base::CreateDirectory(copied_dir));
143 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
144
Greg Kerr9944e242017-01-26 15:09:31 -0800145 // Note: this fails to test the actual mounting process since it is just a
146 // mock here. The platform_ImageLoader autotest tests the real helper
147 // process running as a dbus service.
148 auto helper_mock = base::MakeUnique<MockHelperProcess>();
149 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, _)).Times(1);
150 ON_CALL(*helper_mock, SendMountCommand(_, _, _))
151 .WillByDefault(testing::Return(true));
152 ASSERT_TRUE(copied_component.Mount(helper_mock.get(), mount_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800153}
154
155TEST_F(ComponentTest, CheckFilesAfterCopy) {
156 Component component(GetTestComponentPath());
157 ASSERT_TRUE(component.Init(key_));
158
159 const base::FilePath copied_dir = temp_dir_.Append("dest");
160 ASSERT_TRUE(base::CreateDirectory(copied_dir));
161 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
162
163 ASSERT_TRUE(component.CopyTo(copied_dir));
164
165 Component copied_component(copied_dir);
166 ASSERT_TRUE(copied_component.Init(key_));
167
168 // Check that all the files are present, except for the manifest.json which
169 // should be discarded.
170 std::list<std::string> original_files;
171 std::list<std::string> copied_files;
172 GetFilesInDir(GetTestComponentPath(), &original_files);
173 GetFilesInDir(copied_dir, &copied_files);
174
175 EXPECT_THAT(original_files,
176 testing::UnorderedElementsAre(
177 "imageloader.json", "imageloader.sig.1", "manifest.json",
178 "table", "image.squash", "manifest.fingerprint"));
179 ASSERT_THAT(copied_files,
180 testing::UnorderedElementsAre(
181 "imageloader.json", "imageloader.sig.1", "table",
182 "image.squash", "manifest.fingerprint"));
183 EXPECT_TRUE(
184 CompareFileContents(GetTestComponentPath(), copied_dir, copied_files));
185}
186
187TEST_F(ComponentTest, IsValidFingerprintFile) {
188 Component component(base::FilePath("/nonexistant"));
189 const std::string valid_manifest =
190 "1.3464353b1ed78574e05f3ffe84b52582572b2fe7202f3824a3761e54ace8bb1";
191 EXPECT_TRUE(component.IsValidFingerprintFile(valid_manifest));
192
193 const std::string invalid_unicode_manifest = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ ";
194 EXPECT_FALSE(component.IsValidFingerprintFile(invalid_unicode_manifest));
195
196 EXPECT_FALSE(component.IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
197}
198
199TEST_F(ComponentTest, InitComponentWithBadFiles) {
200 EXPECT_TRUE(
201 TestInitComponentWithCorruptFile("bad-component1", "imageloader.json"));
202 EXPECT_TRUE(
203 TestInitComponentWithCorruptFile("bad-component2", "imageloader.sig.1"));
204}
205
206// Now corrupt the manifest of an already initialized component to verify that
207// the copy operation fails.
208TEST_F(ComponentTest, CopyWithBadFiles) {
209 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component1", "image.squash"));
210 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component2", "table"));
211 EXPECT_TRUE(
212 TestCopyWithCorruptFile("bad-component3", "manifest.fingerprint"));
213}
214
215TEST_F(ComponentTest, CopyValidImage) {
216 const int image_size = 4096 * 4;
217
218 base::FilePath image_path = temp_dir_.Append("image");
219 std::vector<char> image(image_size,
220 0xBB); // large enough to test streaming read.
221 ASSERT_EQ(image_size,
222 base::WriteFile(image_path, image.data(), image.size()));
223
224 std::vector<uint8_t> hash(crypto::kSHA256Length);
225
226 std::unique_ptr<crypto::SecureHash> sha256(
227 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
228 sha256->Update(image.data(), image.size());
229 sha256->Finish(hash.data(), hash.size());
230
231 Component component(GetTestComponentPath());
232 base::FilePath image_dest = temp_dir_.Append("image.copied");
233 ASSERT_TRUE(component.CopyComponentFile(image_path, image_dest, hash));
234
235 // Check if the image file actually exists and has the correct contents.
236 std::string resulting_image;
237 ASSERT_TRUE(base::ReadFileToStringWithMaxSize(image_dest, &resulting_image,
238 image_size));
239
240 EXPECT_TRUE(memcmp(image.data(), resulting_image.data(), image_size) == 0);
241}
242
243} // namespace imageloader