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