summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;