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