• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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