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 if (data != nullptr) {
201 data += append_len;
202 }
203 len -= append_len;
204 }
205 if (len % block_size_ != 0) {
206 if (data == nullptr) {
207 leftover_.assign(len % block_size_, 0);
208 } else {
209 leftover_.assign(data + len - len % block_size_, data + len);
210 }
211 len -= len % block_size_;
212 }
213 return HashBlocks(data, len, &verity_tree_[0]);
214 }
215
CalculateRootDigest(const std::vector<unsigned char> & root_verity,std::vector<unsigned char> * root_digest)216 bool HashTreeBuilder::CalculateRootDigest(const std::vector<unsigned char>& root_verity,
217 std::vector<unsigned char>* root_digest) {
218 if (root_verity.size() != block_size_) {
219 return false;
220 }
221 return HashBlocks(root_verity.data(), block_size_, root_digest);
222 }
223
BuildHashTree()224 bool HashTreeBuilder::BuildHashTree() {
225 // Expects only the base level in the verity_tree_.
226 CHECK_EQ(1, verity_tree_.size());
227
228 if (!leftover_.empty()) {
229 LOG(ERROR) << leftover_.size() << " bytes data left from last Update().";
230 return false;
231 }
232
233 // Expects the base level to have the same size as the total hash size of
234 // input data.
235 AppendPaddings(&verity_tree_.back());
236 size_t base_level_blocks =
237 verity_tree_blocks(data_size_, block_size_, hash_size_, 0);
238 CHECK_EQ(base_level_blocks * block_size_, verity_tree_[0].size());
239
240 while (verity_tree_.back().size() > block_size_) {
241 const auto& current_level = verity_tree_.back();
242 // Computes the next level of the verity tree based on the hash of the
243 // current level.
244 size_t next_level_blocks =
245 verity_tree_blocks(current_level.size(), block_size_, hash_size_, 0);
246 std::vector<unsigned char> next_level;
247 next_level.reserve(next_level_blocks * block_size_);
248
249 HashBlocks(current_level.data(), current_level.size(), &next_level);
250 AppendPaddings(&next_level);
251
252 // Checks the size of the next level.
253 CHECK_EQ(next_level_blocks * block_size_, next_level.size());
254 verity_tree_.emplace_back(std::move(next_level));
255 }
256
257 CHECK_EQ(block_size_, verity_tree_.back().size());
258 return CalculateRootDigest(verity_tree_.back(), &root_hash_);
259 }
260
CheckHashTree(const std::vector<unsigned char> & hash_tree) const261 bool HashTreeBuilder::CheckHashTree(
262 const std::vector<unsigned char>& hash_tree) const {
263 size_t offset = 0;
264 // Reads reversely to output the verity tree top-down.
265 for (size_t i = verity_tree_.size(); i > 0; i--) {
266 const auto& level_blocks = verity_tree_[i - 1];
267 if (offset + level_blocks.size() > hash_tree.size()) {
268 LOG(ERROR) << "Hash tree too small: " << hash_tree.size();
269 return false;
270 }
271 auto iter = std::mismatch(level_blocks.begin(), level_blocks.end(),
272 hash_tree.begin() + offset)
273 .first;
274 if (iter != level_blocks.end()) {
275 LOG(ERROR) << "Mismatch found at the hash tree level " << i << " offset "
276 << std::distance(level_blocks.begin(), iter);
277 return false;
278 }
279 offset += level_blocks.size();
280 }
281 if (offset != hash_tree.size()) {
282 LOG(ERROR) << "Hash tree size mismatch: " << hash_tree.size()
283 << " != " << offset;
284 return false;
285 }
286 return true;
287 }
288
WriteHashTreeToFile(const std::string & output) const289 bool HashTreeBuilder::WriteHashTreeToFile(const std::string& output) const {
290 android::base::unique_fd output_fd(
291 open(output.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666));
292 if (output_fd == -1) {
293 PLOG(ERROR) << "Failed to open output file " << output;
294 return false;
295 }
296
297 return WriteHashTreeToFd(output_fd, 0);
298 }
299
WriteHashTreeToFd(int fd,uint64_t offset) const300 bool HashTreeBuilder::WriteHashTreeToFd(int fd, uint64_t offset) const {
301 CHECK(!verity_tree_.empty());
302
303 if (lseek(fd, offset, SEEK_SET) != offset) {
304 PLOG(ERROR) << "Failed to seek the output fd, offset: " << offset;
305 return false;
306 }
307
308 // Reads reversely to output the verity tree top-down.
309 for (size_t i = verity_tree_.size(); i > 0; i--) {
310 const auto& level_blocks = verity_tree_[i - 1];
311 if (!android::base::WriteFully(fd, level_blocks.data(),
312 level_blocks.size())) {
313 PLOG(ERROR) << "Failed to write the hash tree level " << i;
314 return false;
315 }
316 }
317
318 return true;
319 }
320
AppendPaddings(std::vector<unsigned char> * data)321 void HashTreeBuilder::AppendPaddings(std::vector<unsigned char>* data) {
322 size_t remainder = data->size() % block_size_;
323 if (remainder != 0) {
324 data->resize(data->size() + block_size_ - remainder, 0);
325 }
326 }
327