/* * 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 #include #include "fec_private.h" int parse_vbmeta_from_footer(fec_handle *f, std::vector *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 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 &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(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(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 salt( read_ptr + salt_offset, read_ptr + salt_offset + hashtree_descriptor.salt_len); std::vector 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(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; }