summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Czapiga <jacz@semihalf.com>2022-08-04 17:13:06 +0200
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-10-12 17:41:55 +0000
commit0ca7a9e4dad2e9780690524ced9273fa07052179 (patch)
tree9078489dc0f6e8e79aaf3ff80f2358b5dbc7e814
parentaaeb307f882d0c2e1284636e8423af1d216f6362 (diff)
downloadvboot-stabilize-15183.14.B.tar.gz
firmware: host: futility: Add CBFS metadata hash supportstabilize-15183.14.B
This patch adds support for signing and verification of coreboot images supporting VBOOT_CBFS_INTEGRATION. Images with config option CONFIG_VBOOT_CBFS_INTEGRATION=y will be signed with CBFS metadata hash in signature. vb2api_get_metadata_hash() should be used to extract hash value from VBLOCK and then should be used to verify CBFS metadata. To support full verification, CBFS file data verification should also be enabled and correctly handled. BUG=b:197114807 TEST=build with CB:66909 and boot on volteer/voxel with CONFIG_VBOOT_CBFS_INTEGRATION=y BRANCH=none Signed-off-by: Jakub Czapiga <czapiga@google.com> Change-Id: I4075c84820949be24c423ed14e291c89a0032863 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3811754 Commit-Queue: Julius Werner <jwerner@chromium.org> Reviewed-by: Julius Werner <jwerner@chromium.org> Reviewed-by: Yu-Ping Wu <yupingso@chromium.org>
-rw-r--r--firmware/2lib/2api.c45
-rw-r--r--firmware/2lib/2common.c3
-rw-r--r--firmware/2lib/include/2api.h46
-rw-r--r--futility/cmd_show.c68
-rw-r--r--futility/file_type_bios.c138
-rw-r--r--futility/file_type_bios.h16
-rw-r--r--futility/file_type_rwsig.c10
-rw-r--r--futility/futility.h5
-rw-r--r--futility/misc.c52
-rw-r--r--futility/vb2_helper.c16
-rw-r--r--host/lib/cbfstool.c168
-rw-r--r--host/lib/host_misc.c50
-rw-r--r--host/lib/host_signature2.c25
-rw-r--r--host/lib/include/cbfstool.h25
-rw-r--r--host/lib/include/host_misc.h23
-rw-r--r--host/lib/include/host_signature.h11
-rw-r--r--tests/vb2_api_tests.c96
17 files changed, 657 insertions, 140 deletions
diff --git a/firmware/2lib/2api.c b/firmware/2lib/2api.c
index 0d1ebe86..698e2bb7 100644
--- a/firmware/2lib/2api.c
+++ b/firmware/2lib/2api.c
@@ -305,14 +305,10 @@ vb2_error_t vb2api_init_hash(struct vb2_context *ctx, uint32_t tag)
/*
* Unpack the firmware data key to see which hashing algorithm we
- * should use.
- *
- * TODO: really, the firmware body should be hashed, and not signed,
- * because the signature we're checking is already signed as part of
- * the firmware preamble. But until we can change the signing scripts,
- * we're stuck with a signature here instead of a hash.
+ * should use. Zero body data size means, that signature contains
+ * metadata hash, so vb2api_get_metadata_hash() should be used instead.
*/
- if (!sd->data_key_size)
+ if (!sd->data_key_size || !pre->body_signature.data_size)
return VB2_ERROR_API_INIT_HASH_DATA_KEY;
VB2_TRY(vb2_unpack_key_buffer(&key,
@@ -369,8 +365,9 @@ vb2_error_t vb2api_check_hash_get_digest(struct vb2_context *ctx,
return VB2_ERROR_API_CHECK_HASH_TAG;
/*
- * The body signature is currently a *signature* of the body data, not
- * just its hash. So we need to verify the signature.
+ * In case of verifying a whole memory region the body signature
+ * is a *signature* of the body data, not just its hash.
+ * So we need to verify the signature.
*/
/* Unpack the data key */
@@ -433,3 +430,33 @@ union vb2_fw_boot_info vb2api_get_fw_boot_info(struct vb2_context *ctx)
return info;
}
+
+vb2_error_t vb2api_get_metadata_hash(struct vb2_context *ctx,
+ struct vb2_hash **hash_ptr_out)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ struct vb2_workbuf wb;
+ struct vb2_fw_preamble *pre;
+
+ vb2_workbuf_from_ctx(ctx, &wb);
+
+ if (!sd->preamble_size)
+ return VB2_ERROR_API_CHECK_HASH_PREAMBLE;
+ pre = vb2_member_of(sd, sd->preamble_offset);
+
+ /* Zero size of body signature indicates, that signature holds
+ vb2_hash inside. */
+ if (pre->body_signature.data_size)
+ return VB2_ERROR_API_INIT_HASH_DATA_KEY;
+
+ struct vb2_hash *hash =
+ (struct vb2_hash *)vb2_signature_data(&pre->body_signature);
+ const uint32_t hsize = vb2_digest_size(hash->algo);
+ if (!hsize || pre->body_signature.sig_size <
+ offsetof(struct vb2_hash, raw) + hsize)
+ return VB2_ERROR_API_CHECK_HASH_SIG_SIZE;
+
+ *hash_ptr_out = hash;
+
+ return VB2_SUCCESS;
+}
diff --git a/firmware/2lib/2common.c b/firmware/2lib/2common.c
index 1d1ad690..3d08f5b2 100644
--- a/firmware/2lib/2common.c
+++ b/firmware/2lib/2common.c
@@ -156,6 +156,9 @@ vb2_error_t vb2_verify_digest(const struct vb2_public_key *key,
/* A signature is destroyed in the process of being verified. */
uint8_t *sig_data = vb2_signature_data_mutable(sig);
+ if (!sig->data_size)
+ return VB2_ERROR_VDATA_NOT_ENOUGH_DATA;
+
if (sig->sig_size != vb2_rsa_sig_size(key->sig_alg)) {
VB2_DEBUG("Wrong data signature size for algorithm, "
"sig_size=%d, expected %d for algorithm %d.\n",
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 03e49fe0..869d2361 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -150,19 +150,37 @@ enum vb2_pcr_digest {
* Verify the hash of each section of code/data you need to boot the RW
* firmware. For each section:
*
- * Call vb2_init_hash() to see if the hash exists.
+ * 1) Normal verification:
*
- * Load the data for the section. Call vb2_extend_hash() on the
+ * Call vb2api_init_hash() to see if the hash exists.
+ *
+ * Load the data for the section. Call vb2api_extend_hash() on the
* data as you load it. You can load it all at once and make one
* call, or load and hash-extend a block at a time.
*
- * Call vb2_check_hash() to see if the hash is valid.
+ * Call vb2api_check_hash() to see if the hash is valid.
*
* If it is valid, you may use the data and/or execute
* code from that section.
*
* If the hash was invalid, you must reboot.
*
+ * 2) Verification with CBFS integration:
+ *
+ * Call vb2api_get_metadata_hash() to get hash of CBFS metadata.
+ *
+ * Initialize CBFS using stored hash as correct metadata hash.
+ *
+ * If CBFS initialization fails because of metadata hash
+ * mismatch, you must reboot.
+ *
+ * If CBFS initialization succeeds, you may use the data
+ * and/or execute code from that section.
+ * IMPORTANT: Be aware, that to have full section
+ * verification, the CBFS_VERIFICATION has to be enabled.
+ * Initialization of CBFS volume only checks hash of files
+ * metadata, not their contents!
+ *
* At this point, firmware verification is done, and vb2_context contains the
* kernel key needed to verify the kernel. That context should be preserved
* and passed on to kernel selection. The kernel selection process may be
@@ -442,6 +460,9 @@ vb2_error_t vb2api_fw_phase3(struct vb2_context *ctx);
/**
* Initialize hashing data for the specified tag.
+ * This function is not legal when running from a coreboot image that has
+ * CONFIG_VBOOT_CBFS_INTEGRATION=y set. In that case, vb2api_get_metadata_hash()
+ * must be used instead.
*
* @param ctx Vboot context
* @param tag Tag to start hashing (enum vb2_hash_tag)
@@ -484,6 +505,23 @@ vb2_error_t vb2api_check_hash_get_digest(struct vb2_context *ctx,
uint32_t digest_out_size);
/**
+ * Get pointer to metadata hash from body signature in preamble.
+ * Body signature data size has to be zero to indicate that it contains
+ * metadata hash. This is only legal to call after vb2api_fw_phase3() has
+ * returned successfully, and will return with error otherwise.
+ * This function is only legal to call from coreboot with
+ * CONFIG_VBOOT_CBFS_INTEGRATION=y. `futility sign` will automatically detect
+ * the presence of that option in an image and prepare the correct kind
+ * of signature.
+ *
+ * @param ctx Vboot context
+ * @param hash_ptr_out pointer to output hash to
+ * @return VB2_SUCCESS, or error code on error.
+ */
+vb2_error_t vb2api_get_metadata_hash(struct vb2_context *ctx,
+ struct vb2_hash **hash_ptr_out);
+
+/**
* Get a PCR digest
*
* @param ctx Vboot context
@@ -726,6 +764,8 @@ vb2_gbb_flags_t vb2api_gbb_get_flags(struct vb2_context *ctx);
/**
* Get the size of the signed firmware body. This is only legal to call after
* vb2api_fw_phase3() has returned successfully, and will return 0 otherwise.
+ * It will also return 0 when body signature contains metadata hash instead
+ * of body hash.
*
* @param ctx Vboot context
*
diff --git a/futility/cmd_show.c b/futility/cmd_show.c
index 6ed15435..4b11b50f 100644
--- a/futility/cmd_show.c
+++ b/futility/cmd_show.c
@@ -22,6 +22,7 @@
#include "2common.h"
#include "2sha.h"
#include "2sysincludes.h"
+#include "cbfstool.h"
#include "file_type_bios.h"
#include "file_type.h"
#include "fmap.h"
@@ -181,6 +182,53 @@ done:
return retval;
}
+static int fw_show_metadata_hash(const char *name, enum bios_component body_c,
+ struct vb2_fw_preamble *pre)
+{
+ struct vb2_hash real_hash;
+ struct vb2_hash *body_hash =
+ (struct vb2_hash *)vb2_signature_data(&pre->body_signature);
+ const uint32_t bhsize = vb2_digest_size(body_hash->algo);
+
+ if (!bhsize || pre->body_signature.sig_size <
+ offsetof(struct vb2_hash, raw) + bhsize) {
+ fprintf(stderr, "Body signature data is too small to "
+ "fit metadata hash.\n");
+ return 1;
+ }
+
+ printf(" Body metadata hash: %s",
+ vb2_get_hash_algorithm_name(body_hash->algo));
+ if (vb2_digest_size(body_hash->algo)) {
+ putchar(':');
+ print_bytes((uint8_t *)body_hash->raw,
+ vb2_digest_size(body_hash->algo));
+ }
+ putchar('\n');
+
+ if (cbfstool_get_metadata_hash(name, fmap_name[body_c], &real_hash) !=
+ VB2_SUCCESS ||
+ real_hash.algo == VB2_HASH_INVALID) {
+ fprintf(stderr, "Failed to get metadata hash. Firmware body is"
+ " corrupted or is not a valid CBFS.\n");
+ return 1;
+ }
+
+ if (body_hash->algo != real_hash.algo ||
+ !vb2_digest_size(body_hash->algo) ||
+ memcmp(body_hash->raw, real_hash.raw,
+ vb2_digest_size(body_hash->algo))) {
+ printf(" MISMATCH! Real hash: %s:",
+ vb2_get_hash_algorithm_name(real_hash.algo));
+ print_bytes(&real_hash.raw, vb2_digest_size(real_hash.algo));
+ putchar('\n');
+ fprintf(stderr, "Signature hash does not match with"
+ " real metadata hash.\n");
+ return 1;
+ }
+ return 0;
+}
+
int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len,
void *data)
{
@@ -190,6 +238,7 @@ int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len,
uint8_t *fv_data = show_option.fv;
uint64_t fv_size = show_option.fv_size;
struct bios_area_s *fw_body_area = 0;
+ enum bios_component body_c;
int good_sig = 0;
int retval = 0;
@@ -217,9 +266,8 @@ int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len,
}
/* Identify the firmware body for this VBLOCK */
- enum bios_component body_c = state->c == BIOS_FMAP_VBLOCK_A
- ? BIOS_FMAP_FW_MAIN_A
- : BIOS_FMAP_FW_MAIN_B;
+ body_c = state->c == BIOS_FMAP_VBLOCK_A ? BIOS_FMAP_FW_MAIN_A
+ : BIOS_FMAP_FW_MAIN_B;
fw_body_area = &state->area[body_c];
}
@@ -288,11 +336,15 @@ int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len,
return 0;
}
- if (VB2_SUCCESS !=
- vb2_verify_data(fv_data, fv_size, &pre2->body_signature,
- &data_key, &wb)) {
- fprintf(stderr, "Error verifying firmware body.\n");
- return 1;
+ if (pre2->body_signature.data_size) {
+ if (vb2_verify_data(fv_data, fv_size, &pre2->body_signature,
+ &data_key, &wb) != VB2_SUCCESS) {
+ fprintf(stderr, "Error verifying firmware body.\n");
+ return 1;
+ }
+ } else if (state) { /* Only works for images with at least FW_MAIN_A */
+ if (fw_show_metadata_hash(name, body_c, pre2))
+ return 1;
}
done:
diff --git a/futility/file_type_bios.c b/futility/file_type_bios.c
index e900f271..884dfc8e 100644
--- a/futility/file_type_bios.c
+++ b/futility/file_type_bios.c
@@ -19,15 +19,6 @@
#include "host_common.h"
#include "vb1_helper.h"
-static const char * const fmap_name[] = {
- "GBB", /* BIOS_FMAP_GBB */
- "FW_MAIN_A", /* BIOS_FMAP_FW_MAIN_A */
- "FW_MAIN_B", /* BIOS_FMAP_FW_MAIN_B */
- "VBLOCK_A", /* BIOS_FMAP_VBLOCK_A */
- "VBLOCK_B", /* BIOS_FMAP_VBLOCK_B */
-};
-_Static_assert(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS,
- "Size of fmap_name[] should match NUM_BIOS_COMPONENTS");
static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len)
{
@@ -248,9 +239,15 @@ static int write_new_preamble(struct bios_area_s *vblock,
struct vb2_fw_preamble *preamble = NULL;
int retval = 1;
- body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len, signkey);
+ if (fw_body->metadata_hash.algo != VB2_HASH_INVALID)
+ body_sig =
+ vb2_create_signature_from_hash(&fw_body->metadata_hash);
+ else
+ body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len,
+ signkey);
+
if (!body_sig) {
- ERROR("Error calculating body signature\n");
+ ERROR("Error calculating or creating body signature\n");
goto end;
}
@@ -355,8 +352,8 @@ static int sign_bios_at_end(struct bios_state_s *state)
/* Prepare firmware slot for signing.
If fw_size is not zero, then it will be used as new length of signed area,
for zero the length will be taken form FlashMap or preamble. */
-static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size,
- enum bios_component fw_c, enum bios_component vblock_c,
+static int prepare_slot(uint8_t *buf, uint32_t len, enum bios_component fw_c,
+ enum bios_component vblock_c,
struct bios_state_s *state)
{
FmapHeader *fmap;
@@ -380,14 +377,16 @@ static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size,
fmap_limit_area(ah, len);
state->area[fw_c].buf = buf + ah->area_offset;
state->area[fw_c].is_valid = 1;
- if (fw_size > ah->area_size) {
+ if (state->area[fw_c].fw_size > ah->area_size) {
ERROR("%s size is incorrect.\n", fmap_name[fw_c]);
return 1;
- } else if (fw_size) {
- state->area[fw_c].len = fw_size;
+ } else if (state->area[fw_c].fw_size) {
+ state->area[fw_c].len = state->area[fw_c].fw_size;
} else {
- WARN("%s does not contain CBFS. Trying to sign entire area.\n",
- fmap_name[fw_c]);
+ if (state->area[fw_c].metadata_hash.algo == VB2_HASH_INVALID)
+ WARN("%s does not contain CBFS. Trying to sign entire "
+ "area.\n",
+ fmap_name[fw_c]);
state->area[fw_c].len = ah->area_size;
}
@@ -441,7 +440,7 @@ static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size,
goto end;
}
- if (fw_size == 0) {
+ if (state->area[fw_c].fw_size == 0) {
if (preamble->body_signature.data_size >
state->area[fw_c].len) {
ERROR("%s says the firmware is larger than we have.\n",
@@ -475,6 +474,63 @@ end:
return 0;
}
+static bool image_uses_cbfs_integration(const char *file)
+{
+ char *value;
+ bool rv = false;
+
+ if (cbfstool_get_config_value(file, NULL,
+ "CONFIG_VBOOT_CBFS_INTEGRATION",
+ &value) != VB2_SUCCESS)
+ return false;
+
+ if (value && strcmp("y", value) == 0)
+ rv = true;
+
+ free(value);
+ return rv;
+}
+
+static void image_check_and_prepare_cbfs(const char *file,
+ enum bios_component fw_c,
+ bool uses_cbfs_integration,
+ struct bios_state_s *state)
+{
+ if (!uses_cbfs_integration) {
+ if (cbfstool_truncate(file, fmap_name[fw_c],
+ &state->area[fw_c].fw_size) !=
+ VB2_SUCCESS) {
+ VB2_DEBUG("CBFS not found in area %s\n",
+ fmap_name[fw_c]);
+ return;
+ }
+ VB2_DEBUG("CBFS found in area %s\n", fmap_name[fw_c]);
+ return;
+ }
+
+ if (cbfstool_get_metadata_hash(file, fmap_name[fw_c],
+ &state->area[fw_c].metadata_hash) !=
+ VB2_SUCCESS)
+ FATAL("CBFS metadata hash not found in area"
+ " %s. It is required for images with"
+ " VBOOT_CBFS_INTEGRATION",
+ fmap_name[fw_c]);
+
+ VB2_DEBUG("CBFS metadata hash found in area %s\n", fmap_name[fw_c]);
+}
+
+static void check_slot_after_prepare(enum bios_component fw_c,
+ bool uses_cbfs_integration,
+ struct bios_state_s *state)
+{
+ if (state->area[fw_c].is_valid && uses_cbfs_integration &&
+ state->area[fw_c].metadata_hash.algo == VB2_HASH_INVALID)
+ FATAL("CBFS with metadata hash not found in area %s."
+ " It is required for images with"
+ " VBOOT_CBFS_INTEGRATION",
+ fmap_name[fw_c]);
+}
+
int ft_sign_bios(const char *name, void *data)
{
int retval = 0;
@@ -482,47 +538,35 @@ int ft_sign_bios(const char *name, void *data)
int fd = -1;
uint8_t *buf = NULL;
uint32_t len = 0;
- size_t fw_main_a_size = 0;
- size_t fw_main_b_size = 0;
-
- bool fw_main_a_in_cbfs_mode =
- cbfstool_truncate(name, fmap_name[BIOS_FMAP_FW_MAIN_A],
- &fw_main_a_size) == VB2_SUCCESS;
-
- bool fw_main_b_in_cbfs_mode =
- cbfstool_truncate(name, fmap_name[BIOS_FMAP_FW_MAIN_B],
- &fw_main_b_size) == VB2_SUCCESS;
+ bool uses_cbfs_integration =
+ image_uses_cbfs_integration(name);
- if (fw_main_a_in_cbfs_mode)
- VB2_DEBUG("CBFS found in area %s\n",
- fmap_name[BIOS_FMAP_FW_MAIN_A]);
- else
- VB2_DEBUG("CBFS not found in area %s\n",
- fmap_name[BIOS_FMAP_FW_MAIN_A]);
+ memset(&state, 0, sizeof(state));
- if (fw_main_b_in_cbfs_mode)
- VB2_DEBUG("CBFS found in area %s\n",
- fmap_name[BIOS_FMAP_FW_MAIN_B]);
- else
- VB2_DEBUG("CBFS not found in area %s\n",
- fmap_name[BIOS_FMAP_FW_MAIN_B]);
+ image_check_and_prepare_cbfs(name, BIOS_FMAP_FW_MAIN_A,
+ uses_cbfs_integration, &state);
+ image_check_and_prepare_cbfs(name, BIOS_FMAP_FW_MAIN_B,
+ uses_cbfs_integration, &state);
if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
&buf, &len))
return 1;
- memset(&state, 0, sizeof(state));
-
- retval = prepare_slot(buf, len, fw_main_a_size, BIOS_FMAP_FW_MAIN_A,
- BIOS_FMAP_VBLOCK_A, &state);
+ retval = prepare_slot(buf, len, BIOS_FMAP_FW_MAIN_A, BIOS_FMAP_VBLOCK_A,
+ &state);
if (retval)
goto done;
- retval = prepare_slot(buf, len, fw_main_b_size, BIOS_FMAP_FW_MAIN_B,
- BIOS_FMAP_VBLOCK_B, &state);
+ retval = prepare_slot(buf, len, BIOS_FMAP_FW_MAIN_B, BIOS_FMAP_VBLOCK_B,
+ &state);
if (retval && state.area[BIOS_FMAP_FW_MAIN_B].is_valid)
goto done;
+ check_slot_after_prepare(BIOS_FMAP_FW_MAIN_A, uses_cbfs_integration,
+ &state);
+ check_slot_after_prepare(BIOS_FMAP_FW_MAIN_B, uses_cbfs_integration,
+ &state);
+
retval = sign_bios_at_end(&state);
done:
futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len);
diff --git a/futility/file_type_bios.h b/futility/file_type_bios.h
index 469e3fd0..1f957b1c 100644
--- a/futility/file_type_bios.h
+++ b/futility/file_type_bios.h
@@ -8,6 +8,8 @@
#include <stdint.h>
+#include "futility.h"
+
/*
* The Chrome OS BIOS must contain specific FMAP areas, which we want to look
* at in a certain order.
@@ -22,6 +24,16 @@ enum bios_component {
NUM_BIOS_COMPONENTS
};
+static const char * const fmap_name[] = {
+ "GBB", /* BIOS_FMAP_GBB */
+ "FW_MAIN_A", /* BIOS_FMAP_FW_MAIN_A */
+ "FW_MAIN_B", /* BIOS_FMAP_FW_MAIN_B */
+ "VBLOCK_A", /* BIOS_FMAP_VBLOCK_A */
+ "VBLOCK_B", /* BIOS_FMAP_VBLOCK_B */
+};
+_Static_assert(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS,
+ "Size of fmap_name[] should match NUM_BIOS_COMPONENTS");
+
/* Location information for each component */
struct bios_area_s {
uint32_t offset; /* to avoid pointer math */
@@ -32,6 +44,10 @@ struct bios_area_s {
/* VBLOCK only */
uint32_t flags;
uint32_t version;
+
+ /* FW_MAIN only */
+ size_t fw_size; /* effective size from cbfstool (if available) */
+ struct vb2_hash metadata_hash;
};
/* State to track as we visit all components */
diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c
index 86e9bf29..56d92c5f 100644
--- a/futility/file_type_rwsig.c
+++ b/futility/file_type_rwsig.c
@@ -32,14 +32,6 @@
#define SIGNATURE_RSVD_SIZE 1024
#define EC_RW_FILENAME "EC_RW.bin"
-static inline void vb2_print_bytes(const void *ptr, uint32_t len)
-{
- const uint8_t *buf = (const uint8_t *)ptr;
- int i;
- for (i = 0; i < len; i++)
- printf("%02x", *buf++);
-}
-
static void show_sig(const char *name, const struct vb21_signature *sig)
{
printf("Signature: %s\n", name);
@@ -52,7 +44,7 @@ static void show_sig(const char *name, const struct vb21_signature *sig)
printf(" Total size: %#x (%d)\n", sig->c.total_size,
sig->c.total_size);
printf(" ID: ");
- vb2_print_bytes(&sig->id, sizeof(sig->id));
+ print_bytes(&sig->id, sizeof(sig->id));
printf("\n");
printf(" Data size: %#x (%d)\n", sig->data_size,
sig->data_size);
diff --git a/futility/futility.h b/futility/futility.h
index 18063d97..2bf3d7f4 100644
--- a/futility/futility.h
+++ b/futility/futility.h
@@ -159,6 +159,11 @@ enum futil_file_err futil_unmap_and_close_file(int fd, enum file_mode mode,
*/
void parse_digest_or_die(uint8_t *buf, int len, const char *str);
+/*
+ * Print provided buffer as hex string
+ */
+void print_bytes(const void *ptr, size_t len);
+
/* The CPU architecture is occasionally important */
enum arch_t {
ARCH_UNSPECIFIED,
diff --git a/futility/misc.c b/futility/misc.c
index 8366bc92..85358b29 100644
--- a/futility/misc.c
+++ b/futility/misc.c
@@ -27,6 +27,7 @@
#include "cgptlib_internal.h"
#include "file_type.h"
#include "futility.h"
+#include "host_misc.h"
/* Default is to support everything we can */
enum vboot_version vboot_version = VBOOT_VERSION_ALL;
@@ -420,53 +421,18 @@ enum futil_file_type ft_recognize_gpt(uint8_t *buf, uint32_t len)
return FILE_TYPE_CHROMIUMOS_DISK;
}
-static int parse_hex(uint8_t *val, const char *str)
+void parse_digest_or_die(uint8_t *buf, int len, const char *str)
{
- uint8_t v = 0;
- char c;
- int digit;
-
- for (digit = 0; digit < 2; digit++) {
- c = *str;
- if (!c)
- return 0;
- if (!isxdigit(c))
- return 0;
- c = tolower(c);
- if (c >= '0' && c <= '9')
- v += c - '0';
- else
- v += 10 + c - 'a';
- if (!digit)
- v <<= 4;
- str++;
+ if (!parse_hash(buf, len, str)) {
+ fprintf(stderr, "Invalid DIGEST \"%s\"\n", str);
+ exit(1);
}
-
- *val = v;
- return 1;
}
-void parse_digest_or_die(uint8_t *buf, int len, const char *str)
+void print_bytes(const void *ptr, size_t len)
{
- const char *s = str;
- int i;
-
- for (i = 0; i < len; i++) {
- /* skip whitespace */
- while (*s && isspace(*s))
- s++;
- if (!*s)
- break;
- if (!parse_hex(buf, s))
- break;
-
- /* on to the next byte */
- s += 2;
- buf++;
- }
+ const uint8_t *buf = (const uint8_t *)ptr;
- if ((i != len) || *s) {
- fprintf(stderr, "Invalid DIGEST \"%s\"\n", str);
- exit(1);
- }
+ for (size_t i = 0; i < len; i++)
+ printf("%02x", *buf++);
}
diff --git a/futility/vb2_helper.c b/futility/vb2_helper.c
index 9e875b98..afc0019f 100644
--- a/futility/vb2_helper.c
+++ b/futility/vb2_helper.c
@@ -38,14 +38,6 @@ enum futil_file_type ft_recognize_vb21_key(uint8_t *buf, uint32_t len)
return FILE_TYPE_UNKNOWN;
}
-static inline void vb2_print_bytes(const void *ptr, uint32_t len)
-{
- const uint8_t *buf = (const uint8_t *)ptr;
- int i;
- for (i = 0; i < len; i++)
- printf("%02x", *buf++);
-}
-
static int vb2_public_key_sha1sum(struct vb2_public_key *key,
struct vb2_hash *hash)
{
@@ -81,12 +73,12 @@ int show_vb21_pubkey_buf(const char *name, uint8_t *buf, uint32_t len,
vb2_get_hash_algorithm_name(key.hash_alg));
printf(" Version: 0x%08x\n", key.version);
printf(" ID: ");
- vb2_print_bytes(key.id, sizeof(*key.id));
+ print_bytes(key.id, sizeof(*key.id));
printf("\n");
if (vb2_public_key_sha1sum(&key, &hash) &&
memcmp(key.id, hash.sha1, sizeof(*key.id))) {
printf(" Key sha1sum: ");
- vb2_print_bytes(hash.sha1, sizeof(hash.sha1));
+ print_bytes(hash.sha1, sizeof(hash.sha1));
printf("\n");
}
return 0;
@@ -148,12 +140,12 @@ int ft_show_vb21_privkey(const char *name, void *data)
printf(" Hash Algorithm: %d %s\n", key->hash_alg,
vb2_get_hash_algorithm_name(key->hash_alg));
printf(" ID: ");
- vb2_print_bytes(&key->id, sizeof(key->id));
+ print_bytes(&key->id, sizeof(key->id));
printf("\n");
if (vb2_private_key_sha1sum(key, &hash) &&
memcmp(&key->id, hash.sha1, sizeof(key->id))) {
printf(" Key sha1sum: ");
- vb2_print_bytes(hash.sha1, sizeof(hash.sha1));
+ print_bytes(hash.sha1, sizeof(hash.sha1));
printf("\n");
}
vb2_private_key_free(key);
diff --git a/host/lib/cbfstool.c b/host/lib/cbfstool.c
index 4bbed028..0f6ed16f 100644
--- a/host/lib/cbfstool.c
+++ b/host/lib/cbfstool.c
@@ -4,9 +4,12 @@
*/
#include "2common.h"
+#include "2crypto.h"
#include "2return_codes.h"
-#include "subprocess.h"
#include "cbfstool.h"
+#include "host_misc.h"
+#include "subprocess.h"
+#include "vboot_host.h"
static const char *get_cbfstool_path(void)
{
@@ -68,3 +71,166 @@ vb2_error_t cbfstool_truncate(const char *file, const char *region,
return VB2_SUCCESS;
}
+
+/* Requires null-terminated buffer */
+static vb2_error_t extract_metadata_hash(const char *buf, struct vb2_hash *hash)
+{
+ enum vb2_hash_algorithm algo;
+ const char *to_find = "\n[METADATA HASH]";
+ char *algo_str = NULL;
+ char *hash_str = NULL;
+ vb2_error_t rv = VB2_ERROR_CBFSTOOL;
+
+ const char *start = strstr(buf, to_find);
+ if (start)
+ start += strlen(to_find);
+
+ if (start) {
+ const int matches = sscanf(start, " %m[^:\n\t ]:%m[^:\n\t ]",
+ &algo_str, &hash_str);
+
+ if (matches < 2)
+ goto done;
+
+ if (!algo_str || !vb2_lookup_hash_alg(algo_str, &algo) ||
+ algo == VB2_HASH_INVALID)
+ goto done;
+ hash->algo = algo;
+
+ if (!hash_str ||
+ strlen(hash_str) != (vb2_digest_size(algo) * 2) ||
+ !parse_hash(&hash->raw[0], vb2_digest_size(algo), hash_str))
+ goto done;
+
+ if (!strstr(buf, "] fully valid"))
+ goto done;
+
+ rv = VB2_SUCCESS;
+ }
+
+done:
+ if (rv != VB2_SUCCESS)
+ hash->algo = VB2_HASH_INVALID;
+
+ free(algo_str);
+ free(hash_str);
+
+ return rv;
+}
+
+vb2_error_t cbfstool_get_metadata_hash(const char *file, const char *region,
+ struct vb2_hash *hash)
+{
+ int status;
+ const char *cbfstool = get_cbfstool_path();
+ const size_t data_buffer_sz = 1024 * 1024;
+ char *data_buffer = malloc(data_buffer_sz);
+ vb2_error_t rv = VB2_ERROR_CBFSTOOL;
+
+ if (!data_buffer)
+ goto done;
+
+ memset(hash, 0, sizeof(*hash));
+ hash->algo = VB2_HASH_INVALID;
+
+ struct subprocess_target output = {
+ .type = TARGET_BUFFER_NULL_TERMINATED,
+ .buffer = {
+ .buf = data_buffer,
+ .size = data_buffer_sz,
+ },
+ };
+ const char *argv[] = {
+ cbfstool, file, "print", "-kv",
+ region ? "-r" : NULL, region, NULL
+ };
+
+ status = subprocess_run(argv, &subprocess_null, &output,
+ &subprocess_null);
+
+ if (status < 0) {
+ fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
+ __func__);
+ exit(1);
+ }
+
+ if (status > 0)
+ goto done;
+
+ rv = extract_metadata_hash(data_buffer, hash);
+
+done:
+ free(data_buffer);
+ return rv;
+}
+
+/* Requires null-terminated buffer */
+static char *extract_config_value(const char *buf, const char *config_field)
+{
+ char *to_find = NULL;
+
+ if (asprintf(&to_find, "\n%s=", config_field) == -1) {
+ fprintf(stderr, "Out of Memory\n");
+ VB2_DIE("Out of memory\n");
+ }
+
+ const char *start = strstr(buf, to_find);
+ if (start)
+ start += strlen(to_find);
+
+ free(to_find);
+
+ if (start) {
+ char *end = strchr(start, '\n');
+ if (end)
+ return strndup(start, end - start);
+ }
+
+ return NULL;
+}
+
+vb2_error_t cbfstool_get_config_value(const char *file, const char *region,
+ const char *config_field, char **value)
+{
+ int status;
+ const char *cbfstool = get_cbfstool_path();
+ const size_t data_buffer_sz = 1024 * 1024;
+ char *data_buffer = malloc(data_buffer_sz);
+ vb2_error_t rv = VB2_ERROR_CBFSTOOL;
+
+ *value = NULL;
+
+ if (!data_buffer)
+ goto done;
+
+ struct subprocess_target output = {
+ .type = TARGET_BUFFER_NULL_TERMINATED,
+ .buffer = {
+ .buf = data_buffer,
+ .size = data_buffer_sz,
+ },
+ };
+ const char *argv[] = {
+ cbfstool, file, "extract", "-n", "config", "-f", "/dev/stdout",
+ region ? "-r" : NULL, region, NULL
+ };
+
+ status = subprocess_run(argv, &subprocess_null, &output,
+ &subprocess_null);
+
+ if (status < 0) {
+ fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
+ __func__);
+ exit(1);
+ }
+
+ if (status > 0)
+ goto done;
+
+ *value = extract_config_value(data_buffer, config_field);
+
+ rv = VB2_SUCCESS;
+done:
+ free(data_buffer);
+ return rv;
+}
diff --git a/host/lib/host_misc.c b/host/lib/host_misc.c
index 63d1fee4..899d807f 100644
--- a/host/lib/host_misc.c
+++ b/host/lib/host_misc.c
@@ -116,3 +116,53 @@ vb2_error_t WriteFile(const char* filename, const void *data, uint64_t size)
fclose(f);
return 0;
}
+
+bool parse_hex(uint8_t *val, const char *str)
+{
+ uint8_t v = 0;
+ char c;
+ int digit;
+
+ for (digit = 0; digit < 2; digit++) {
+ c = *str;
+ if (!c)
+ return false;
+ if (!isxdigit(c))
+ return false;
+ c = tolower(c);
+ if (c >= '0' && c <= '9')
+ v += c - '0';
+ else
+ v += 10 + c - 'a';
+ if (!digit)
+ v <<= 4;
+ str++;
+ }
+
+ *val = v;
+ return true;
+}
+
+bool parse_hash(uint8_t *buf, size_t len, const char *str)
+{
+ const char *s = str;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ /* skip whitespace */
+ while (*s && isspace(*s))
+ s++;
+ if (!*s)
+ break;
+ if (!parse_hex(buf, s))
+ break;
+
+ /* on to the next byte */
+ s += 2;
+ buf++;
+ }
+
+ if (i != len || *s)
+ return false;
+ return true;
+}
diff --git a/host/lib/host_signature2.c b/host/lib/host_signature2.c
index 5ec307d6..a588d1ee 100644
--- a/host/lib/host_signature2.c
+++ b/host/lib/host_signature2.c
@@ -132,3 +132,28 @@ struct vb2_signature *vb2_calculate_signature(
/* Return the signature */
return sig;
}
+
+struct vb2_signature *
+vb2_create_signature_from_hash(const struct vb2_hash *hash)
+{
+ const uint32_t hsize = vb2_digest_size(hash->algo);
+
+ /* Unsupported algorithm */
+ if (!hsize)
+ return NULL;
+
+ const uint32_t full_hsize = offsetof(struct vb2_hash, raw) + hsize;
+
+ /* The body size is unknown, so set it to zero */
+ struct vb2_signature *sig =
+ (struct vb2_signature *)vb2_alloc_signature(full_hsize, 0);
+ if (!sig)
+ return NULL;
+
+ if (!memcpy(vb2_signature_data_mutable(sig), hash, full_hsize)) {
+ free(sig);
+ return NULL;
+ }
+
+ return sig;
+}
diff --git a/host/lib/include/cbfstool.h b/host/lib/include/cbfstool.h
index acc6e927..863039ec 100644
--- a/host/lib/include/cbfstool.h
+++ b/host/lib/include/cbfstool.h
@@ -4,9 +4,34 @@
*/
#include "2return_codes.h"
+#include "2sha.h"
#define ENV_CBFSTOOL "CBFSTOOL"
#define DEFAULT_CBFSTOOL "cbfstool"
vb2_error_t cbfstool_truncate(const char *file, const char *region,
size_t *new_size);
+
+/*
+ * Check whether image under `file` path supports CBFS_VERIFICATION,
+ * and contains metadata hash. Hash found is available under *hash. If it was
+ * not found, then hash type will be set to VB2_HASH_INVALID.
+ *
+ * If `region` is NULL, then region option will not be passed to cbfstool.
+ * Operations will be performed on default `COREBOOT` region.
+ */
+vb2_error_t cbfstool_get_metadata_hash(const char *file, const char *region,
+ struct vb2_hash *hash);
+
+/*
+ * Get value of `config` file field.
+ *
+ * This function extracts "config" file from selected region, parses it to find
+ * value of `config_field`, and returns it to `value` as allocated string
+ * (which has to be freed) or NULL if value was not found.
+ *
+ * If `region` is NULL, then region option will not be passed to cbfstool.
+ * Operations will be performed on default `COREBOOT` region.
+ */
+vb2_error_t cbfstool_get_config_value(const char *file, const char *region,
+ const char *config_field, char **value);
diff --git a/host/lib/include/host_misc.h b/host/lib/include/host_misc.h
index 1a59af54..96aba057 100644
--- a/host/lib/include/host_misc.h
+++ b/host/lib/include/host_misc.h
@@ -8,6 +8,8 @@
#ifndef VBOOT_REFERENCE_HOST_MISC_H_
#define VBOOT_REFERENCE_HOST_MISC_H_
+#include <stdbool.h>
+
#include "vboot_struct.h"
#include "vboot_api.h"
@@ -99,4 +101,25 @@ static inline const uint32_t roundup32(uint32_t v)
*/
uint32_t vb2_desc_size(const char *desc);
+/**
+ * Parse byte from hex string.
+ *
+ * @param val Pointer to the value buffer
+ * @param str String to parse
+ * @return true on success, false otherwise.
+ * */
+bool parse_hex(uint8_t *val, const char *str);
+
+
+/**
+ * Parse hash from string.
+ *
+ * @param buf Output buffer. Has to be at least `len` bytes long
+ * @param len Hash length in bytes
+ * @param str Hash string form. Has to be at least 2 * `len` long
+ * Whitespaces are ignored
+ * @return true on success, false otherwise.
+ */
+bool parse_hash(uint8_t *buf, size_t len, const char *str);
+
#endif /* VBOOT_REFERENCE_HOST_MISC_H_ */
diff --git a/host/lib/include/host_signature.h b/host/lib/include/host_signature.h
index 47ffe764..d154a9d5 100644
--- a/host/lib/include/host_signature.h
+++ b/host/lib/include/host_signature.h
@@ -85,4 +85,15 @@ struct vb2_signature *vb2_external_signature(const uint8_t *data, uint32_t size,
uint32_t key_algorithm,
const char *external_signer);
+/**
+ * Create signature using the provided hash as its body. Created signature
+ * contains vb2_hash trimmed to fit digest of its algorithm and nothing more.
+ *
+ * @param hash Hash to create signature from
+ *
+ * @return The signature, or NULL if error. Caller must free() it.
+ */
+struct vb2_signature *
+vb2_create_signature_from_hash(const struct vb2_hash *hash);
+
#endif /* VBOOT_REFERENCE_HOST_SIGNATURE_H_ */
diff --git a/tests/vb2_api_tests.c b/tests/vb2_api_tests.c
index 23078cf9..3ed8bd5f 100644
--- a/tests/vb2_api_tests.c
+++ b/tests/vb2_api_tests.c
@@ -38,6 +38,16 @@ const int mock_sig_size = 64;
static uint8_t digest_result[VB2_SHA256_DIGEST_SIZE];
static const uint32_t digest_result_size = sizeof(digest_result);
+static const struct vb2_hash mock_mhash = {
+ .algo = VB2_HASH_SHA256,
+ .sha256 = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ },
+};
+
/* Mocked function data */
static int force_dev_mode;
@@ -56,6 +66,7 @@ enum reset_type {
FOR_MISC,
FOR_EXTEND_HASH,
FOR_CHECK_HASH,
+ FOR_METADATA_HASH,
};
static void reset_common_data(enum reset_type t)
@@ -93,15 +104,31 @@ static void reset_common_data(enum reset_type t)
sd->preamble_size = sizeof(*pre);
vb2_set_workbuf_used(ctx, sd->preamble_offset + sd->preamble_size);
pre = vb2_member_of(sd, sd->preamble_offset);
- pre->body_signature.data_size = mock_body_size;
- pre->body_signature.sig_size = mock_sig_size;
- pre->flags = 0;
- sd->data_key_offset = sd->workbuf_used;
- sd->data_key_size = sizeof(*k) + 8;
- vb2_set_workbuf_used(ctx, sd->data_key_offset + sd->data_key_size);
- k = vb2_member_of(sd, sd->data_key_offset);
- k->algorithm = mock_algorithm;
+ if (t == FOR_METADATA_HASH) {
+ struct vb2_hash *mhash;
+ pre->body_signature.data_size = 0;
+ pre->body_signature.sig_size = offsetof(struct vb2_hash, raw) +
+ vb2_digest_size(mock_mhash.algo);
+ pre->body_signature.sig_offset =
+ vb2_offset_of(&pre->body_signature,
+ vb2_member_of(sd, sd->workbuf_used));
+ pre->flags = 0;
+ mhash = vb2_member_of(sd, sd->workbuf_used);
+ vb2_set_workbuf_used(ctx, pre->body_signature.sig_size);
+ memcpy(mhash, &mock_mhash, pre->body_signature.sig_size);
+ } else {
+ pre->body_signature.data_size = mock_body_size;
+ pre->body_signature.sig_size = mock_sig_size;
+ pre->flags = 0;
+
+ sd->data_key_offset = sd->workbuf_used;
+ sd->data_key_size = sizeof(*k) + 8;
+ vb2_set_workbuf_used(ctx,
+ sd->data_key_offset + sd->data_key_size);
+ k = vb2_member_of(sd, sd->data_key_offset);
+ k->algorithm = mock_algorithm;
+ }
if (t == FOR_EXTEND_HASH || t == FOR_CHECK_HASH)
vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY);
@@ -604,6 +631,7 @@ static void init_hash_tests(void)
{
struct vb2_packed_key *k;
int wb_used_before;
+ struct vb2_fw_preamble *pre;
/* For now, all we support is body signature hash */
reset_common_data(FOR_MISC);
@@ -649,6 +677,12 @@ static void init_hash_tests(void)
VB2_ERROR_API_INIT_HASH_DATA_KEY, "init hash data key");
reset_common_data(FOR_MISC);
+ pre = vb2_member_of(sd, sd->preamble_offset);
+ pre->body_signature.data_size = 0;
+ TEST_EQ(vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY),
+ VB2_ERROR_API_INIT_HASH_DATA_KEY, "init hash data size");
+
+ reset_common_data(FOR_MISC);
sd->data_key_size--;
TEST_EQ(vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY),
VB2_ERROR_UNPACK_KEY_SIZE, "init hash data key size");
@@ -792,6 +826,50 @@ static void check_hash_tests(void)
VB2_ERROR_RSA_VERIFY_DIGEST, "check hash finalize");
}
+static void get_metadata_hash_test(void)
+{
+ struct vb2_hash *hash;
+ struct vb2_fw_preamble *pre;
+
+ reset_common_data(FOR_MISC);
+ TEST_EQ(vb2api_get_metadata_hash(ctx, &hash),
+ VB2_ERROR_API_INIT_HASH_DATA_KEY, "check body sig data size");
+
+ reset_common_data(FOR_MISC);
+ sd->preamble_size = 0;
+ TEST_EQ(vb2api_get_metadata_hash(ctx, &hash),
+ VB2_ERROR_API_CHECK_HASH_PREAMBLE, "check preamble size");
+
+ reset_common_data(FOR_METADATA_HASH);
+ pre = vb2_member_of(sd, sd->preamble_offset);
+ pre->body_signature.sig_size = 8;
+ TEST_EQ(vb2api_get_metadata_hash(ctx, &hash),
+ VB2_ERROR_API_CHECK_HASH_SIG_SIZE, "check too small sig size");
+
+ reset_common_data(FOR_METADATA_HASH);
+ pre = vb2_member_of(sd, sd->preamble_offset);
+ pre->body_signature.data_size = mock_body_size;
+ TEST_EQ(vb2api_get_metadata_hash(ctx, &hash),
+ VB2_ERROR_API_INIT_HASH_DATA_KEY, "check incorrect data size");
+
+ reset_common_data(FOR_METADATA_HASH);
+ pre = vb2_member_of(sd, sd->preamble_offset);
+ hash = (struct vb2_hash *)vb2_signature_data_mutable(
+ &pre->body_signature);
+ hash->algo = VB2_HASH_INVALID;
+ TEST_EQ(vb2api_get_metadata_hash(ctx, &hash),
+ VB2_ERROR_API_CHECK_HASH_SIG_SIZE, "check unsupported algo");
+
+ reset_common_data(FOR_METADATA_HASH);
+ TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), VB2_SUCCESS,
+ "check metadata hash get");
+ TEST_PTR_NEQ(hash, NULL, "check metadata hash not null");
+ TEST_EQ(hash->algo, mock_mhash.algo, "check metadata hash algo");
+ TEST_EQ(memcmp(hash->raw, mock_mhash.raw,
+ vb2_digest_size(mock_mhash.algo)),
+ 0, "check metadata hash digest");
+}
+
int main(int argc, char* argv[])
{
misc_tests();
@@ -803,6 +881,8 @@ int main(int argc, char* argv[])
extend_hash_tests();
check_hash_tests();
+ get_metadata_hash_test();
+
get_pcr_digest_tests();
return gTestSuccess ? 0 : 255;