blob: d1fa33cff86d107a825551848b49cccede75ebcc [file] [log] [blame]
/*
* 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;
}
/* Calculates a SHA256 hash of the TrillianLogRootDescriptor in icp_entry.
The hash is calculated over the entire TrillianLogRootDescriptor
structure. Some of the fields in this implementation are dynamically
allocated, and so the data needs to be reconstructed so that the hash
can be properly calculated. The TrillianLogRootDescriptor is defined
here: https://github.com/google/trillian/blob/master/trillian.proto#L255 */
bool avb_aftl_hash_log_root_descriptor(AftlIcpEntry* icp_entry, uint8_t* hash) {
uint8_t* buffer;
uint8_t* lrd_offset; /* Byte offset into the descriptor. */
uint32_t tlrd_size;
uint16_t version;
uint64_t tree_size;
uint64_t timestamp;
uint64_t revision;
uint16_t metadata_size;
bool retval;
avb_assert(icp_entry != NULL && hash != NULL);
/* Size of the non-pointer elements of the TrillianLogRootDescriptor. */
tlrd_size = sizeof(uint16_t) * 2 + sizeof(uint64_t) * 3 + sizeof(uint8_t);
/* Ensure the log_root_descriptor size is correct. */
if (icp_entry->log_root_descriptor_size > AVB_AFTL_MAX_TLRD_SIZE) {
avb_error("Invalid log root descriptor size.\n");
return false;
}
if (icp_entry->log_root_descriptor_size !=
(tlrd_size + icp_entry->log_root_descriptor.root_hash_size +
icp_entry->log_root_descriptor.metadata_size)) {
avb_error("Log root descriptor size doesn't match fields.\n");
return false;
}
/* Check that the root_hash exists, and if not, it's size is sane. */
if (!icp_entry->log_root_descriptor.root_hash &&
(icp_entry->log_root_descriptor.root_hash_size != 0)) {
avb_error("Invalid tree root hash values.\n");
return false;
}
/* Check that the metadata exists, and if not, it's size is sane. */
if (!icp_entry->log_root_descriptor.metadata &&
(icp_entry->log_root_descriptor.metadata_size != 0)) {
avb_error("Invalid log root descriptor metadata values.\n");
return false;
}
buffer = (uint8_t*)avb_malloc(icp_entry->log_root_descriptor_size);
if (buffer == NULL) {
avb_error("Allocation failure in avb_aftl_hash_log_root_descriptor.\n");
return false;
}
lrd_offset = buffer;
/* Copy in the version, tree_size and root hash length. */
/* Ensure endianness is correct. */
version = avb_be16toh(icp_entry->log_root_descriptor.version);
avb_memcpy(lrd_offset, &version, sizeof(uint16_t));
lrd_offset += sizeof(uint16_t);
/* Ensure endianness is correct. */
tree_size = avb_be64toh(icp_entry->log_root_descriptor.tree_size);
avb_memcpy(lrd_offset, &tree_size, sizeof(uint64_t));
lrd_offset += sizeof(uint64_t);
avb_memcpy(lrd_offset,
&(icp_entry->log_root_descriptor.root_hash_size),
sizeof(uint8_t));
lrd_offset += sizeof(uint8_t);
/* Copy the root hash. */
if (icp_entry->log_root_descriptor.root_hash_size > 0) {
avb_memcpy(lrd_offset,
icp_entry->log_root_descriptor.root_hash,
icp_entry->log_root_descriptor.root_hash_size);
}
lrd_offset += icp_entry->log_root_descriptor.root_hash_size;
/* Copy in the timestamp, revision, and the metadata length. */
/* Ensure endianness is correct. */
timestamp = avb_be64toh(icp_entry->log_root_descriptor.timestamp);
avb_memcpy(lrd_offset, &timestamp, sizeof(uint64_t));
lrd_offset += sizeof(uint64_t);
/* Ensure endianness is correct. */
revision = avb_be64toh(icp_entry->log_root_descriptor.revision);
avb_memcpy(lrd_offset, &revision, sizeof(uint64_t));
lrd_offset += sizeof(uint64_t);
/* Ensure endianness is correct. */
metadata_size = avb_be16toh(icp_entry->log_root_descriptor.metadata_size);
avb_memcpy(lrd_offset, &metadata_size, sizeof(uint16_t));
lrd_offset += sizeof(uint16_t);
/* Copy the metadata if it exists. */
if (icp_entry->log_root_descriptor.metadata_size > 0) {
avb_memcpy(lrd_offset,
icp_entry->log_root_descriptor.metadata,
icp_entry->log_root_descriptor.metadata_size);
}
/* Hash the result & clean up. */
retval = avb_aftl_sha256(buffer, icp_entry->log_root_descriptor_size, hash);
avb_free(buffer);
return retval;
}
/* 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;
}
/* 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) {
size_t parsed_size;
avb_assert(icp_entry);
avb_assert(aftl_blob);
avb_assert(aftl_blob_remaining >= AVB_AFTL_MIN_TLRD_SIZE);
/* Copy in the version field from the blob. */
avb_memcpy(&(icp_entry->log_root_descriptor.version),
*aftl_blob,
avb_aftl_member_size(TrillianLogRootDescriptor, version));
icp_entry->log_root_descriptor.version =
avb_be16toh(icp_entry->log_root_descriptor.version);
*aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, version);
parsed_size = avb_aftl_member_size(TrillianLogRootDescriptor, version);
/* Copy in the tree size field from the blob. */
avb_memcpy(&(icp_entry->log_root_descriptor.tree_size),
*aftl_blob,
avb_aftl_member_size(TrillianLogRootDescriptor, tree_size));
icp_entry->log_root_descriptor.tree_size =
avb_be64toh(icp_entry->log_root_descriptor.tree_size);
*aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, tree_size);
parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, tree_size);
/* Copy in the root hash size field from the blob. */
avb_memcpy(&(icp_entry->log_root_descriptor.root_hash_size),
*aftl_blob,
avb_aftl_member_size(TrillianLogRootDescriptor, root_hash_size));
if (icp_entry->log_root_descriptor.root_hash_size != AVB_AFTL_HASH_SIZE) {
avb_error("Invalid root hash size.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
*aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, root_hash_size);
parsed_size +=
avb_aftl_member_size(TrillianLogRootDescriptor, root_hash_size);
/* Copy in the root hash from the blob. */
icp_entry->log_root_descriptor.root_hash =
(uint8_t*)avb_calloc(icp_entry->log_root_descriptor.root_hash_size);
if (!icp_entry->log_root_descriptor.root_hash) {
avb_error("Failure to allocate root hash.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
avb_memcpy(icp_entry->log_root_descriptor.root_hash,
*aftl_blob,
icp_entry->log_root_descriptor.root_hash_size);
*aftl_blob += icp_entry->log_root_descriptor.root_hash_size;
parsed_size += icp_entry->log_root_descriptor.root_hash_size;
/* Copy in the timestamp field from the blob. */
avb_memcpy(&(icp_entry->log_root_descriptor.timestamp),
*aftl_blob,
avb_aftl_member_size(TrillianLogRootDescriptor, timestamp));
icp_entry->log_root_descriptor.timestamp =
avb_be64toh(icp_entry->log_root_descriptor.timestamp);
*aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, timestamp);
parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, timestamp);
/* Copy in the revision field from the blob. */
avb_memcpy(&(icp_entry->log_root_descriptor.revision),
*aftl_blob,
avb_aftl_member_size(TrillianLogRootDescriptor, revision));
icp_entry->log_root_descriptor.revision =
avb_be64toh(icp_entry->log_root_descriptor.revision);
*aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, revision);
parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, revision);
/* Copy in the metadata size field from the blob. */
avb_memcpy(&(icp_entry->log_root_descriptor.metadata_size),
*aftl_blob,
avb_aftl_member_size(TrillianLogRootDescriptor, metadata_size));
icp_entry->log_root_descriptor.metadata_size =
avb_be16toh(icp_entry->log_root_descriptor.metadata_size);
*aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, metadata_size);
parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, metadata_size);
if (icp_entry->log_root_descriptor.metadata_size >
AVB_AFTL_MAX_METADATA_SIZE) {
avb_error("Invalid metadata size.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
if (icp_entry->log_root_descriptor.metadata_size + parsed_size >
aftl_blob_remaining) {
avb_error("Invalid AftlDescriptor.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
/* If it exists, copy in the metadata field from the blob. */
if (icp_entry->log_root_descriptor.metadata_size > 0) {
icp_entry->log_root_descriptor.metadata =
(uint8_t*)avb_calloc(icp_entry->log_root_descriptor.metadata_size);
if (!icp_entry->log_root_descriptor.metadata) {
avb_error("Failure to allocate metadata.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
avb_memcpy(icp_entry->log_root_descriptor.metadata,
*aftl_blob,
icp_entry->log_root_descriptor.metadata_size);
*aftl_blob += icp_entry->log_root_descriptor.metadata_size;
} else {
icp_entry->log_root_descriptor.metadata = NULL;
}
return true;
}
static void base64_decode(uint8_t* input,
size_t input_size,
uint8_t* output,
size_t output_size) {
size_t i, j;
uint32_t tmp_val;
uint8_t decode_table[] = {
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
-1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
avb_assert(input != NULL);
avb_assert(output != NULL);
for (i = 0, j = 0; i < input_size; i += 4, j += 3) {
tmp_val = decode_table[input[i] - '+'];
tmp_val = (tmp_val << 6) | decode_table[input[i + 1] - '+'];
tmp_val <<= 6;
if (input[i + 2] != '=') tmp_val |= decode_table[input[i + 2] - '+'];
tmp_val <<= 6;
if (input[i + 3] != '=') tmp_val |= decode_table[input[i + 3] - '+'];
output[j] = (tmp_val >> 16) & 0xff;
if (input[i + 2] != '=') output[j + 1] = (tmp_val >> 8) & 0xff;
if (input[i + 3] != '=') output[j + 2] = tmp_val & 0xff;
}
}
static bool find_and_decode_vbmeta_hash(uint8_t* vbmeta_hash,
size_t vbmeta_size,
uint8_t* json_data,
size_t json_data_size) {
const char vbmeta_id[] = "\"vbmeta_hash\":";
size_t vbmeta_id_size = sizeof(vbmeta_id) - 1;
size_t vbmeta_base64_size;
uint8_t* vbmeta_ptr;
uint8_t* vbmeta_base64;
avb_assert(vbmeta_hash != NULL);
avb_assert(vbmeta_size == AVB_AFTL_HASH_SIZE);
avb_assert(json_data != NULL);
avb_assert(json_data_size > vbmeta_size);
vbmeta_ptr = (uint8_t*)avb_strstr((const char*)json_data, vbmeta_id);
if (vbmeta_ptr == NULL) {
vbmeta_hash = NULL;
return false;
}
/* Jump past the vbmeta_hash identifier */
vbmeta_ptr += vbmeta_id_size;
if (vbmeta_ptr[0] == '"') {
vbmeta_base64_size = 1;
while (vbmeta_ptr[vbmeta_base64_size] != '"' &&
vbmeta_base64_size <= AVB_AFTL_HASH_SIZE * 4 / 3 + 1) {
vbmeta_base64_size++;
}
vbmeta_base64 = (uint8_t*)avb_calloc(vbmeta_base64_size + 1);
if (vbmeta_base64 == NULL) {
vbmeta_hash = NULL;
return false;
}
avb_memcpy(vbmeta_base64, vbmeta_ptr + 1, vbmeta_base64_size);
base64_decode(vbmeta_base64, vbmeta_base64_size, vbmeta_hash, vbmeta_size);
avb_free(vbmeta_base64);
} else {
vbmeta_hash = NULL;
return false;
}
return true;
}
/* Allocates and populates a FirmwareInfo element in an
AftlIcpEntry from a binary blob.
The blob is expected to be pointing to the beginning of a
serialized FirmwareInfo element of an AftlIcpEntry.
The aftl_blob argument is updated to point to the area after the
FirmwareInfo leaf. */
static bool parse_firmware_info(AftlIcpEntry* icp_entry, uint8_t** aftl_blob) {
/* Copy in the fw_info leaf bytes from the blob. */
/* Parse out and decode in the vbmeta_hash value from the fw_info
leaf bytes. */
icp_entry->fw_info_leaf.json_data =
(uint8_t*)avb_calloc(icp_entry->fw_info_leaf_size);
if (icp_entry->fw_info_leaf.json_data == NULL) {
avb_error("Failed to allocate for FirmwareInfo leaf.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
avb_memcpy(icp_entry->fw_info_leaf.json_data,
*aftl_blob,
icp_entry->fw_info_leaf_size);
*aftl_blob += icp_entry->fw_info_leaf_size;
icp_entry->fw_info_leaf.vbmeta_hash_size = AVB_AFTL_HASH_SIZE;
icp_entry->fw_info_leaf.vbmeta_hash =
(uint8_t*)avb_calloc(icp_entry->fw_info_leaf.vbmeta_hash_size);
if (icp_entry->fw_info_leaf.vbmeta_hash == NULL) {
avb_error("Failed to allocate vbmeta hash.\n");
free_aftl_icp_entry(icp_entry);
return false;
}
if (!find_and_decode_vbmeta_hash(icp_entry->fw_info_leaf.vbmeta_hash,
icp_entry->fw_info_leaf.vbmeta_hash_size,
icp_entry->fw_info_leaf.json_data,
icp_entry->fw_info_leaf_size)) {
avb_error("Could not parse vbmeta_hash out of FirmwareInfo leaf.\n");
free_aftl_icp_entry(icp_entry);
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, *tmp_icp_entry;
uint32_t proof_size;
uint64_t parsed_size;
/* Make a temp AftlIcpEntry to get the inclusion proof size
for memory allocation purposes.*/
tmp_icp_entry = (AftlIcpEntry*)*aftl_blob;
proof_size = avb_be32toh(tmp_icp_entry->inc_proof_size);
/* Ensure the calculated size is sane. */
if (proof_size > AVB_AFTL_MAX_PROOF_SIZE) {
avb_error("Invalid inclusion proof size.\n");
return NULL;
}
if (*remaining_size < proof_size + AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE) {
avb_error("Invalid AftlDescriptor\n");
return NULL;
}
icp_entry = (AftlIcpEntry*)avb_calloc(proof_size + sizeof(AftlIcpEntry));
if (!icp_entry) {
avb_error("Failure allocating AftlIcpEntry\n");
return NULL;
}
/* Copy in the log server URL size field. */
avb_memcpy(&(icp_entry->log_url_size),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, log_url_size));
icp_entry->log_url_size = avb_be32toh(icp_entry->log_url_size);
if (icp_entry->log_url_size > AVB_AFTL_MAX_URL_SIZE) {
avb_error("Invalid log URL size.\n");
avb_free(icp_entry);
return NULL;
}
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, log_url_size);
parsed_size = avb_aftl_member_size(AftlIcpEntry, log_url_size);
/* Copy in the leaf index field. */
avb_memcpy(&(icp_entry->leaf_index),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, leaf_index));
icp_entry->leaf_index = avb_be64toh(icp_entry->leaf_index);
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, leaf_index);
parsed_size += avb_aftl_member_size(AftlIcpEntry, leaf_index);
/* Copy in the TrillianLogRootDescriptor size field. */
avb_memcpy(&(icp_entry->log_root_descriptor_size),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, log_root_descriptor_size));
icp_entry->log_root_descriptor_size =
avb_be32toh(icp_entry->log_root_descriptor_size);
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;
}
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, log_root_descriptor_size);
parsed_size += avb_aftl_member_size(AftlIcpEntry, log_root_descriptor_size);
/* Copy in the FirmwareInfo leaf size field. */
avb_memcpy(&(icp_entry->fw_info_leaf_size),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size));
icp_entry->fw_info_leaf_size = avb_be32toh(icp_entry->fw_info_leaf_size);
if (icp_entry->fw_info_leaf_size == 0 ||
icp_entry->fw_info_leaf_size > AVB_AFTL_MAX_FW_INFO_SIZE) {
avb_error("Invalid FirmwareInfo leaf size.\n");
avb_free(icp_entry);
return NULL;
}
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size);
parsed_size += avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size);
/* Copy the log root signature size field. */
avb_memcpy(&(icp_entry->log_root_sig_size),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, log_root_sig_size));
icp_entry->log_root_sig_size = avb_be16toh(icp_entry->log_root_sig_size);
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;
}
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, log_root_sig_size);
parsed_size += avb_aftl_member_size(AftlIcpEntry, log_root_sig_size);
/* Copy the inclusion proof hash count field. */
avb_memcpy(&(icp_entry->proof_hash_count),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, proof_hash_count));
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, proof_hash_count);
parsed_size += avb_aftl_member_size(AftlIcpEntry, proof_hash_count);
/* Copy the inclusion proof size field. */
avb_memcpy(&(icp_entry->inc_proof_size),
*aftl_blob,
avb_aftl_member_size(AftlIcpEntry, inc_proof_size));
icp_entry->inc_proof_size = avb_be32toh(icp_entry->inc_proof_size);
if (icp_entry->inc_proof_size !=
icp_entry->proof_hash_count * AVB_AFTL_HASH_SIZE) {
avb_error("Invalid inclusion proof size.\n");
avb_free(icp_entry);
return NULL;
}
*aftl_blob += avb_aftl_member_size(AftlIcpEntry, inc_proof_size);
parsed_size += avb_aftl_member_size(AftlIcpEntry, inc_proof_size);
/* Copy in the log server URL from the blob. */
if (!avb_safe_add_to(&parsed_size, icp_entry->log_url_size)) {
avb_error("Invalid URL size.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (parsed_size > *remaining_size) {
avb_error("Invalid AftlDescriptor.\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 (!avb_safe_add_to(&parsed_size, icp_entry->log_root_descriptor_size)) {
avb_error("Invalid TrillianLogRootDescriptor size.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (parsed_size > *remaining_size) {
avb_error("Invalid AftlDescriptor.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (!parse_trillian_log_root_descriptor(
icp_entry, aftl_blob, icp_entry->log_root_descriptor_size)) {
return NULL;
}
/* Populate the FirmwareInfo elements. */
if (!avb_safe_add_to(&parsed_size, icp_entry->fw_info_leaf_size)) {
avb_error("Invalid FirmwareInfo leaf size.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (parsed_size > *remaining_size) {
avb_error("Invalid AftlDescriptor.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (!parse_firmware_info(icp_entry, aftl_blob)) return NULL;
/* Allocate and copy the log root signature from the blob. */
if (!avb_safe_add_to(&parsed_size, icp_entry->log_root_sig_size)) {
avb_error("Invalid log root signature size.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (parsed_size > *remaining_size) {
avb_error("Invalid AftlDescriptor.\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) {
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;
if (!avb_safe_add_to(&parsed_size, icp_entry->inc_proof_size)) {
avb_error("Invalid inclusion proof size.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
if (parsed_size > *remaining_size) {
avb_error("Invalid AftlDescriptor.\n");
free_aftl_icp_entry(icp_entry);
return NULL;
}
/* Finally, copy the proof hash data from the blob to the AftlDescriptor. */
avb_memcpy(icp_entry->proofs, *aftl_blob, icp_entry->inc_proof_size);
*aftl_blob += icp_entry->inc_proof_size;
*remaining_size -= parsed_size;
return icp_entry;
}
/* Allocate and parse an AftlDescriptor object out of binary data. */
AftlDescriptor* parse_aftl_descriptor(uint8_t* aftl_blob,
size_t aftl_blob_size) {
AftlDescriptor* aftl_descriptor;
AftlIcpHeader* icp_header;
size_t aftl_descriptor_size;
size_t i;
size_t remaining_size;
/* Ensure the blob is at least large enough for an AftlIcpHeader */
avb_assert(aftl_blob_size >= sizeof(AftlIcpHeader));
icp_header = (AftlIcpHeader*)aftl_blob;
/* Check for the magic value for an AftlIcpHeader. */
if (icp_header->magic != AVB_AFTL_MAGIC) {
avb_error("Invalid magic number\n");
return NULL;
}
/* Extract the size out of the header. */
aftl_descriptor_size = avb_be32toh(icp_header->aftl_descriptor_size);
if (aftl_descriptor_size > AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE) return NULL;
avb_assert(aftl_descriptor_size >= sizeof(AftlIcpHeader) &&
aftl_descriptor_size < AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE);
aftl_descriptor = (AftlDescriptor*)avb_calloc(sizeof(AftlDescriptor));
if (!aftl_descriptor) {
avb_error("Failed allocation for AftlDescriptor.\n");
return NULL;
}
/* Copy the header bytes directly from the aftl_blob. */
avb_memcpy(&(aftl_descriptor->header), aftl_blob, sizeof(AftlIcpHeader));
/* Fix endianness. */
aftl_descriptor->header.required_icp_version_major =
avb_be32toh(aftl_descriptor->header.required_icp_version_major);
aftl_descriptor->header.required_icp_version_minor =
avb_be32toh(aftl_descriptor->header.required_icp_version_minor);
aftl_descriptor->header.aftl_descriptor_size =
avb_be32toh(aftl_descriptor->header.aftl_descriptor_size);
aftl_descriptor->header.icp_count =
avb_be16toh(aftl_descriptor->header.icp_count);
/* Allocate memory for the entry array */
aftl_descriptor->entries = (AftlIcpEntry**)avb_calloc(
sizeof(AftlIcpEntry*) * aftl_descriptor->header.icp_count);
if (!aftl_descriptor->entries) {
avb_error("Failed allocation for AftlIcpEntry array.\n");
avb_free(aftl_descriptor);
return NULL;
}
/* Jump past the header and parse out each AftlIcpEntry. */
aftl_blob += sizeof(AftlIcpHeader);
remaining_size = aftl_blob_size - sizeof(AftlIcpHeader);
for (i = 0; i < aftl_descriptor->header.icp_count && remaining_size > 0;
i++) {
aftl_descriptor->entries[i] = parse_icp_entry(&aftl_blob, &remaining_size);
}
return aftl_descriptor;
}
/* 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 FirmwareInfo elements if they exist. */
if (icp_entry->fw_info_leaf.json_data)
avb_free(icp_entry->fw_info_leaf.json_data);
if (icp_entry->fw_info_leaf.vbmeta_hash)
avb_free(icp_entry->fw_info_leaf.vbmeta_hash);
/* 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);
/* Finally, free the AftlIcpEntry. */
avb_free(icp_entry);
}
}
/* Free the AftlDescriptor and each allocated sub-element. */
void free_aftl_descriptor(AftlDescriptor* aftl_descriptor) {
size_t i;
/* Ensure the descriptor exists before attempting to free it. */
if (!aftl_descriptor) {
return;
}
/* Free the entry array. */
if (aftl_descriptor->entries) {
/* Walk through each entry, freeing each one. */
for (i = 0; i < aftl_descriptor->header.icp_count; i++) {
if (aftl_descriptor->entries[i]) {
free_aftl_icp_entry(aftl_descriptor->entries[i]);
}
}
avb_free(aftl_descriptor->entries);
}
avb_free(aftl_descriptor);
}