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