1 /*
2 * Copyright (C) 2018 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 "verity/hash_tree_builder.h"
18
19 #include <algorithm>
20 #include <memory>
21
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/stringprintf.h>
25 #include <android-base/strings.h>
26 #include <android-base/unique_fd.h>
27 #include <openssl/bn.h>
28
29 #include "build_verity_tree_utils.h"
30
HashFunction(const std::string & hash_name)31 const EVP_MD* HashTreeBuilder::HashFunction(const std::string& hash_name) {
32 if (android::base::EqualsIgnoreCase(hash_name, "sha1")) {
33 return EVP_sha1();
34 }
35 if (android::base::EqualsIgnoreCase(hash_name, "sha256")) {
36 return EVP_sha256();
37 }
38 if (android::base::EqualsIgnoreCase(hash_name, "sha384")) {
39 return EVP_sha384();
40 }
41 if (android::base::EqualsIgnoreCase(hash_name, "sha512")) {
42 return EVP_sha512();
43 }
44
45 LOG(ERROR) << "Unsupported hash algorithm " << hash_name;
46 return nullptr;
47 }
48
HashTreeBuilder(size_t block_size,const EVP_MD * md)49 HashTreeBuilder::HashTreeBuilder(size_t block_size, const EVP_MD* md)
50 : block_size_(block_size), data_size_(0), md_(md) {
51 CHECK(md_ != nullptr) << "Failed to initialize md";
52
53 hash_size_raw_ = EVP_MD_size(md_);
54
55 // Round up the hash size to the next power of 2.
56 hash_size_ = 1;
57 while (hash_size_ < hash_size_raw_) {
58 hash_size_ = hash_size_ << 1;
59 }
60 CHECK_LT(hash_size_ * 2, block_size_);
61 }
62
BytesArrayToString(const std::vector<unsigned char> & bytes)63 std::string HashTreeBuilder::BytesArrayToString(
64 const std::vector<unsigned char>& bytes) {
65 std::string result;
66 for (const auto& c : bytes) {
67 result += android::base::StringPrintf("%02x", c);
68 }
69 return result;
70 }
71
ParseBytesArrayFromString(const std::string & hex_string,std::vector<unsigned char> * bytes)72 bool HashTreeBuilder::ParseBytesArrayFromString(
73 const std::string& hex_string, std::vector<unsigned char>* bytes) {
74 if (hex_string.size() % 2 != 0) {
75 LOG(ERROR) << "Hex string size must be even number " << hex_string;
76 return false;
77 }
78
79 BIGNUM* bn = nullptr;
80 if (!BN_hex2bn(&bn, hex_string.c_str())) {
81 LOG(ERROR) << "Failed to parse hex in " << hex_string;
82 return false;
83 }
84 std::unique_ptr<BIGNUM, decltype(&BN_free)> guard(bn, BN_free);
85
86 size_t bytes_size = BN_num_bytes(bn);
87 bytes->resize(bytes_size);
88 if (BN_bn2bin(bn, bytes->data()) != bytes_size) {
89 LOG(ERROR) << "Failed to convert hex to bytes " << hex_string;
90 return false;
91 }
92 return true;
93 }
94
CalculateSize(uint64_t input_size) const95 uint64_t HashTreeBuilder::CalculateSize(uint64_t input_size) const {
96 uint64_t verity_blocks = 0;
97 size_t level_blocks;
98 size_t levels = 0;
99 do {
100 level_blocks =
101 verity_tree_blocks(input_size, block_size_, hash_size_, levels);
102 levels++;
103 verity_blocks += level_blocks;
104 } while (level_blocks > 1);
105
106 return verity_blocks * block_size_;
107 }
108
Initialize(int64_t expected_data_size,const std::vector<unsigned char> & salt)109 bool HashTreeBuilder::Initialize(int64_t expected_data_size,
110 const std::vector<unsigned char>& salt) {
111 data_size_ = expected_data_size;
112 salt_ = salt;
113
114 if (data_size_ % block_size_ != 0) {
115 LOG(ERROR) << "file size " << data_size_
116 << " is not a multiple of block size " << block_size_;
117 return false;
118 }
119
120 // Reserve enough space for the hash of the input data.
121 size_t base_level_blocks =
122 verity_tree_blocks(data_size_, block_size_, hash_size_, 0);
123 std::vector<unsigned char> base_level;
124 base_level.reserve(base_level_blocks * block_size_);
125 verity_tree_.emplace_back(std::move(base_level));
126
127 // Save the hash of the zero block to avoid future recalculation.
128 std::vector<unsigned char> zero_block(block_size_, 0);
129 zero_block_hash_.resize(hash_size_);
130 HashBlock(zero_block.data(), zero_block_hash_.data());
131
132 return true;
133 }
134
HashBlock(const unsigned char * block,unsigned char * out)135 bool HashTreeBuilder::HashBlock(const unsigned char* block,
136 unsigned char* out) {
137 unsigned int s;
138 int ret = 1;
139
140 EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
141 CHECK(mdctx != nullptr);
142 ret &= EVP_DigestInit_ex(mdctx, md_, nullptr);
143 ret &= EVP_DigestUpdate(mdctx, salt_.data(), salt_.size());
144 ret &= EVP_DigestUpdate(mdctx, block, block_size_);
145 ret &= EVP_DigestFinal_ex(mdctx, out, &s);
146 EVP_MD_CTX_destroy(mdctx);
147
148 CHECK_EQ(1, ret);
149 CHECK_EQ(hash_size_raw_, s);
150 std::fill(out + s, out + hash_size_, 0);
151
152 return true;
153 }
154
HashBlocks(const unsigned char * data,size_t len,std::vector<unsigned char> * output_vector)155 bool HashTreeBuilder::HashBlocks(const unsigned char* data, size_t len,
156 std::vector<unsigned char>* output_vector) {
157 if (len == 0) {
158 return true;
159 }
160 CHECK_EQ(0, len % block_size_);
161
162 if (data == nullptr) {
163 for (size_t i = 0; i < len; i += block_size_) {
164 output_vector->insert(output_vector->end(), zero_block_hash_.begin(),
165 zero_block_hash_.end());
166 }
167 return true;
168 }
169
170 for (size_t i = 0; i < len; i += block_size_) {
171 unsigned char hash_buffer[hash_size_];
172 if (!HashBlock(data + i, hash_buffer)) {
173 return false;
174 }
175 output_vector->insert(output_vector->end(), hash_buffer,
176 hash_buffer + hash_size_);
177 }
178
179 return true;
180 }
181
Update(const unsigned char * data,size_t len)182 bool HashTreeBuilder::Update(const unsigned char* data, size_t len) {
183 CHECK_GT(data_size_, 0);
184
185 if (!leftover_.empty()) {
186 CHECK_LT(leftover_.size(), block_size_);
187 size_t append_len = std::min(len, block_size_ - leftover_.size());
188 if (data == nullptr) {
189 leftover_.insert(leftover_.end(), append_len, 0);
190 } else {
191 leftover_.insert(leftover_.end(), data, data + append_len);
192 }
193 if (leftover_.size() < block_size_) {
194 return true;
195 }
196 if (!HashBlocks(leftover_.data(), leftover_.size(), &verity_tree_[0])) {
197 return false;
198 }
199 leftover_.clear();
200 data += append_len;
201 len -= append_len;
202 }
203 if (len % block_size_ != 0) {
204 if (data == nullptr) {
205 leftover_.assign(len % block_size_, 0);
206 } else {
207 leftover_.assign(data + len - len % block_size_, data + len);
208 }
209 len -= len % block_size_;
210 }
211 return HashBlocks(data, len, &verity_tree_[0]);
212 }
213
BuildHashTree()214 bool HashTreeBuilder::BuildHashTree() {
215 // Expects only the base level in the verity_tree_.
216 CHECK_EQ(1, verity_tree_.size());
217
218 if (!leftover_.empty()) {
219 LOG(ERROR) << leftover_.size() << " bytes data left from last Update().";
220 return false;
221 }
222
223 // Expects the base level to have the same size as the total hash size of
224 // input data.
225 AppendPaddings(&verity_tree_.back());
226 size_t base_level_blocks =
227 verity_tree_blocks(data_size_, block_size_, hash_size_, 0);
228 CHECK_EQ(base_level_blocks * block_size_, verity_tree_[0].size());
229
230 while (verity_tree_.back().size() > block_size_) {
231 const auto& current_level = verity_tree_.back();
232 // Computes the next level of the verity tree based on the hash of the
233 // current level.
234 size_t next_level_blocks =
235 verity_tree_blocks(current_level.size(), block_size_, hash_size_, 0);
236 std::vector<unsigned char> next_level;
237 next_level.reserve(next_level_blocks * block_size_);
238
239 HashBlocks(current_level.data(), current_level.size(), &next_level);
240 AppendPaddings(&next_level);
241
242 // Checks the size of the next level.
243 CHECK_EQ(next_level_blocks * block_size_, next_level.size());
244 verity_tree_.emplace_back(std::move(next_level));
245 }
246
247 CHECK_EQ(block_size_, verity_tree_.back().size());
248 HashBlocks(verity_tree_.back().data(), block_size_, &root_hash_);
249
250 return true;
251 }
252
CheckHashTree(const std::vector<unsigned char> & hash_tree) const253 bool HashTreeBuilder::CheckHashTree(
254 const std::vector<unsigned char>& hash_tree) const {
255 size_t offset = 0;
256 // Reads reversely to output the verity tree top-down.
257 for (size_t i = verity_tree_.size(); i > 0; i--) {
258 const auto& level_blocks = verity_tree_[i - 1];
259 if (offset + level_blocks.size() > hash_tree.size()) {
260 LOG(ERROR) << "Hash tree too small: " << hash_tree.size();
261 return false;
262 }
263 auto iter = std::mismatch(level_blocks.begin(), level_blocks.end(),
264 hash_tree.begin() + offset)
265 .first;
266 if (iter != level_blocks.end()) {
267 LOG(ERROR) << "Mismatch found at the hash tree level " << i << " offset "
268 << std::distance(level_blocks.begin(), iter);
269 return false;
270 }
271 offset += level_blocks.size();
272 }
273 if (offset != hash_tree.size()) {
274 LOG(ERROR) << "Hash tree size mismatch: " << hash_tree.size()
275 << " != " << offset;
276 return false;
277 }
278 return true;
279 }
280
WriteHashTreeToFile(const std::string & output) const281 bool HashTreeBuilder::WriteHashTreeToFile(const std::string& output) const {
282 android::base::unique_fd output_fd(
283 open(output.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666));
284 if (output_fd == -1) {
285 PLOG(ERROR) << "Failed to open output file " << output;
286 return false;
287 }
288
289 return WriteHashTreeToFd(output_fd, 0);
290 }
291
WriteHashTreeToFd(int fd,uint64_t offset) const292 bool HashTreeBuilder::WriteHashTreeToFd(int fd, uint64_t offset) const {
293 CHECK(!verity_tree_.empty());
294
295 if (lseek(fd, offset, SEEK_SET) != offset) {
296 PLOG(ERROR) << "Failed to seek the output fd, offset: " << offset;
297 return false;
298 }
299
300 // Reads reversely to output the verity tree top-down.
301 for (size_t i = verity_tree_.size(); i > 0; i--) {
302 const auto& level_blocks = verity_tree_[i - 1];
303 if (!android::base::WriteFully(fd, level_blocks.data(),
304 level_blocks.size())) {
305 PLOG(ERROR) << "Failed to write the hash tree level " << i;
306 return false;
307 }
308 }
309
310 return true;
311 }
312
AppendPaddings(std::vector<unsigned char> * data)313 void HashTreeBuilder::AppendPaddings(std::vector<unsigned char>* data) {
314 size_t remainder = data->size() % block_size_;
315 if (remainder != 0) {
316 data->resize(data->size() + block_size_ - remainder, 0);
317 }
318 }
319