diff options
author | Randall Spangler <rspangler@chromium.org> | 2014-06-11 16:01:08 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-06-19 03:23:28 +0000 |
commit | 1803068173a625efd83d1cee8dd90843feb0d972 (patch) | |
tree | d60b7678f832fd2e4875e4cbfb8de1fcd30a059e /firmware/2lib | |
parent | da2b49cf08a27551fd910626f669910a636378d4 (diff) | |
download | vboot-1803068173a625efd83d1cee8dd90843feb0d972.tar.gz |
vboot2: misc higher-level routines, part 2
I'm breaking the last chunk of vboot2 into smaller pieces as I add
tests. This has the higher-level routines for verifying keyblock and
preamble.
BUG=chromium:370082
BRANCH=none
TEST=make clean && VBOOT2=1 COV=1 make
Change-Id: I82da9542c8857a3f89a85f206c9f5aecadf94a79
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/203501
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Diffstat (limited to 'firmware/2lib')
-rw-r--r-- | firmware/2lib/2misc.c | 213 | ||||
-rw-r--r-- | firmware/2lib/include/2misc.h | 20 |
2 files changed, 233 insertions, 0 deletions
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c index c0692c53..685cf07d 100644 --- a/firmware/2lib/2misc.c +++ b/firmware/2lib/2misc.c @@ -342,3 +342,216 @@ int vb2_select_fw_slot(struct vb2_context *ctx) return VB2_SUCCESS; } + +int vb2_verify_fw_keyblock(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_workbuf wb; + + uint8_t *key_data; + uint32_t key_size; + struct vb2_packed_key *packed_key; + struct vb2_public_key root_key; + + struct vb2_keyblock *kb; + uint32_t block_size; + + uint32_t sec_version; + int rv; + + vb2_workbuf_from_ctx(ctx, &wb); + + /* Read the root key */ + key_size = sd->gbb_rootkey_size; + key_data = vb2_workbuf_alloc(&wb, key_size); + if (!key_data) + return VB2_ERROR_FW_KEYBLOCK_WORKBUF_ROOT_KEY; + + rv = vb2ex_read_resource(ctx, VB2_RES_GBB, sd->gbb_rootkey_offset, + key_data, key_size); + if (rv) + return rv; + + /* Unpack the root key */ + rv = vb2_unpack_key(&root_key, key_data, key_size); + if (rv) + return rv; + + /* Load the firmware keyblock header after the root key */ + kb = vb2_workbuf_alloc(&wb, sizeof(*kb)); + if (!kb) + return VB2_ERROR_FW_KEYBLOCK_WORKBUF_HEADER; + + rv = vb2ex_read_resource(ctx, VB2_RES_FW_VBLOCK, 0, kb, sizeof(*kb)); + if (rv) + return rv; + + block_size = kb->keyblock_size; + + /* + * Load the entire keyblock, now that we know how big it is. Note that + * we're loading the entire keyblock instead of just the piece after + * the header. That means we re-read the header. But that's a tiny + * amount of data, and it makes the code much more straightforward. + */ + kb = vb2_workbuf_realloc(&wb, sizeof(*kb), block_size); + if (!kb) + return VB2_ERROR_FW_KEYBLOCK_WORKBUF; + + rv = vb2ex_read_resource(ctx, VB2_RES_FW_VBLOCK, 0, kb, block_size); + if (rv) + return rv; + + /* Verify the keyblock */ + rv = vb2_verify_keyblock(kb, block_size, &root_key, &wb); + if (rv) + return rv; + + /* Read the secure key version */ + rv = vb2_secdata_get(ctx, VB2_SECDATA_VERSIONS, &sec_version); + if (rv) + return rv; + + /* Key version is the upper 16 bits of the composite firmware version */ + if (kb->data_key.key_version > 0xffff) + return VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE; + if (kb->data_key.key_version < (sec_version >> 16)) + return VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK; + + sd->fw_version = kb->data_key.key_version << 16; + + /* + * Save the data key in the work buffer. This overwrites the root key + * we read above. That's ok, because now that we have the data key we + * no longer need the root key. + */ + packed_key = (struct vb2_packed_key *)key_data; + + packed_key->algorithm = kb->data_key.algorithm; + packed_key->key_version = kb->data_key.key_version; + packed_key->key_size = kb->data_key.key_size; + + /* + * Use memmove() instead of memcpy(). In theory, the destination will + * never overlap because with the source because the root key is likely + * to be at least as large as the data key, but there's no harm here in + * being paranoid. + */ + memmove(key_data + packed_key->key_offset, + (uint8_t*)&kb->data_key + kb->data_key.key_offset, + packed_key->key_size); + + /* Save the packed key offset and size */ + sd->workbuf_data_key_offset = vb2_offset_of(ctx->workbuf, key_data); + sd->workbuf_data_key_size = + packed_key->key_offset + packed_key->key_size; + + /* Preamble follows the keyblock in the vblock */ + sd->vblock_preamble_offset = kb->keyblock_size; + + /* Data key will persist in the workbuf after we return */ + ctx->workbuf_used = sd->workbuf_data_key_offset + + sd->workbuf_data_key_size; + + return VB2_SUCCESS; +} + +// TODO: Terrible that this and the low-level verification want to have the +// same function name. Pick a better name... +int vb2_verify_fw_preamble2(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_workbuf wb; + + uint8_t *key_data = ctx->workbuf + sd->workbuf_data_key_offset; + uint32_t key_size = sd->workbuf_data_key_size; + struct vb2_public_key data_key; + + /* Preamble goes in the next unused chunk of work buffer */ + struct vb2_fw_preamble *pre; + uint32_t pre_size; + + uint32_t sec_version; + int rv; + + vb2_workbuf_from_ctx(ctx, &wb); + + /* Unpack the firmware data key */ + if (!sd->workbuf_data_key_size) + return VB2_ERROR_FW_PREAMBLE2_DATA_KEY; + + rv = vb2_unpack_key(&data_key, key_data, key_size); + if (rv) + return rv; + + /* Load the firmware preamble header */ + pre = vb2_workbuf_alloc(&wb, sizeof(*pre)); + if (!pre) + return VB2_ERROR_FW_PREAMBLE2_WORKBUF_HEADER; + + rv = vb2ex_read_resource(ctx, VB2_RES_FW_VBLOCK, + sd->vblock_preamble_offset, + pre, sizeof(*pre)); + if (rv) + return rv; + + pre_size = pre->preamble_size; + + /* Load the entire firmware preamble, now that we know how big it is */ + pre = vb2_workbuf_realloc(&wb, sizeof(*pre), pre_size); + if (!pre) + return VB2_ERROR_FW_PREAMBLE2_WORKBUF; + + rv = vb2ex_read_resource(ctx, VB2_RES_FW_VBLOCK, + sd->vblock_preamble_offset, + pre, pre_size); + if (rv) + return rv; + + /* Work buffer now contains the data subkey data and the preamble */ + + /* Verify the preamble */ + rv = vb2_verify_fw_preamble(pre, pre_size, &data_key, &wb); + if (rv) + return rv; + + /* Read the secure key version */ + rv = vb2_secdata_get(ctx, VB2_SECDATA_VERSIONS, &sec_version); + if (rv) + return rv; + + /* + * Firmware version is the lower 16 bits of the composite firmware + * version. + */ + if (pre->firmware_version > 0xffff) + return VB2_ERROR_FW_PREAMBLE2_VERSION_RANGE; + + /* Combine with the key version from vb2_verify_fw_keyblock() */ + sd->fw_version |= pre->firmware_version; + if (sd->fw_version < sec_version) + return VB2_ERROR_FW_PREAMBLE2_VERSION_ROLLBACK; + + /* + * If this is a newer version than in secure storage, and we + * successfully booted the same slot last boot, roll forward the + * version in secure storage. + */ + if (sd->fw_version > sec_version && + sd->last_fw_slot == sd->fw_slot && + sd->last_fw_result == VB2_FW_RESULT_SUCCESS) { + + rv = vb2_secdata_set(ctx, VB2_SECDATA_VERSIONS, sd->fw_version); + if (rv) + return rv; + } + + /* Keep track of where we put the preamble */ + sd->workbuf_preamble_offset = vb2_offset_of(ctx->workbuf, pre); + sd->workbuf_preamble_size = pre_size; + + /* Preamble will persist in work buffer after we return */ + ctx->workbuf_used = sd->workbuf_preamble_offset + pre_size; + + return VB2_SUCCESS; +} diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h index f2e259e4..d90399a4 100644 --- a/firmware/2lib/include/2misc.h +++ b/firmware/2lib/include/2misc.h @@ -112,4 +112,24 @@ int vb2_check_tpm_clear(struct vb2_context *ctx); */ int vb2_select_fw_slot(struct vb2_context *ctx); +/** + * Verify the firmware keyblock using the root key. + * + * After this call, the data key is stored in the work buffer. + * + * @param ctx Vboot context + * @return VB2_SUCCESS, or error code on error. + */ +int vb2_verify_fw_keyblock(struct vb2_context *ctx); + +/** + * Verify the firmware preamble using the data subkey from the keyblock. + * + * After this call, the preamble is stored in the work buffer. + * + * @param ctx Vboot context + * @return VB2_SUCCESS, or error code on error. + */ +int vb2_verify_fw_preamble2(struct vb2_context *ctx); + #endif /* VBOOT_REFERENCE_VBOOT_2MISC_H_ */ |