• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 #define LOG_TAG "apexd"
17 
18 #include "apexd_verity.h"
19 
20 #include <android-base/file.h>
21 #include <android-base/result.h>
22 #include <android-base/unique_fd.h>
23 #include <verity/hash_tree_builder.h>
24 
25 #include <filesystem>
26 #include <iomanip>
27 #include <sstream>
28 #include <string>
29 #include <vector>
30 
31 #include "apex_constants.h"
32 #include "apex_file.h"
33 #include "apexd_utils.h"
34 
35 using android::base::Dirname;
36 using android::base::ErrnoError;
37 using android::base::Error;
38 using android::base::ReadFully;
39 using android::base::Result;
40 using android::base::unique_fd;
41 
42 namespace android {
43 namespace apex {
44 
45 namespace {
46 
HexToBin(char h)47 uint8_t HexToBin(char h) {
48   if (h >= 'A' && h <= 'H') return h - 'A' + 10;
49   if (h >= 'a' && h <= 'h') return h - 'a' + 10;
50   return h - '0';
51 }
52 
HexToBin(const std::string & hex)53 std::vector<uint8_t> HexToBin(const std::string& hex) {
54   std::vector<uint8_t> bin;
55   bin.reserve(hex.size() / 2);
56   for (size_t i = 0; i + 1 < hex.size(); i += 2) {
57     uint8_t c = (HexToBin(hex[i]) << 4) + HexToBin(hex[i + 1]);
58     bin.push_back(c);
59   }
60   return bin;
61 }
62 
GenerateHashTree(const ApexFile & apex,const ApexVerityData & verity_data,const std::string & hashtree_file)63 Result<void> GenerateHashTree(const ApexFile& apex,
64                               const ApexVerityData& verity_data,
65                               const std::string& hashtree_file) {
66   unique_fd fd(
67       TEMP_FAILURE_RETRY(open(apex.GetPath().c_str(), O_RDONLY | O_CLOEXEC)));
68   if (fd.get() == -1) {
69     return ErrnoError() << "Failed to open " << apex.GetPath();
70   }
71 
72   auto block_size = verity_data.desc->hash_block_size;
73   auto image_size = verity_data.desc->image_size;
74 
75   auto hash_fn = HashTreeBuilder::HashFunction(verity_data.hash_algorithm);
76   if (hash_fn == nullptr) {
77     return Error() << "Unsupported hash algorithm "
78                    << verity_data.hash_algorithm;
79   }
80 
81   auto builder = std::make_unique<HashTreeBuilder>(block_size, hash_fn);
82   if (!builder->Initialize(image_size, HexToBin(verity_data.salt))) {
83     return Error() << "Invalid image size " << image_size;
84   }
85 
86   if (!apex.GetImageOffset()) {
87     return Error() << "Cannot generate HashTree without image offset";
88   }
89   if (lseek(fd, apex.GetImageOffset().value(), SEEK_SET) == -1) {
90     return ErrnoError() << "Failed to seek";
91   }
92 
93   auto block_count = image_size / block_size;
94   auto buf = std::vector<uint8_t>(block_size);
95   while (block_count-- > 0) {
96     if (!ReadFully(fd, buf.data(), block_size)) {
97       return Error() << "Failed to read";
98     }
99     if (!builder->Update(buf.data(), block_size)) {
100       return Error() << "Failed to build hashtree: Update";
101     }
102   }
103   if (!builder->BuildHashTree()) {
104     return Error() << "Failed to build hashtree: incomplete data";
105   }
106 
107   auto golden_digest = HexToBin(verity_data.root_digest);
108   auto digest = builder->root_hash();
109   // This returns zero-padded digest.
110   // resize() it to compare with golden digest,
111   digest.resize(golden_digest.size());
112   if (digest != golden_digest) {
113     return Error() << "Failed to build hashtree: root digest mismatch";
114   }
115 
116   unique_fd out_fd(TEMP_FAILURE_RETRY(open(
117       hashtree_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600)));
118   if (!builder->WriteHashTreeToFd(out_fd, 0)) {
119     return Error() << "Failed to write hashtree to " << hashtree_file;
120   }
121   return {};
122 }
123 
CalculateRootDigest(const std::string & hashtree_file,const ApexVerityData & verity_data)124 Result<std::string> CalculateRootDigest(const std::string& hashtree_file,
125                                         const ApexVerityData& verity_data) {
126   unique_fd fd(
127       TEMP_FAILURE_RETRY(open(hashtree_file.c_str(), O_RDONLY | O_CLOEXEC)));
128   if (fd.get() == -1) {
129     return ErrnoError() << "Failed to open " << hashtree_file;
130   }
131   auto block_size = verity_data.desc->hash_block_size;
132   auto image_size = verity_data.desc->image_size;
133   std::vector<uint8_t> root_verity(block_size);
134   if (!ReadFully(fd.get(), root_verity.data(), block_size)) {
135     return ErrnoError() << "Failed to read " << block_size << " bytes from "
136                         << hashtree_file;
137   }
138   auto hash_fn = HashTreeBuilder::HashFunction(verity_data.hash_algorithm);
139   if (hash_fn == nullptr) {
140     return Error() << "Unsupported hash algorithm "
141                    << verity_data.hash_algorithm;
142   }
143   auto builder = std::make_unique<HashTreeBuilder>(block_size, hash_fn);
144   if (!builder->Initialize(image_size, HexToBin(verity_data.salt))) {
145     return Error() << "Invalid image size " << image_size;
146   }
147   std::vector<unsigned char> root_digest;
148   if (!builder->CalculateRootDigest(root_verity, &root_digest)) {
149     return Error() << "Failed to calculate digest of " << hashtree_file;
150   }
151   auto result = HashTreeBuilder::BytesArrayToString(root_digest);
152   result.resize(verity_data.root_digest.size());
153   return result;
154 }
155 
156 }  // namespace
157 
PrepareHashTree(const ApexFile & apex,const ApexVerityData & verity_data,const std::string & hashtree_file)158 Result<PrepareHashTreeResult> PrepareHashTree(
159     const ApexFile& apex, const ApexVerityData& verity_data,
160     const std::string& hashtree_file) {
161   if (apex.IsCompressed()) {
162     return Error() << "Cannot prepare HashTree of compressed APEX";
163   }
164 
165   if (auto st = CreateDirIfNeeded(Dirname(hashtree_file), 0700); !st.ok()) {
166     return st.error();
167   }
168   bool should_regenerate_hashtree = false;
169   auto exists = PathExists(hashtree_file);
170   if (!exists.ok()) {
171     return exists.error();
172   }
173   if (*exists) {
174     auto digest = CalculateRootDigest(hashtree_file, verity_data);
175     if (!digest.ok()) {
176       return digest.error();
177     }
178     if (*digest != verity_data.root_digest) {
179       LOG(ERROR) << "Regenerating hashtree! Digest of " << hashtree_file
180                  << " does not match digest of " << apex.GetPath() << " : "
181                  << *digest << "\nvs\n"
182                  << verity_data.root_digest;
183       should_regenerate_hashtree = true;
184     }
185   } else {
186     should_regenerate_hashtree = true;
187   }
188 
189   if (should_regenerate_hashtree) {
190     if (auto st = GenerateHashTree(apex, verity_data, hashtree_file);
191         !st.ok()) {
192       return st.error();
193     }
194     LOG(INFO) << "hashtree: generated to " << hashtree_file;
195     return KRegenerate;
196   }
197   LOG(INFO) << "hashtree: reuse " << hashtree_file;
198   return kReuse;
199 }
200 
RemoveObsoleteHashTrees()201 void RemoveObsoleteHashTrees() {
202   // TODO(b/120058143): on boot complete, remove unused hashtree files
203 }
204 
BytesToHex(const uint8_t * bytes,size_t bytes_len)205 std::string BytesToHex(const uint8_t* bytes, size_t bytes_len) {
206   std::ostringstream s;
207 
208   s << std::hex << std::setfill('0');
209   for (size_t i = 0; i < bytes_len; i++) {
210     s << std::setw(2) << static_cast<int>(bytes[i]);
211   }
212   return s.str();
213 }
214 
215 }  // namespace apex
216 }  // namespace android
217