blob: a4be3363d3ebb0823429c32ae7ba5b652f31ab8b [file] [log] [blame]
Jooyung Hanf7c8d032019-04-11 15:12:09 +09001/*
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 <filesystem>
21#include <vector>
22
23#include <android-base/file.h>
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010024#include <android-base/result.h>
Jooyung Hanf7c8d032019-04-11 15:12:09 +090025#include <android-base/unique_fd.h>
26#include <verity/hash_tree_builder.h>
27
Nikita Ioffe695b0a32019-12-05 17:17:41 +000028#include "apex_constants.h"
Jooyung Hanf7c8d032019-04-11 15:12:09 +090029#include "apex_file.h"
Jooyung Hanf7c8d032019-04-11 15:12:09 +090030#include "apexd_utils.h"
Jooyung Hanf7c8d032019-04-11 15:12:09 +090031
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010032using android::base::ErrnoError;
33using android::base::Error;
Jooyung Hanf7c8d032019-04-11 15:12:09 +090034using android::base::ReadFully;
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010035using android::base::Result;
Jooyung Hanf7c8d032019-04-11 15:12:09 +090036using android::base::unique_fd;
37
38namespace android {
39namespace apex {
40
41namespace {
42
Jooyung Hanf7c8d032019-04-11 15:12:09 +090043uint8_t HexToBin(char h) {
44 if (h >= 'A' && h <= 'H') return h - 'A' + 10;
45 if (h >= 'a' && h <= 'h') return h - 'a' + 10;
46 return h - '0';
47}
48
49std::vector<uint8_t> HexToBin(const std::string& hex) {
50 std::vector<uint8_t> bin;
51 bin.reserve(hex.size() / 2);
52 for (size_t i = 0; i + 1 < hex.size(); i += 2) {
53 uint8_t c = (HexToBin(hex[i]) << 4) + HexToBin(hex[i + 1]);
54 bin.push_back(c);
55 }
56 return bin;
57}
58
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010059Result<void> GenerateHashTree(const ApexFile& apex,
60 const ApexVerityData& verity_data,
61 const std::string& hashtree_file) {
Jooyung Hanf7c8d032019-04-11 15:12:09 +090062 unique_fd fd(TEMP_FAILURE_RETRY(open(apex.GetPath().c_str(), O_RDONLY)));
63 if (fd.get() == -1) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010064 return ErrnoError() << "Failed to open " << apex.GetPath();
Jooyung Hanf7c8d032019-04-11 15:12:09 +090065 }
66
67 auto block_size = verity_data.desc->hash_block_size;
68 auto image_size = verity_data.desc->image_size;
69
70 auto hash_fn = HashTreeBuilder::HashFunction(verity_data.hash_algorithm);
71 if (hash_fn == nullptr) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010072 return Error() << "Unsupported hash algorithm "
73 << verity_data.hash_algorithm;
Jooyung Hanf7c8d032019-04-11 15:12:09 +090074 }
75
76 auto builder = std::make_unique<HashTreeBuilder>(block_size, hash_fn);
77 if (!builder->Initialize(image_size, HexToBin(verity_data.salt))) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010078 return Error() << "Invalid image size " << image_size;
Jooyung Hanf7c8d032019-04-11 15:12:09 +090079 }
80
81 if (lseek(fd, apex.GetImageOffset(), SEEK_SET) == -1) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010082 return ErrnoError() << "Failed to seek";
Jooyung Hanf7c8d032019-04-11 15:12:09 +090083 }
84
85 auto block_count = image_size / block_size;
86 auto buf = std::vector<uint8_t>(block_size);
87 while (block_count-- > 0) {
88 if (!ReadFully(fd, buf.data(), block_size)) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010089 return Error() << "Failed to read";
Jooyung Hanf7c8d032019-04-11 15:12:09 +090090 }
91 if (!builder->Update(buf.data(), block_size)) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010092 return Error() << "Failed to build hashtree: Update";
Jooyung Hanf7c8d032019-04-11 15:12:09 +090093 }
94 }
95 if (!builder->BuildHashTree()) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010096 return Error() << "Failed to build hashtree: incomplete data";
Jooyung Hanf7c8d032019-04-11 15:12:09 +090097 }
98
99 auto golden_digest = HexToBin(verity_data.root_digest);
100 auto digest = builder->root_hash();
101 // This returns zero-padded digest.
102 // resize() it to compare with golden digest,
103 digest.resize(golden_digest.size());
104 if (digest != golden_digest) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100105 return Error() << "Failed to build hashtree: root digest mismatch";
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900106 }
107
108 unique_fd out_fd(TEMP_FAILURE_RETRY(
109 open(hashtree_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0600)));
110 if (!builder->WriteHashTreeToFd(out_fd, 0)) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100111 return Error() << "Failed to write hashtree to " << hashtree_file;
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900112 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100113 return {};
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900114}
115
Nikita Ioffeff49d522020-01-05 02:29:03 +0000116Result<std::string> CalculateRootDigest(const std::string& hashtree_file,
117 const ApexVerityData& verity_data) {
118 unique_fd fd(TEMP_FAILURE_RETRY(open(hashtree_file.c_str(), O_RDONLY)));
119 if (fd.get() == -1) {
120 return ErrnoError() << "Failed to open " << hashtree_file;
121 }
122 auto block_size = verity_data.desc->hash_block_size;
123 auto image_size = verity_data.desc->image_size;
124 std::vector<uint8_t> root_verity(block_size);
125 if (!ReadFully(fd.get(), root_verity.data(), block_size)) {
126 return ErrnoError() << "Failed to read " << block_size << " bytes from "
127 << hashtree_file;
128 }
129 auto hash_fn = HashTreeBuilder::HashFunction(verity_data.hash_algorithm);
130 if (hash_fn == nullptr) {
131 return Error() << "Unsupported hash algorithm "
132 << verity_data.hash_algorithm;
133 }
134 auto builder = std::make_unique<HashTreeBuilder>(block_size, hash_fn);
135 if (!builder->Initialize(image_size, HexToBin(verity_data.salt))) {
136 return Error() << "Invalid image size " << image_size;
137 }
138 std::vector<unsigned char> root_digest;
139 if (!builder->CalculateRootDigest(root_verity, &root_digest)) {
140 return Error() << "Failed to calculate digest of " << hashtree_file;
141 }
142 auto result = HashTreeBuilder::BytesArrayToString(root_digest);
143 result.resize(verity_data.root_digest.size());
144 return result;
145}
146
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900147} // namespace
148
Nikita Ioffeff49d522020-01-05 02:29:03 +0000149Result<PrepareHashTreeResult> PrepareHashTree(
Nikita Ioffe695b0a32019-12-05 17:17:41 +0000150 const ApexFile& apex, const ApexVerityData& verity_data,
151 const std::string& hashtree_file) {
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900152 if (auto st = createDirIfNeeded(kApexHashTreeDir, 0700); !st.ok()) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100153 return st.error();
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900154 }
Nikita Ioffeff49d522020-01-05 02:29:03 +0000155 bool should_regenerate_hashtree = false;
156 auto exists = PathExists(hashtree_file);
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900157 if (!exists.ok()) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100158 return exists.error();
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900159 }
Nikita Ioffeff49d522020-01-05 02:29:03 +0000160 if (*exists) {
161 auto digest = CalculateRootDigest(hashtree_file, verity_data);
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900162 if (!digest.ok()) {
Nikita Ioffeff49d522020-01-05 02:29:03 +0000163 return digest.error();
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900164 }
Nikita Ioffeff49d522020-01-05 02:29:03 +0000165 if (*digest != verity_data.root_digest) {
166 LOG(ERROR) << "Regenerating hashtree! Digest of " << hashtree_file
167 << " does not match digest of " << apex.GetPath() << " : "
168 << *digest << "\nvs\n"
169 << verity_data.root_digest;
170 should_regenerate_hashtree = true;
171 }
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900172 } else {
Nikita Ioffeff49d522020-01-05 02:29:03 +0000173 should_regenerate_hashtree = true;
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900174 }
175
Nikita Ioffeff49d522020-01-05 02:29:03 +0000176 if (should_regenerate_hashtree) {
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900177 if (auto st = GenerateHashTree(apex, verity_data, hashtree_file);
178 !st.ok()) {
Nikita Ioffeff49d522020-01-05 02:29:03 +0000179 return st.error();
180 }
181 LOG(INFO) << "hashtree: generated to " << hashtree_file;
182 return KRegenerate;
183 }
184 LOG(INFO) << "hashtree: reuse " << hashtree_file;
185 return kReuse;
Jooyung Hanf7c8d032019-04-11 15:12:09 +0900186}
187
188void RemoveObsoleteHashTrees() {
189 // TODO(b/120058143): on boot complete, remove unused hashtree files
190}
191
192} // namespace apex
Nikita Ioffe695b0a32019-12-05 17:17:41 +0000193} // namespace android