| /* | |
| * 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; | |
| } |