You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
6.0 KiB
165 lines
6.0 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "avb_utils.h"
|
|
|
|
#include <android-base/strings.h>
|
|
#include <libavb/libavb.h>
|
|
|
|
#include "fec_private.h"
|
|
|
|
int parse_vbmeta_from_footer(fec_handle *f, std::vector<uint8_t> *vbmeta) {
|
|
if (f->size <= AVB_FOOTER_SIZE) {
|
|
debug("file size not large enough to be avb images:" PRIu64, f->size);
|
|
return -1;
|
|
}
|
|
|
|
AvbFooter footer_read;
|
|
if (!raw_pread(f->fd, &footer_read, AVB_FOOTER_SIZE,
|
|
f->size - AVB_FOOTER_SIZE)) {
|
|
error("failed to read footer: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
AvbFooter footer;
|
|
if (!avb_footer_validate_and_byteswap(&footer_read, &footer)) {
|
|
debug("invalid avb footer");
|
|
return -1;
|
|
}
|
|
uint64_t vbmeta_offset = footer.vbmeta_offset;
|
|
uint64_t vbmeta_size = footer.vbmeta_size;
|
|
check(vbmeta_offset <= f->size - sizeof(footer) - vbmeta_size);
|
|
|
|
std::vector<uint8_t> vbmeta_data(vbmeta_size, 0);
|
|
// TODO(xunchang) handle the sparse image with libsparse.
|
|
if (!raw_pread(f->fd, vbmeta_data.data(), vbmeta_data.size(),
|
|
vbmeta_offset)) {
|
|
error("failed to read avb vbmeta: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (auto status = avb_vbmeta_image_verify(
|
|
vbmeta_data.data(), vbmeta_data.size(), nullptr, nullptr);
|
|
status != AVB_VBMETA_VERIFY_RESULT_OK &&
|
|
status != AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED) {
|
|
error("failed to verify avb vbmeta, status: %d", status);
|
|
return -1;
|
|
}
|
|
*vbmeta = std::move(vbmeta_data);
|
|
return 0;
|
|
}
|
|
|
|
int parse_avb_image(fec_handle *f, const std::vector<uint8_t> &vbmeta) {
|
|
// TODO(xunchang) check if avb verification or hashtree is disabled.
|
|
|
|
// Look for the hashtree descriptor, we expect exactly one descriptor in
|
|
// vbmeta.
|
|
// TODO(xunchang) handle the image with AvbHashDescriptor.
|
|
auto parse_descriptor = [](const AvbDescriptor *descriptor,
|
|
void *user_data) {
|
|
if (descriptor &&
|
|
avb_be64toh(descriptor->tag) == AVB_DESCRIPTOR_TAG_HASHTREE) {
|
|
auto desp = static_cast<const AvbDescriptor **>(user_data);
|
|
*desp = descriptor;
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const AvbHashtreeDescriptor *hashtree_descriptor_ptr = nullptr;
|
|
avb_descriptor_foreach(vbmeta.data(), vbmeta.size(), parse_descriptor,
|
|
&hashtree_descriptor_ptr);
|
|
if (!hashtree_descriptor_ptr) {
|
|
error("failed to find avb hashtree descriptor");
|
|
return -1;
|
|
}
|
|
|
|
AvbHashtreeDescriptor hashtree_descriptor;
|
|
if (!avb_hashtree_descriptor_validate_and_byteswap(hashtree_descriptor_ptr,
|
|
&hashtree_descriptor)) {
|
|
error("failed to verify avb hashtree descriptor");
|
|
return -1;
|
|
}
|
|
|
|
// The partition name, salt, root append right after the hashtree
|
|
// descriptor.
|
|
auto read_ptr = reinterpret_cast<const uint8_t *>(hashtree_descriptor_ptr);
|
|
// Calculate the offset with respect to the vbmeta; and check both the
|
|
// salt & root are within the range.
|
|
uint32_t salt_offset =
|
|
sizeof(AvbHashtreeDescriptor) + hashtree_descriptor.partition_name_len;
|
|
uint32_t root_offset = salt_offset + hashtree_descriptor.salt_len;
|
|
check(hashtree_descriptor.salt_len < vbmeta.size());
|
|
check(salt_offset < vbmeta.size() - hashtree_descriptor.salt_len);
|
|
check(hashtree_descriptor.root_digest_len < vbmeta.size());
|
|
check(root_offset < vbmeta.size() - hashtree_descriptor.root_digest_len);
|
|
std::vector<uint8_t> salt(
|
|
read_ptr + salt_offset,
|
|
read_ptr + salt_offset + hashtree_descriptor.salt_len);
|
|
std::vector<uint8_t> root_hash(
|
|
read_ptr + root_offset,
|
|
read_ptr + root_offset + hashtree_descriptor.root_digest_len);
|
|
|
|
// Expect the AVB image has the format:
|
|
// 1. hashtree
|
|
// 2. ecc data
|
|
// 3. vbmeta
|
|
// 4. avb footer
|
|
check(hashtree_descriptor.fec_offset ==
|
|
hashtree_descriptor.tree_offset + hashtree_descriptor.tree_size);
|
|
check(hashtree_descriptor.fec_offset <=
|
|
f->size - hashtree_descriptor.fec_size);
|
|
|
|
f->data_size = hashtree_descriptor.fec_offset;
|
|
|
|
f->ecc.blocks = fec_div_round_up(f->data_size, FEC_BLOCKSIZE);
|
|
f->ecc.rounds = fec_div_round_up(f->ecc.blocks, f->ecc.rsn);
|
|
f->ecc.size = hashtree_descriptor.fec_size;
|
|
f->ecc.start = hashtree_descriptor.fec_offset;
|
|
// TODO(xunchang) verify the integrity of the ecc data.
|
|
f->ecc.valid = true;
|
|
|
|
std::string hash_algorithm =
|
|
reinterpret_cast<char *>(hashtree_descriptor.hash_algorithm);
|
|
int nid = -1;
|
|
if (android::base::EqualsIgnoreCase(hash_algorithm, "sha1")) {
|
|
nid = NID_sha1;
|
|
} else if (android::base::EqualsIgnoreCase(hash_algorithm, "sha256")) {
|
|
nid = NID_sha256;
|
|
} else {
|
|
error("unsupported hash algorithm %s", hash_algorithm.c_str());
|
|
}
|
|
|
|
hashtree_info hashtree;
|
|
hashtree.initialize(hashtree_descriptor.tree_offset,
|
|
hashtree_descriptor.tree_offset / FEC_BLOCKSIZE, salt,
|
|
nid);
|
|
if (hashtree.verify_tree(f, root_hash.data()) != 0) {
|
|
error("failed to verify hashtree");
|
|
return -1;
|
|
}
|
|
|
|
// We have validate the hashtree,
|
|
f->data_size = hashtree.hash_start;
|
|
f->avb = {
|
|
.valid = true,
|
|
.vbmeta = vbmeta,
|
|
.hashtree = std::move(hashtree),
|
|
};
|
|
|
|
return 0;
|
|
}
|