1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdint.h>
18 #include <stdlib.h>
19
20 #include <string>
21 #include <vector>
22
23 #include <android-base/file.h>
24 #include <android-base/strings.h>
25 #include <gtest/gtest.h>
26 #include <verity/hash_tree_builder.h>
27
28 #include "../fec_private.h"
29 #include "fec/io.h"
30
31 class FecUnitTest : public ::testing::Test {
32 protected:
SetUp()33 void SetUp() override {
34 // Construct a 1 MiB image as file system.
35 image_.reserve(1024 * 1024);
36 for (unsigned i = 0; i <= 255; i++) {
37 std::vector<uint8_t> tmp_vec(4096, i);
38 image_.insert(image_.end(), tmp_vec.begin(), tmp_vec.end());
39 }
40 }
BuildHashtree(const std::string & hash_name)41 void BuildHashtree(const std::string &hash_name) {
42 // Build the hashtree.
43 HashTreeBuilder builder(4096, HashTreeBuilder::HashFunction(hash_name));
44 // Use a random salt.
45 salt_ = std::vector<uint8_t>(64, 10);
46 ASSERT_TRUE(builder.Initialize(image_.size(), salt_));
47 ASSERT_TRUE(builder.Update(image_.data(), image_.size()));
48 ASSERT_TRUE(builder.BuildHashTree());
49 root_hash_ = builder.root_hash();
50
51 TemporaryFile temp_file;
52 ASSERT_TRUE(builder.WriteHashTreeToFd(temp_file.fd, 0));
53 android::base::ReadFileToString(temp_file.path, &hashtree_content_);
54 }
55
56 // Builds the verity metadata and appends the bytes to the image.
BuildAndAppendsVerityMetadata()57 void BuildAndAppendsVerityMetadata() {
58 BuildHashtree("sha256");
59 // Append the hashtree to the end of image.
60 image_.insert(image_.end(), hashtree_content_.begin(),
61 hashtree_content_.end());
62
63 // The metadata table has the format: "1 block_device, block_device,
64 // BLOCK_SIZE, BLOCK_SIZE, data_blocks, data_blocks, 'sha256',
65 // root_hash, salt".
66 std::vector<std::string> table = {
67 "1",
68 "fake_block_device",
69 "fake_block_device",
70 "4096",
71 "4096",
72 "256",
73 "256",
74 "sha256",
75 HashTreeBuilder::BytesArrayToString(root_hash_),
76 HashTreeBuilder::BytesArrayToString(salt_),
77 };
78 verity_table_ = android::base::Join(table, ' ');
79
80 verity_header_ = {
81 0xb001b001, 0, {}, static_cast<unsigned int>(verity_table_.size())
82 };
83
84 // Construct the verity metadata with header, table, and padding.
85 constexpr auto VERITY_META_SIZE = 8 * 4096;
86 image_.insert(image_.end(),
87 reinterpret_cast<uint8_t *>(&verity_header_),
88 reinterpret_cast<uint8_t *>(&verity_header_) +
89 sizeof(verity_header_));
90 image_.insert(image_.end(), verity_table_.data(),
91 verity_table_.data() + verity_table_.size());
92 std::vector<uint8_t> padding(
93 VERITY_META_SIZE - sizeof(verity_header_) - verity_table_.size(),
94 0);
95 image_.insert(image_.end(), padding.begin(), padding.end());
96 }
97
BuildAndAppendsEccImage(const std::string & image_name,const std::string & fec_name)98 static void BuildAndAppendsEccImage(const std::string &image_name,
99 const std::string &fec_name) {
100 std::vector<std::string> cmd = { "fec", "--encode", "--roots",
101 "2", image_name, fec_name };
102 ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str()));
103 }
104
AddAvbHashtreeFooter(const std::string & image_name,std::string algorithm="sha256")105 void AddAvbHashtreeFooter(const std::string &image_name,
106 std::string algorithm = "sha256") {
107 salt_ = std::vector<uint8_t>(64, 10);
108 std::vector<std::string> cmd = {
109 "avbtool", "add_hashtree_footer",
110 "--salt", HashTreeBuilder::BytesArrayToString(salt_),
111 "--hash_algorithm", algorithm,
112 "--image", image_name,
113 };
114 ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str()));
115
116 BuildHashtree(algorithm);
117 }
118
119 std::vector<uint8_t> image_;
120 std::vector<uint8_t> salt_;
121 std::vector<uint8_t> root_hash_;
122 std::string hashtree_content_;
123 verity_header verity_header_;
124 std::string verity_table_;
125 };
126
TEST_F(FecUnitTest,LoadVerityImage_ParseVerity)127 TEST_F(FecUnitTest, LoadVerityImage_ParseVerity) {
128 TemporaryFile verity_image;
129 BuildAndAppendsVerityMetadata();
130 ASSERT_TRUE(android::base::WriteFully(verity_image.fd, image_.data(),
131 image_.size()));
132
133 struct fec_handle *handle = nullptr;
134 ASSERT_EQ(0, fec_open(&handle, verity_image.path, O_RDONLY, FEC_FS_EXT4, 2));
135 std::unique_ptr<fec_handle> guard(handle);
136
137 ASSERT_EQ(image_.size(), handle->size);
138 ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
139
140 ASSERT_EQ(1024 * 1024 + hashtree_content_.size(),
141 handle->verity.metadata_start);
142 ASSERT_EQ(verity_header_.length, handle->verity.header.length);
143 ASSERT_EQ(verity_table_, handle->verity.table);
144
145 // check the hashtree.
146 ASSERT_EQ(salt_, handle->hashtree().salt);
147 ASSERT_EQ(1024 * 1024, handle->hashtree().hash_start);
148 // the fec hashtree only stores the hash of the lowest level.
149 ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
150 hashtree_content_.end()),
151 handle->hashtree().hash_data);
152
153 uint64_t hash_size =
154 verity_get_size(handle->hashtree().data_blocks * FEC_BLOCKSIZE, nullptr,
155 nullptr, SHA256_DIGEST_LENGTH);
156 ASSERT_EQ(hashtree_content_.size(), hash_size);
157 }
158
TEST_F(FecUnitTest,LoadVerityImage_ParseEcc)159 TEST_F(FecUnitTest, LoadVerityImage_ParseEcc) {
160 TemporaryFile verity_image;
161 BuildAndAppendsVerityMetadata();
162 ASSERT_TRUE(android::base::WriteFully(verity_image.fd, image_.data(),
163 image_.size()));
164 TemporaryFile ecc_image;
165 BuildAndAppendsEccImage(verity_image.path, ecc_image.path);
166 std::string ecc_content;
167 ASSERT_TRUE(android::base::ReadFileToString(ecc_image.path, &ecc_content));
168 ASSERT_TRUE(android::base::WriteStringToFd(ecc_content, verity_image.fd));
169 struct fec_handle *handle = nullptr;
170 ASSERT_EQ(0, fec_open(&handle, verity_image.path, O_RDONLY, FEC_FS_EXT4, 2));
171 std::unique_ptr<fec_handle> guard(handle);
172
173 ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
174 ASSERT_EQ(1024 * 1024 + hashtree_content_.size(),
175 handle->verity.metadata_start);
176
177 fec_verity_metadata verity_metadata{};
178 ASSERT_EQ(0, fec_verity_get_metadata(handle, &verity_metadata));
179 ASSERT_FALSE(verity_metadata.disabled);
180 ASSERT_EQ(1024 * 1024, verity_metadata.data_size);
181 ASSERT_EQ(verity_table_, verity_metadata.table);
182
183 fec_ecc_metadata ecc_metadata{};
184 ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
185 ASSERT_TRUE(ecc_metadata.valid);
186 ASSERT_EQ(handle->verity.metadata_start + 8 * 4096, ecc_metadata.start);
187 ASSERT_EQ(2, ecc_metadata.roots);
188 // 256 (data) + 3 (hashtree) + 8 (verity meta)
189 ASSERT_EQ(267, ecc_metadata.blocks);
190 }
191
TEST_F(FecUnitTest,VerityImage_FecRead)192 TEST_F(FecUnitTest, VerityImage_FecRead) {
193 TemporaryFile verity_image;
194 BuildAndAppendsVerityMetadata();
195 ASSERT_TRUE(android::base::WriteFully(verity_image.fd, image_.data(),
196 image_.size()));
197 TemporaryFile ecc_image;
198 BuildAndAppendsEccImage(verity_image.path, ecc_image.path);
199 std::string ecc_content;
200 ASSERT_TRUE(android::base::ReadFileToString(ecc_image.path, &ecc_content));
201 ASSERT_TRUE(android::base::WriteStringToFd(ecc_content, verity_image.fd));
202
203 // Corrupt the last block
204 uint64_t corrupt_offset = 4096 * 255;
205 ASSERT_EQ(corrupt_offset, lseek64(verity_image.fd, corrupt_offset, 0));
206 std::vector<uint8_t> corruption(100, 10);
207 ASSERT_TRUE(android::base::WriteFully(verity_image.fd, corruption.data(),
208 corruption.size()));
209
210 std::vector<uint8_t> read_data(1024, 0);
211 struct fec_handle *handle = nullptr;
212 ASSERT_EQ(0,
213 fec_open(&handle, verity_image.path, O_RDONLY, FEC_FS_EXT4, 2));
214 std::unique_ptr<fec_handle> guard(handle);
215
216 ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
217 ASSERT_EQ(std::vector<uint8_t>(1024, 255), read_data);
218 }
219
TEST_F(FecUnitTest,LoadAvbImage_HashtreeFooter)220 TEST_F(FecUnitTest, LoadAvbImage_HashtreeFooter) {
221 TemporaryFile avb_image;
222 ASSERT_TRUE(
223 android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
224 AddAvbHashtreeFooter(avb_image.path);
225
226 struct fec_handle *handle = nullptr;
227 ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
228 std::unique_ptr<fec_handle> guard(handle);
229
230 ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
231
232 ASSERT_TRUE(handle->avb.valid);
233
234 // check the hashtree.
235 ASSERT_EQ(salt_, handle->hashtree().salt);
236 ASSERT_EQ(1024 * 1024, handle->hashtree().hash_start);
237 // the fec hashtree only stores the hash of the lowest level.
238 ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
239 hashtree_content_.end()),
240 handle->hashtree().hash_data);
241 uint64_t hash_size =
242 verity_get_size(handle->hashtree().data_blocks * FEC_BLOCKSIZE, nullptr,
243 nullptr, SHA256_DIGEST_LENGTH);
244 ASSERT_EQ(hashtree_content_.size(), hash_size);
245
246 fec_ecc_metadata ecc_metadata{};
247 ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
248 ASSERT_TRUE(ecc_metadata.valid);
249 ASSERT_EQ(1024 * 1024 + hash_size, ecc_metadata.start);
250 ASSERT_EQ(259, ecc_metadata.blocks);
251 }
252
TEST_F(FecUnitTest,LoadAvbImage_CorrectHashtree)253 TEST_F(FecUnitTest, LoadAvbImage_CorrectHashtree) {
254 TemporaryFile avb_image;
255 ASSERT_TRUE(
256 android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
257 AddAvbHashtreeFooter(avb_image.path);
258
259 uint64_t corrupt_offset = 1024 * 1024 + 2 * 4096 + 50;
260 ASSERT_EQ(corrupt_offset, lseek64(avb_image.fd, corrupt_offset, 0));
261 std::vector<uint8_t> corruption(20, 5);
262 ASSERT_TRUE(android::base::WriteFully(avb_image.fd, corruption.data(),
263 corruption.size()));
264
265 struct fec_handle *handle = nullptr;
266 ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
267 std::unique_ptr<fec_handle> guard(handle);
268
269 ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
270 fec_ecc_metadata ecc_metadata{};
271 ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
272 ASSERT_TRUE(ecc_metadata.valid);
273 }
274
TEST_F(FecUnitTest,AvbImage_FecRead)275 TEST_F(FecUnitTest, AvbImage_FecRead) {
276 TemporaryFile avb_image;
277 ASSERT_TRUE(
278 android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
279 AddAvbHashtreeFooter(avb_image.path, "sha1");
280
281 uint64_t corrupt_offset = 4096 * 10;
282 ASSERT_EQ(corrupt_offset, lseek64(avb_image.fd, corrupt_offset, 0));
283 std::vector<uint8_t> corruption(50, 99);
284 ASSERT_TRUE(android::base::WriteFully(avb_image.fd, corruption.data(),
285 corruption.size()));
286
287 std::vector<uint8_t> read_data(1024, 0);
288 struct fec_handle *handle = nullptr;
289 ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
290 std::unique_ptr<fec_handle> guard(handle);
291
292 // Verify the hashtree has the expected content.
293 ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
294 hashtree_content_.end()),
295 handle->hashtree().hash_data);
296
297 // Verify the corruption gets corrected.
298 ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
299 ASSERT_EQ(std::vector<uint8_t>(1024, 10), read_data);
300 }
301