| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Permission is hereby granted, free of charge, to any person |
| * obtaining a copy of this software and associated documentation |
| * files (the "Software"), to deal in the Software without |
| * restriction, including without limitation the rights to use, copy, |
| * modify, merge, publish, distribute, sublicense, and/or sell copies |
| * of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <libavb/avb_crypto.h> |
| #include <libavb/avb_rsa.h> |
| #include <libavb/avb_sha.h> |
| #include <libavb/avb_util.h> |
| |
| #include "avb_aftl_types.h" |
| #include "avb_aftl_util.h" |
| #include "avb_aftl_validate.h" |
| |
| /* Performs a SHA256 hash operation on data. */ |
| bool avb_aftl_sha256(uint8_t* data, |
| uint64_t length, |
| uint8_t hash[AVB_AFTL_HASH_SIZE]) { |
| AvbSHA256Ctx context; |
| uint8_t* tmp; |
| |
| if ((data == NULL) && (length != 0)) return false; |
| |
| avb_sha256_init(&context); |
| avb_sha256_update(&context, data, length); |
| tmp = avb_sha256_final(&context); |
| avb_memcpy(hash, tmp, AVB_AFTL_HASH_SIZE); |
| return true; |
| } |
| |
| /* Computes a leaf hash as detailed by https://tools.ietf.org/html/rfc6962. */ |
| bool avb_aftl_rfc6962_hash_leaf(uint8_t* leaf, |
| uint64_t leaf_size, |
| uint8_t* hash) { |
| uint8_t* buffer; |
| bool retval; |
| |
| avb_assert(leaf != NULL && hash != NULL); |
| avb_assert(leaf_size != AVB_AFTL_UINT64_MAX); |
| |
| buffer = (uint8_t*)avb_malloc(leaf_size + 1); |
| |
| if (buffer == NULL) { |
| avb_error("Allocation failure in avb_aftl_rfc6962_hash_leaf.\n"); |
| return false; |
| } |
| /* Prefix the data with a '0' for 2nd preimage attack resistance. */ |
| buffer[0] = 0; |
| |
| if (leaf_size > 0) avb_memcpy(buffer + 1, leaf, leaf_size); |
| |
| retval = avb_aftl_sha256(buffer, leaf_size + 1, hash); |
| avb_free(buffer); |
| return retval; |
| } |
| |
| /* Computes an inner hash as detailed by https://tools.ietf.org/html/rfc6962. */ |
| bool avb_aftl_rfc6962_hash_children(uint8_t* left_child, |
| uint64_t left_child_size, |
| uint8_t* right_child, |
| uint64_t right_child_size, |
| uint8_t* hash) { |
| uint8_t* buffer; |
| uint64_t data_size; |
| bool retval; |
| |
| avb_assert(left_child != NULL && right_child != NULL && hash != NULL); |
| |
| /* Check for integer overflow. */ |
| avb_assert(left_child_size < AVB_AFTL_UINT64_MAX - right_child_size); |
| |
| data_size = left_child_size + right_child_size + 1; |
| buffer = (uint8_t*)avb_malloc(data_size); |
| if (buffer == NULL) { |
| avb_error("Allocation failure in avb_aftl_rfc6962_hash_children.\n"); |
| return false; |
| } |
| |
| /* Prefix the data with '1' for 2nd preimage attack resistance. */ |
| buffer[0] = 1; |
| |
| /* Copy the left child data, if it exists. */ |
| if (left_child_size > 0) avb_memcpy(buffer + 1, left_child, left_child_size); |
| /* Copy the right child data, if it exists. */ |
| if (right_child_size > 0) |
| avb_memcpy(buffer + 1 + left_child_size, right_child, right_child_size); |
| |
| /* Hash the concatenated data and clean up. */ |
| retval = avb_aftl_sha256(buffer, data_size, hash); |
| avb_free(buffer); |
| return retval; |
| } |
| |
| /* Computes a subtree hash along tree's right border. */ |
| bool avb_aftl_chain_border_right(uint8_t* seed, |
| uint64_t seed_size, |
| uint8_t* proof, |
| uint32_t proof_entry_count, |
| uint8_t* hash) { |
| size_t i; |
| uint8_t* tmp_hash; |
| uint8_t* tmp = seed; |
| bool retval = true; |
| |
| avb_assert(seed_size == AVB_AFTL_HASH_SIZE); |
| avb_assert(seed != NULL && proof != NULL && hash != NULL); |
| |
| tmp_hash = (uint8_t*)avb_malloc(AVB_AFTL_HASH_SIZE); |
| if (tmp_hash == NULL) { |
| avb_error("Allocation failure in avb_aftl_chain_border_right.\n"); |
| return false; |
| } |
| for (i = 0; i < proof_entry_count; i++) { |
| retval = avb_aftl_rfc6962_hash_children(proof + (i * AVB_AFTL_HASH_SIZE), |
| AVB_AFTL_HASH_SIZE, |
| tmp, |
| AVB_AFTL_HASH_SIZE, |
| tmp_hash); |
| if (!retval) { |
| avb_error("Failed to hash Merkle tree children.\n"); |
| break; |
| } |
| tmp = tmp_hash; |
| } |
| |
| if (retval) avb_memcpy(hash, tmp, AVB_AFTL_HASH_SIZE); |
| |
| avb_free(tmp_hash); |
| return retval; |
| } |
| |
| /* Computes a subtree hash on or below the tree's right border. */ |
| bool avb_aftl_chain_inner(uint8_t* seed, |
| uint64_t seed_size, |
| uint8_t* proof, |
| uint32_t proof_entry_count, |
| uint64_t leaf_index, |
| uint8_t* hash) { |
| size_t i; |
| uint8_t* tmp_hash; |
| uint8_t* tmp = seed; |
| bool retval = true; |
| |
| avb_assert(seed_size == AVB_AFTL_HASH_SIZE); |
| avb_assert(seed != NULL && proof != NULL && hash != NULL); |
| |
| tmp_hash = (uint8_t*)avb_malloc(AVB_AFTL_HASH_SIZE); |
| if (tmp_hash == NULL) { |
| avb_error("Allocation failure in avb_aftl_chain_inner.\n"); |
| return false; |
| } |
| for (i = 0; i < proof_entry_count; i++) { |
| if ((leaf_index >> i & 1) == 0) { |
| retval = avb_aftl_rfc6962_hash_children(tmp, |
| seed_size, |
| proof + (i * AVB_AFTL_HASH_SIZE), |
| AVB_AFTL_HASH_SIZE, |
| tmp_hash); |
| } else { |
| retval = avb_aftl_rfc6962_hash_children(proof + (i * AVB_AFTL_HASH_SIZE), |
| AVB_AFTL_HASH_SIZE, |
| tmp, |
| seed_size, |
| tmp_hash); |
| } |
| if (!retval) { |
| avb_error("Failed to hash Merkle tree children.\n"); |
| break; |
| } |
| tmp = tmp_hash; |
| } |
| if (retval) avb_memcpy(hash, tmp, AVB_AFTL_HASH_SIZE); |
| avb_free(tmp_hash); |
| return retval; |
| } |
| |
| /* Counts leading zeros. Used in Merkle tree hash validation .*/ |
| unsigned int avb_aftl_count_leading_zeros(uint64_t val) { |
| int r = 0; |
| if (val == 0) return 64; |
| if (!(val & 0xffffffff00000000u)) { |
| val <<= 32; |
| r += 32; |
| } |
| if (!(val & 0xffff000000000000u)) { |
| val <<= 16; |
| r += 16; |
| } |
| if (!(val & 0xff00000000000000u)) { |
| val <<= 8; |
| r += 8; |
| } |
| if (!(val & 0xf000000000000000u)) { |
| val <<= 4; |
| r += 4; |
| } |
| if (!(val & 0xc000000000000000u)) { |
| val <<= 2; |
| r += 2; |
| } |
| if (!(val & 0x8000000000000000u)) { |
| val <<= 1; |
| r += 1; |
| } |
| |
| return r; |
| } |
| |
| /* Calculates the expected Merkle tree hash. */ |
| bool avb_aftl_root_from_icp(uint64_t leaf_index, |
| uint64_t tree_size, |
| uint8_t proof[][AVB_AFTL_HASH_SIZE], |
| uint32_t proof_entry_count, |
| uint8_t* leaf_hash, |
| uint64_t leaf_hash_size, |
| uint8_t* root_hash) { |
| uint64_t inner_proof_size; |
| uint64_t border_proof_size; |
| size_t i; |
| uint8_t hash[AVB_AFTL_HASH_SIZE]; |
| uint8_t* inner_proof; |
| uint8_t* border_proof; |
| bool retval; |
| |
| avb_assert(proof_entry_count != 0); |
| avb_assert(leaf_hash_size != 0); |
| avb_assert(proof != NULL && leaf_hash != NULL && root_hash != NULL); |
| |
| /* This cannot overflow. */ |
| inner_proof_size = |
| 64 - avb_aftl_count_leading_zeros(leaf_index ^ (tree_size - 1)); |
| |
| /* Check for integer underflow.*/ |
| if ((proof_entry_count - inner_proof_size) > proof_entry_count) { |
| avb_error("Invalid proof entry count value.\n"); |
| return false; |
| } |
| border_proof_size = proof_entry_count - inner_proof_size; |
| /* Split the proof into two parts based on the calculated pivot point. */ |
| inner_proof = (uint8_t*)avb_malloc(inner_proof_size * AVB_AFTL_HASH_SIZE); |
| if (inner_proof == NULL) { |
| avb_error("Allocation failure in avb_aftl_root_from_icp.\n"); |
| return false; |
| } |
| border_proof = (uint8_t*)avb_malloc(border_proof_size * AVB_AFTL_HASH_SIZE); |
| if (border_proof == NULL) { |
| avb_free(inner_proof); |
| avb_error("Allocation failure in avb_aftl_root_from_icp.\n"); |
| return false; |
| } |
| |
| for (i = 0; i < inner_proof_size; i++) { |
| avb_memcpy( |
| inner_proof + (AVB_AFTL_HASH_SIZE * i), proof[i], AVB_AFTL_HASH_SIZE); |
| } |
| for (i = 0; i < border_proof_size; i++) { |
| avb_memcpy(border_proof + (AVB_AFTL_HASH_SIZE * i), |
| proof[inner_proof_size + i], |
| AVB_AFTL_HASH_SIZE); |
| } |
| |
| /* Calculate the root hash and store it in root_hash. */ |
| retval = avb_aftl_chain_inner(leaf_hash, |
| leaf_hash_size, |
| inner_proof, |
| inner_proof_size, |
| leaf_index, |
| hash); |
| if (retval) |
| retval = avb_aftl_chain_border_right( |
| hash, AVB_AFTL_HASH_SIZE, border_proof, border_proof_size, root_hash); |
| |
| if (inner_proof != NULL) avb_free(inner_proof); |
| if (border_proof != NULL) avb_free(border_proof); |
| return retval; |
| } |
| |
| /* Defines helper functions read_u8, read_u16, read_u32 and read_u64. These |
| * functions can be used to read from a |data| stream a |value| of a specific |
| * size. The value endianness is converted from big-endian to host. We ensure |
| * that the read do not overflow beyond |data_end|. If successful, |data| is |
| * brought forward by the size of the value read. |
| */ |
| #define _read_u(fct) \ |
| { \ |
| size_t value_size = sizeof(*value); \ |
| if ((*data + value_size) < *data) return false; \ |
| if ((*data + value_size) > data_end) return false; \ |
| avb_memcpy(value, *data, value_size); \ |
| *value = fct(*value); \ |
| *data += value_size; \ |
| return true; \ |
| } |
| static bool read_u8(uint8_t* value, uint8_t** data, uint8_t* data_end) { |
| _read_u(); |
| } |
| AVB_ATTR_WARN_UNUSED_RESULT |
| static bool read_u16(uint16_t* value, uint8_t** data, uint8_t* data_end) { |
| _read_u(avb_be16toh); |
| } |
| AVB_ATTR_WARN_UNUSED_RESULT |
| static bool read_u32(uint32_t* value, uint8_t** data, uint8_t* data_end) { |
| _read_u(avb_be32toh); |
| } |
| AVB_ATTR_WARN_UNUSED_RESULT |
| static bool read_u64(uint64_t* value, uint8_t** data, uint8_t* data_end) { |
| _read_u(avb_be64toh); |
| } |
| AVB_ATTR_WARN_UNUSED_RESULT |
| |
| /* Allocates |value_size| bytes into |value| and copy |value_size| bytes from |
| * |data|. Ensure that we don't overflow beyond |data_end|. It is the caller |
| * responsibility to avb_free |value|. Advances the |data| pointer pass the |
| * value that has been read. Returns false if an overflow would have occurred or |
| * if the allocation failed. |
| */ |
| static bool read_mem(uint8_t** value, |
| size_t value_size, |
| uint8_t** data, |
| uint8_t* data_end) { |
| if (*data + value_size < *data || *data + value_size > data_end) { |
| return false; |
| } |
| *value = (uint8_t*)avb_calloc(value_size); |
| if (!value) { |
| return false; |
| } |
| avb_memcpy(*value, *data, value_size); |
| *data += value_size; |
| return true; |
| } |
| |
| /* Allocates and populates a TrillianLogRootDescriptor element in an |
| AftlIcpEntry from a binary blob. |
| The blob is expected to be pointing to the beginning of a |
| serialized TrillianLogRootDescriptor element of an AftlIcpEntry. |
| The aftl_blob argument is updated to point to the area after the |
| TrillianLogRootDescriptor. aftl_blob_remaining gives the amount of the |
| aftl_blob that is left to parse. */ |
| static bool parse_trillian_log_root_descriptor(AftlIcpEntry* icp_entry, |
| uint8_t** aftl_blob, |
| size_t aftl_blob_remaining) { |
| avb_assert(icp_entry); |
| avb_assert(aftl_blob); |
| uint8_t* blob_end = *aftl_blob + aftl_blob_remaining; |
| if (*aftl_blob > blob_end) { |
| return false; |
| } |
| |
| /* Copy in the version field from the blob. */ |
| if (!read_u16( |
| &(icp_entry->log_root_descriptor.version), aftl_blob, blob_end)) { |
| avb_error("Unable to parse version.\n"); |
| return false; |
| } |
| |
| /* Copy in the tree size field from the blob. */ |
| if (!read_u64( |
| &(icp_entry->log_root_descriptor.tree_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse tree size.\n"); |
| return false; |
| } |
| |
| /* Copy in the root hash size field from the blob. */ |
| if (!read_u8(&(icp_entry->log_root_descriptor.root_hash_size), |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse root hash size.\n"); |
| return false; |
| } |
| if (icp_entry->log_root_descriptor.root_hash_size != AVB_AFTL_HASH_SIZE) { |
| avb_error("Invalid root hash size.\n"); |
| return false; |
| } |
| |
| /* Copy in the root hash from the blob. */ |
| if (!read_mem(&(icp_entry->log_root_descriptor.root_hash), |
| icp_entry->log_root_descriptor.root_hash_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse root hash.\n"); |
| return false; |
| } |
| |
| /* Copy in the timestamp field from the blob. */ |
| if (!read_u64( |
| &(icp_entry->log_root_descriptor.timestamp), aftl_blob, blob_end)) { |
| avb_error("Unable to parse timestamp.\n"); |
| return false; |
| } |
| |
| /* Copy in the revision field from the blob. */ |
| if (!read_u64( |
| &(icp_entry->log_root_descriptor.revision), aftl_blob, blob_end)) { |
| avb_error("Unable to parse revision.\n"); |
| return false; |
| } |
| |
| /* Copy in the metadata size field from the blob. */ |
| if (!read_u16(&(icp_entry->log_root_descriptor.metadata_size), |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse metadata size.\n"); |
| return false; |
| } |
| |
| if (icp_entry->log_root_descriptor.metadata_size > |
| AVB_AFTL_MAX_METADATA_SIZE) { |
| avb_error("Invalid metadata size.\n"); |
| return false; |
| } |
| |
| /* If it exists, copy in the metadata field from the blob. */ |
| if (icp_entry->log_root_descriptor.metadata_size > 0) { |
| if (!read_mem(&(icp_entry->log_root_descriptor.metadata), |
| icp_entry->log_root_descriptor.metadata_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse metadata.\n"); |
| return false; |
| } |
| } else { |
| icp_entry->log_root_descriptor.metadata = NULL; |
| } |
| return true; |
| } |
| |
| /* Parses a Signature from |aftl_blob| into leaf->signature. |
| * Returns false if an error occurred during the parsing */ |
| static bool parse_signature(SignedVBMetaPrimaryAnnotationLeaf* leaf, |
| uint8_t** aftl_blob, |
| uint8_t* blob_end) { |
| Signature* signature = (Signature*)avb_calloc(sizeof(Signature)); |
| if (!signature) { |
| avb_error("Failed to allocate signature.\n"); |
| return false; |
| } |
| leaf->signature = signature; |
| |
| if (!read_u8(&(signature->hash_algorithm), aftl_blob, blob_end)) { |
| avb_error("Unable to parse the hash algorithm.\n"); |
| return false; |
| } |
| if (signature->hash_algorithm >= _AVB_AFTL_HASH_ALGORITHM_NUM) { |
| avb_error("Unexpect hash algorithm in leaf signature.\n"); |
| return false; |
| } |
| |
| if (!read_u8(&(signature->signature_algorithm), aftl_blob, blob_end)) { |
| avb_error("Unable to parse the signature algorithm.\n"); |
| return false; |
| } |
| if (signature->signature_algorithm >= _AVB_AFTL_SIGNATURE_ALGORITHM_NUM) { |
| avb_error("Unexpect signature algorithm in leaf signature.\n"); |
| return false; |
| } |
| |
| if (!read_u16(&(signature->signature_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse the signature size.\n"); |
| return false; |
| } |
| if (!read_mem(&(signature->signature), |
| signature->signature_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse signature.\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| /* Parses an VBMetaPrimaryAnnotation from |aftl_blob| into leaf->annotation. |
| * Returns false if an error occurred during the parsing */ |
| static bool parse_annotation(SignedVBMetaPrimaryAnnotationLeaf* leaf, |
| uint8_t** aftl_blob, |
| uint8_t* blob_end) { |
| VBMetaPrimaryAnnotation* annotation = |
| (VBMetaPrimaryAnnotation*)avb_calloc(sizeof(VBMetaPrimaryAnnotation)); |
| if (!annotation) { |
| avb_error("Failed to allocate annotation.\n"); |
| return false; |
| } |
| leaf->annotation = annotation; |
| |
| if (!read_u8(&(annotation->vbmeta_hash_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse VBMeta hash size.\n"); |
| return false; |
| } |
| if (annotation->vbmeta_hash_size != AVB_AFTL_HASH_SIZE) { |
| avb_error("Unexpected VBMeta hash size.\n"); |
| return false; |
| } |
| if (!read_mem(&(annotation->vbmeta_hash), |
| annotation->vbmeta_hash_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse VBMeta hash.\n"); |
| return false; |
| } |
| |
| if (!read_u8(&(annotation->version_incremental_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse version incremental size.\n"); |
| return false; |
| } |
| if (!read_mem(&(annotation->version_incremental), |
| annotation->version_incremental_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse version incremental.\n"); |
| return false; |
| } |
| |
| if (!read_u8( |
| &(annotation->manufacturer_key_hash_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse manufacturer key hash size.\n"); |
| return false; |
| } |
| if (!read_mem(&(annotation->manufacturer_key_hash), |
| annotation->manufacturer_key_hash_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse manufacturer key hash.\n"); |
| return false; |
| } |
| |
| if (!read_u16(&(annotation->description_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse description size.\n"); |
| return false; |
| } |
| if (!read_mem(&(annotation->description), |
| annotation->description_size, |
| aftl_blob, |
| blob_end)) { |
| avb_error("Unable to parse description.\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| /* Allocates and populates a SignedVBMetaPrimaryAnnotationLeaf element in an |
| AftlIcpEntry from a binary blob. |
| The blob is expected to be pointing to the beginning of a |
| serialized SignedVBMetaPrimaryAnnotationLeaf element of an AftlIcpEntry. |
| The aftl_blob argument is updated to point to the area after the leaf. */ |
| static bool parse_annotation_leaf(AftlIcpEntry* icp_entry, |
| uint8_t** aftl_blob) { |
| SignedVBMetaPrimaryAnnotationLeaf* leaf; |
| uint8_t* blob_end = *aftl_blob + icp_entry->annotation_leaf_size; |
| if (*aftl_blob > blob_end) { |
| return false; |
| } |
| |
| leaf = (SignedVBMetaPrimaryAnnotationLeaf*)avb_calloc( |
| sizeof(SignedVBMetaPrimaryAnnotationLeaf)); |
| if (!leaf) { |
| avb_error("Failed to allocate for annotation leaf.\n"); |
| return false; |
| } |
| /* The leaf will be free'd within the free_aftl_icp_entry() */ |
| icp_entry->annotation_leaf = leaf; |
| if (!read_u8(&(leaf->version), aftl_blob, blob_end)) { |
| avb_error("Unable to parse version.\n"); |
| return false; |
| } |
| if (leaf->version != 1) { |
| avb_error("Unexpected leaf version.\n"); |
| return false; |
| } |
| if (!read_u64(&(leaf->timestamp), aftl_blob, blob_end)) { |
| avb_error("Unable to parse timestamp.\n"); |
| return false; |
| } |
| if (!read_u8(&(leaf->leaf_type), aftl_blob, blob_end)) { |
| avb_error("Unable to parse version.\n"); |
| return false; |
| } |
| if (leaf->leaf_type != AVB_AFTL_SIGNED_VBMETA_PRIMARY_ANNOTATION_LEAF) { |
| avb_error("Unexpected leaf type.\n"); |
| return false; |
| } |
| if (!parse_signature(leaf, aftl_blob, blob_end)) { |
| avb_error("Unable to parse signature.\n"); |
| return false; |
| } |
| if (!parse_annotation(leaf, aftl_blob, blob_end)) { |
| avb_error("Unable to parse annotation.\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| /* Allocates and populates an AftlIcpEntry from a binary blob. |
| The blob is expected to be pointing to the beginning of a |
| serialized AftlIcpEntry structure. */ |
| AftlIcpEntry* parse_icp_entry(uint8_t** aftl_blob, size_t* remaining_size) { |
| AftlIcpEntry* icp_entry; |
| uint8_t* blob_start = *aftl_blob; |
| uint8_t* blob_end = *aftl_blob + *remaining_size; |
| if (*aftl_blob > blob_end) { |
| return NULL; |
| } |
| |
| if (*remaining_size < AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE) { |
| avb_error("Invalid AftlImage\n"); |
| return NULL; |
| } |
| |
| icp_entry = (AftlIcpEntry*)avb_calloc(sizeof(AftlIcpEntry)); |
| if (!icp_entry) { |
| avb_error("Failure allocating AftlIcpEntry\n"); |
| return NULL; |
| } |
| |
| /* Copy in the log server URL size field. */ |
| if (!read_u32(&(icp_entry->log_url_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse log url size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| if (icp_entry->log_url_size > AVB_AFTL_MAX_URL_SIZE) { |
| avb_error("Invalid log URL size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| /* Copy in the leaf index field. */ |
| if (!read_u64(&(icp_entry->leaf_index), aftl_blob, blob_end)) { |
| avb_error("Unable to parse leaf_index.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| /* Copy in the TrillianLogRootDescriptor size field. */ |
| if (!read_u32(&(icp_entry->log_root_descriptor_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse log root descriptor size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| if (icp_entry->log_root_descriptor_size < AVB_AFTL_MIN_TLRD_SIZE || |
| icp_entry->log_root_descriptor_size > AVB_AFTL_MAX_TLRD_SIZE) { |
| avb_error("Invalid TrillianLogRootDescriptor size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| |
| /* Copy in the annotation leaf size field. */ |
| if (!read_u32(&(icp_entry->annotation_leaf_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse annotation leaf size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| if (icp_entry->annotation_leaf_size == 0 || |
| icp_entry->annotation_leaf_size > AVB_AFTL_MAX_ANNOTATION_SIZE) { |
| avb_error("Invalid annotation leaf size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| |
| /* Copy the log root signature size field. */ |
| if (!read_u16(&(icp_entry->log_root_sig_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse log root signature size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| if (icp_entry->log_root_sig_size != AVB_AFTL_SIGNATURE_SIZE) { |
| avb_error("Invalid log root signature size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| /* Copy the inclusion proof hash count field. */ |
| if (!read_u8(&(icp_entry->proof_hash_count), aftl_blob, blob_end)) { |
| avb_error("Unable to parse proof hash count.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| /* Copy the inclusion proof size field. */ |
| if (!read_u32(&(icp_entry->inc_proof_size), aftl_blob, blob_end)) { |
| avb_error("Unable to parse inclusion proof size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| if ((icp_entry->inc_proof_size != |
| icp_entry->proof_hash_count * AVB_AFTL_HASH_SIZE) || |
| (icp_entry->inc_proof_size > AVB_AFTL_MAX_PROOF_SIZE)) { |
| avb_error("Invalid inclusion proof size.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| /* Copy in the log server URL from the blob. */ |
| if (*aftl_blob + icp_entry->log_url_size < *aftl_blob || |
| *aftl_blob + icp_entry->log_url_size > blob_end) { |
| avb_error("Invalid AftlImage.\n"); |
| avb_free(icp_entry); |
| return NULL; |
| } |
| icp_entry->log_url = (uint8_t*)avb_calloc(icp_entry->log_url_size); |
| if (!icp_entry->log_url) { |
| avb_error("Failure to allocate URL.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| avb_memcpy(icp_entry->log_url, *aftl_blob, icp_entry->log_url_size); |
| *aftl_blob += icp_entry->log_url_size; |
| |
| /* Populate the TrillianLogRootDescriptor elements. */ |
| if (*aftl_blob + icp_entry->log_root_descriptor_size < *aftl_blob || |
| *aftl_blob + icp_entry->log_root_descriptor_size > blob_end) { |
| avb_error("Invalid AftlImage.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| icp_entry->log_root_descriptor_raw = |
| (uint8_t*)avb_calloc(icp_entry->log_root_descriptor_size); |
| if (!icp_entry->log_root_descriptor_raw) { |
| avb_error("Failure to allocate log root descriptor.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| avb_memcpy(icp_entry->log_root_descriptor_raw, |
| *aftl_blob, |
| icp_entry->log_root_descriptor_size); |
| if (!parse_trillian_log_root_descriptor( |
| icp_entry, aftl_blob, icp_entry->log_root_descriptor_size)) { |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| |
| /* Populate the annotation leaf. */ |
| if (*aftl_blob + icp_entry->annotation_leaf_size < *aftl_blob || |
| *aftl_blob + icp_entry->annotation_leaf_size > blob_end) { |
| avb_error("Invalid AftlImage.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| icp_entry->annotation_leaf_raw = |
| (uint8_t*)avb_calloc(icp_entry->annotation_leaf_size); |
| if (!icp_entry->annotation_leaf_raw) { |
| avb_error("Failure to allocate annotation leaf.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| avb_memcpy(icp_entry->annotation_leaf_raw, |
| *aftl_blob, |
| icp_entry->annotation_leaf_size); |
| if (!parse_annotation_leaf(icp_entry, aftl_blob)) { |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| |
| /* Allocate and copy the log root signature from the blob. */ |
| if (*aftl_blob + icp_entry->log_root_sig_size < *aftl_blob || |
| *aftl_blob + icp_entry->log_root_sig_size > blob_end) { |
| avb_error("Invalid AftlImage.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| icp_entry->log_root_signature = |
| (uint8_t*)avb_calloc(icp_entry->log_root_sig_size); |
| if (!icp_entry->log_root_signature) { |
| avb_error("Failure to allocate log root signature.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| avb_memcpy( |
| icp_entry->log_root_signature, *aftl_blob, icp_entry->log_root_sig_size); |
| *aftl_blob += icp_entry->log_root_sig_size; |
| |
| /* Finally, copy the proof hash data from the blob to the AftlImage. */ |
| if (*aftl_blob + icp_entry->inc_proof_size < *aftl_blob || |
| *aftl_blob + icp_entry->inc_proof_size > blob_end) { |
| avb_error("Invalid AftlImage.\n"); |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| icp_entry->proofs = avb_calloc(icp_entry->inc_proof_size); |
| if (!icp_entry->proofs) { |
| free_aftl_icp_entry(icp_entry); |
| return NULL; |
| } |
| avb_memcpy(icp_entry->proofs, *aftl_blob, icp_entry->inc_proof_size); |
| *aftl_blob += icp_entry->inc_proof_size; |
| |
| *remaining_size -= *aftl_blob - blob_start; |
| return icp_entry; |
| } |
| |
| /* Allocate and parse an AftlImage object out of binary data. */ |
| AftlImage* parse_aftl_image(uint8_t* aftl_blob, size_t aftl_blob_size) { |
| AftlImage* image; |
| AftlImageHeader* image_header; |
| AftlIcpEntry* entry; |
| size_t image_size; |
| size_t i; |
| size_t remaining_size; |
| |
| /* Ensure the blob is at least large enough for an AftlImageHeader */ |
| if (aftl_blob_size < sizeof(AftlImageHeader)) { |
| avb_error("Invalid image header.\n"); |
| return NULL; |
| } |
| image_header = (AftlImageHeader*)aftl_blob; |
| /* Check for the magic value for an AftlImageHeader. */ |
| if (image_header->magic != AVB_AFTL_MAGIC) { |
| avb_error("Invalid magic number\n"); |
| return NULL; |
| } |
| /* Extract the size out of the header. */ |
| image_size = avb_be32toh(image_header->image_size); |
| if (image_size < sizeof(AftlImageHeader) || |
| image_size > AVB_AFTL_MAX_AFTL_IMAGE_SIZE) { |
| avb_error("Invalid image size.\n"); |
| return NULL; |
| } |
| image = (AftlImage*)avb_calloc(sizeof(AftlImage)); |
| if (!image) { |
| avb_error("Failed allocation for AftlImage.\n"); |
| return NULL; |
| } |
| /* Copy the header bytes directly from the aftl_blob. */ |
| avb_memcpy(&(image->header), aftl_blob, sizeof(AftlImageHeader)); |
| /* Fix endianness. */ |
| image->header.required_icp_version_major = |
| avb_be32toh(image->header.required_icp_version_major); |
| image->header.required_icp_version_minor = |
| avb_be32toh(image->header.required_icp_version_minor); |
| image->header.image_size = avb_be32toh(image->header.image_size); |
| image->header.icp_count = avb_be16toh(image->header.icp_count); |
| /* Allocate memory for the entry array */ |
| image->entries = (AftlIcpEntry**)avb_calloc(sizeof(AftlIcpEntry*) * |
| image->header.icp_count); |
| if (!image->entries) { |
| avb_error("Failed allocation for AftlIcpEntry array.\n"); |
| avb_free(image); |
| return NULL; |
| } |
| |
| /* Jump past the header and parse out each AftlIcpEntry. */ |
| aftl_blob += sizeof(AftlImageHeader); |
| remaining_size = aftl_blob_size - sizeof(AftlImageHeader); |
| for (i = 0; i < image->header.icp_count && remaining_size > 0; i++) { |
| entry = parse_icp_entry(&aftl_blob, &remaining_size); |
| if (!entry) { |
| free_aftl_image(image); |
| return NULL; |
| } |
| image->entries[i] = entry; |
| } |
| |
| return image; |
| } |
| |
| /* Free an AftlIcpEntry and each allocated sub-element. */ |
| void free_aftl_icp_entry(AftlIcpEntry* icp_entry) { |
| /* Ensure the AftlIcpEntry exists before attempting to free it. */ |
| if (icp_entry) { |
| /* Free the log_url and log_root_signature elements if they exist. */ |
| if (icp_entry->log_url) avb_free(icp_entry->log_url); |
| if (icp_entry->log_root_signature) avb_free(icp_entry->log_root_signature); |
| /* Free the annotation elements if they exist. */ |
| if (icp_entry->annotation_leaf) { |
| if (icp_entry->annotation_leaf->signature) { |
| if (icp_entry->annotation_leaf->signature->signature) { |
| avb_free(icp_entry->annotation_leaf->signature->signature); |
| } |
| avb_free(icp_entry->annotation_leaf->signature); |
| } |
| if (icp_entry->annotation_leaf->annotation) { |
| if (icp_entry->annotation_leaf->annotation->vbmeta_hash) |
| avb_free(icp_entry->annotation_leaf->annotation->vbmeta_hash); |
| if (icp_entry->annotation_leaf->annotation->version_incremental) |
| avb_free(icp_entry->annotation_leaf->annotation->version_incremental); |
| if (icp_entry->annotation_leaf->annotation->manufacturer_key_hash) |
| avb_free( |
| icp_entry->annotation_leaf->annotation->manufacturer_key_hash); |
| if (icp_entry->annotation_leaf->annotation->description) |
| avb_free(icp_entry->annotation_leaf->annotation->description); |
| avb_free(icp_entry->annotation_leaf->annotation); |
| } |
| avb_free(icp_entry->annotation_leaf); |
| } |
| if (icp_entry->annotation_leaf_raw) |
| avb_free(icp_entry->annotation_leaf_raw); |
| /* Free the TrillianLogRoot elements if they exist. */ |
| if (icp_entry->log_root_descriptor.metadata) |
| avb_free(icp_entry->log_root_descriptor.metadata); |
| if (icp_entry->log_root_descriptor.root_hash) |
| avb_free(icp_entry->log_root_descriptor.root_hash); |
| if (icp_entry->log_root_descriptor_raw) |
| avb_free(icp_entry->log_root_descriptor_raw); |
| if (icp_entry->proofs) avb_free(icp_entry->proofs); |
| /* Finally, free the AftlIcpEntry. */ |
| avb_free(icp_entry); |
| } |
| } |
| |
| /* Free the AftlImage and each allocated sub-element. */ |
| void free_aftl_image(AftlImage* image) { |
| size_t i; |
| |
| /* Ensure the descriptor exists before attempting to free it. */ |
| if (!image) { |
| return; |
| } |
| /* Free the entry array. */ |
| if (image->entries) { |
| /* Walk through each entry, freeing each one. */ |
| for (i = 0; i < image->header.icp_count; i++) { |
| if (image->entries[i]) { |
| free_aftl_icp_entry(image->entries[i]); |
| } |
| } |
| avb_free(image->entries); |
| } |
| avb_free(image); |
| } |