• 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 <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