diff options
Diffstat (limited to 'firmware/lib21')
-rw-r--r-- | firmware/lib21/api.c | 146 | ||||
-rw-r--r-- | firmware/lib21/common.c | 444 | ||||
-rw-r--r-- | firmware/lib21/misc.c | 238 | ||||
-rw-r--r-- | firmware/lib21/packed_key.c | 107 |
4 files changed, 935 insertions, 0 deletions
diff --git a/firmware/lib21/api.c b/firmware/lib21/api.c new file mode 100644 index 00000000..0df2cf69 --- /dev/null +++ b/firmware/lib21/api.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Externally-callable APIs + * (Firmware portion) + */ + +#include "2sysincludes.h" +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2secdata.h" +#include "2sha.h" +#include "2rsa.h" + +int vb2api_fw_phase3_2(struct vb2_context *ctx) +{ + int rv; + + /* Verify firmware keyblock */ + rv = vb2_load_fw_keyblock2(ctx); + if (rv) { + vb2_fail(ctx, VB2_RECOVERY_RO_INVALID_RW, rv); + return rv; + } + + /* Verify firmware preamble */ + rv = vb2_load_fw_preamble2(ctx); + if (rv) { + vb2_fail(ctx, VB2_RECOVERY_RO_INVALID_RW, rv); + return rv; + } + + return VB2_SUCCESS; +} + +int vb2api_init_hash2(struct vb2_context *ctx, + const struct vb2_guid *guid, + uint32_t *size) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + const struct vb2_fw_preamble2 *pre; + const struct vb2_signature2 *sig = NULL; + struct vb2_digest_context *dc; + struct vb2_workbuf wb; + uint32_t hash_offset; + int i; + + vb2_workbuf_from_ctx(ctx, &wb); + + /* Get preamble pointer */ + if (!sd->workbuf_preamble_size) + return VB2_ERROR_API_INIT_HASH_PREAMBLE; + pre = (const struct vb2_fw_preamble2 *) + (ctx->workbuf + sd->workbuf_preamble_offset); + + /* Find the matching signature */ + hash_offset = pre->hash_offset; + for (i = 0; i < pre->hash_count; i++) { + sig = (const struct vb2_signature2 *) + ((uint8_t *)pre + hash_offset); + + if (!memcmp(guid, &sig->guid, sizeof(*guid))) + break; + + hash_offset += sig->c.total_size; + } + if (i >= pre->hash_count) + return VB2_ERROR_API_INIT_HASH_GUID; /* No match */ + + /* Allocate workbuf space for the hash */ + if (sd->workbuf_hash_size) { + dc = (struct vb2_digest_context *) + (ctx->workbuf + sd->workbuf_hash_offset); + } else { + uint32_t dig_size = sizeof(*dc); + + dc = vb2_workbuf_alloc(&wb, dig_size); + if (!dc) + return VB2_ERROR_API_INIT_HASH_WORKBUF; + + sd->workbuf_hash_offset = vb2_offset_of(ctx->workbuf, dc); + sd->workbuf_hash_size = dig_size; + ctx->workbuf_used = sd->workbuf_hash_offset + dig_size; + } + + sd->hash_tag = vb2_offset_of(ctx->workbuf, sig); + sd->hash_remaining_size = sig->data_size; + + if (size) + *size = sig->data_size; + + return vb2_digest_init(dc, sig->hash_alg); +} + +int vb2api_check_hash2(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_digest_context *dc = (struct vb2_digest_context *) + (ctx->workbuf + sd->workbuf_hash_offset); + struct vb2_workbuf wb; + + uint8_t *digest; + uint32_t digest_size = vb2_digest_size(dc->hash_alg); + + const struct vb2_signature2 *sig; + + int rv; + + vb2_workbuf_from_ctx(ctx, &wb); + + /* Get signature pointer */ + if (!sd->hash_tag) + return VB2_ERROR_API_CHECK_HASH_TAG; + sig = (const struct vb2_signature2 *)(ctx->workbuf + sd->hash_tag); + + /* Must have initialized hash digest work area */ + if (!sd->workbuf_hash_size) + return VB2_ERROR_API_CHECK_HASH_WORKBUF; + + /* Should have hashed the right amount of data */ + if (sd->hash_remaining_size) + return VB2_ERROR_API_CHECK_HASH_SIZE; + + /* Allocate the digest */ + digest = vb2_workbuf_alloc(&wb, digest_size); + if (!digest) + return VB2_ERROR_API_CHECK_HASH_WORKBUF_DIGEST; + + /* Finalize the digest */ + rv = vb2_digest_finalize(dc, digest, digest_size); + if (rv) + return rv; + + /* Compare with the signature */ + if (vb2_safe_memcmp(digest, (const uint8_t *)sig + sig->sig_offset, + digest_size)) + return VB2_ERROR_API_CHECK_HASH_SIG; + + // TODO: the old check-hash function called vb2_fail() on any mismatch. + // I don't think it should do that; the caller should. + + return VB2_SUCCESS; +} diff --git a/firmware/lib21/common.c b/firmware/lib21/common.c new file mode 100644 index 00000000..a5ebc70c --- /dev/null +++ b/firmware/lib21/common.c @@ -0,0 +1,444 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Signature validation functions + */ + +#include "2sysincludes.h" +#include "2common.h" +#include "2rsa.h" +#include "2sha.h" + +const char *vb2_common_desc(const void *buf) +{ + const struct vb2_struct_common *c = buf; + + return c->desc_size ? (const char *)c + c->fixed_size : ""; +} + +int vb2_verify_common_header(const void *parent, uint32_t parent_size) +{ + const struct vb2_struct_common *c = parent; + + /* Parent buffer size must be at least the claimed total size */ + if (parent_size < c->total_size) + return VB2_ERROR_COMMON_TOTAL_SIZE; + + /* + * And big enough for the fixed size, which itself must be at least as + * big as the common struct header. + */ + if (c->total_size < c->fixed_size || c->fixed_size < sizeof(*c)) + return VB2_ERROR_COMMON_FIXED_SIZE; + + /* Make sure sizes are all multiples of 32 bits */ + if (!vb2_aligned(c->total_size, sizeof(uint32_t))) + return VB2_ERROR_COMMON_TOTAL_UNALIGNED; + if (!vb2_aligned(c->fixed_size, sizeof(uint32_t))) + return VB2_ERROR_COMMON_FIXED_UNALIGNED; + if (!vb2_aligned(c->desc_size, sizeof(uint32_t))) + return VB2_ERROR_COMMON_DESC_UNALIGNED; + + /* Check description */ + if (c->desc_size > 0) { + /* Make sure description fits and doesn't wrap */ + if (c->fixed_size + c->desc_size < c->fixed_size) + return VB2_ERROR_COMMON_DESC_WRAPS; + if (c->fixed_size + c->desc_size > c->total_size) + return VB2_ERROR_COMMON_DESC_SIZE; + + /* Description must be null-terminated */ + if (vb2_common_desc(c)[c->desc_size - 1] != 0) + return VB2_ERROR_COMMON_DESC_TERMINATOR; + } + + return VB2_SUCCESS; +} + +int vb2_verify_common_member(const void *parent, + uint32_t *min_offset, + uint32_t member_offset, + uint32_t member_size) +{ + const struct vb2_struct_common *c = parent; + uint32_t member_end = member_offset + member_size; + + /* Make sure member doesn't wrap */ + if (member_end < member_offset) + return VB2_ERROR_COMMON_MEMBER_WRAPS; + + /* Member offset and size must be 32-bit aligned */ + if (!vb2_aligned(member_offset, sizeof(uint32_t)) || + !vb2_aligned(member_size, sizeof(uint32_t))) + return VB2_ERROR_COMMON_MEMBER_UNALIGNED; + + /* Initialize minimum offset if necessary */ + if (!*min_offset) + *min_offset = c->fixed_size + c->desc_size; + + /* Member must be after minimum offset */ + if (member_offset < *min_offset) + return VB2_ERROR_COMMON_MEMBER_OVERLAP; + + /* Member must end before total size */ + if (member_end > c->total_size) + return VB2_ERROR_COMMON_MEMBER_SIZE; + + /* Update minimum offset for subsequent checks */ + *min_offset = member_end; + + return VB2_SUCCESS; +} + +int vb2_verify_common_subobject(const void *parent, + uint32_t *min_offset, + uint32_t member_offset) +{ + const struct vb2_struct_common *p = parent; + const struct vb2_struct_common *m = + (const struct vb2_struct_common *) + ((const uint8_t *)parent + member_offset); + int rv; + + /* + * Verify the parent has space at the member offset for the common + * header. + */ + rv = vb2_verify_common_member(parent, min_offset, member_offset, + sizeof(*m)); + if (rv) + return rv; + + /* + * Now it's safe to look at the member's header, and verify any + * additional data for the object past its common header fits in the + * parent. + */ + rv = vb2_verify_common_header(m, p->total_size - member_offset); + if (rv) + return rv; + + /* Advance the min offset to the end of the subobject */ + *min_offset = member_offset + m->total_size; + + return VB2_SUCCESS; +} + +uint32_t vb2_sig_size(enum vb2_signature_algorithm sig_alg, + enum vb2_hash_algorithm hash_alg) +{ + uint32_t digest_size = vb2_digest_size(hash_alg); + + /* Fail if we don't support the hash algorithm */ + if (!digest_size) + return 0; + + /* Handle unsigned hashes */ + if (sig_alg == VB2_SIG_NONE) + return digest_size; + + return vb2_rsa_sig_size(sig_alg); +} + +const struct vb2_guid *vb2_hash_guid(enum vb2_hash_algorithm hash_alg) +{ + switch(hash_alg) { +#ifdef VB2_SUPPORT_SHA1 + case VB2_HASH_SHA1: + { + static const struct vb2_guid guid = VB2_GUID_NONE_SHA1; + return &guid; + } +#endif +#ifdef VB2_SUPPORT_SHA256 + case VB2_HASH_SHA256: + { + static const struct vb2_guid guid = + VB2_GUID_NONE_SHA256; + return &guid; + } +#endif +#ifdef VB2_SUPPORT_SHA512 + case VB2_HASH_SHA512: + { + static const struct vb2_guid guid = + VB2_GUID_NONE_SHA512; + return &guid; + } +#endif + default: + return NULL; + } +} + +int vb2_verify_signature2(const struct vb2_signature2 *sig, + uint32_t size) +{ + uint32_t min_offset = 0; + uint32_t expect_sig_size; + int rv; + + /* Check magic number */ + if (sig->c.magic != VB2_MAGIC_SIGNATURE2) + return VB2_ERROR_SIG_MAGIC; + + /* Make sure common header is good */ + rv = vb2_verify_common_header(sig, size); + if (rv) + return rv; + + /* + * Check for compatible version. No need to check minor version, since + * that's compatible across readers matching the major version, and we + * haven't added any new fields. + */ + if (sig->c.struct_version_major != VB2_SIGNATURE2_VERSION_MAJOR) + return VB2_ERROR_SIG_VERSION; + + /* Make sure header is big enough for signature */ + if (sig->c.fixed_size < sizeof(*sig)) + return VB2_ERROR_SIG_HEADER_SIZE; + + /* Make sure signature data is inside */ + rv = vb2_verify_common_member(sig, &min_offset, + sig->sig_offset, sig->sig_size); + if (rv) + return rv; + + /* Make sure signature size is correct for the algorithm */ + expect_sig_size = vb2_sig_size(sig->sig_alg, sig->hash_alg); + if (!expect_sig_size) + return VB2_ERROR_SIG_ALGORITHM; + if (sig->sig_size != expect_sig_size) + return VB2_ERROR_SIG_SIZE; + + return VB2_SUCCESS; +} + +/** + * Return the signature data for a signature + */ +static uint8_t *vb2_signature2_data(struct vb2_signature2 *sig) +{ + return (uint8_t *)sig + sig->sig_offset; +} + +int vb2_verify_digest2(const struct vb2_public_key *key, + struct vb2_signature2 *sig, + const uint8_t *digest, + const struct vb2_workbuf *wb) +{ + uint32_t key_sig_size = vb2_sig_size(key->sig_alg, key->hash_alg); + + /* If we can't figure out the signature size, key algorithm was bad */ + if (!key_sig_size) + return VB2_ERROR_VDATA_ALGORITHM; + + /* Make sure the signature and key algorithms match */ + if (key->sig_alg != sig->sig_alg || key->hash_alg != sig->hash_alg) + return VB2_ERROR_VDATA_ALGORITHM_MISMATCH; + + if (sig->sig_size != key_sig_size) + return VB2_ERROR_VDATA_SIG_SIZE; + + if (key->sig_alg == VB2_SIG_NONE) { + /* Bare hash */ + if (vb2_safe_memcmp(vb2_signature2_data(sig), + digest, key_sig_size)) + return VB2_ERROR_VDATA_VERIFY_DIGEST; + + return VB2_SUCCESS; + } else { + /* RSA-signed digest */ + return vb2_rsa_verify_digest(key, + vb2_signature2_data(sig), + digest, wb); + } +} + +int vb2_verify_data2(const void *data, + uint32_t size, + struct vb2_signature2 *sig, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + struct vb2_workbuf wblocal = *wb; + struct vb2_digest_context *dc; + uint8_t *digest; + uint32_t digest_size; + int rv; + + if (sig->data_size != size) { + VB2_DEBUG("Wrong amount of data signed.\n"); + return VB2_ERROR_VDATA_SIZE; + } + + /* Digest goes at start of work buffer */ + digest_size = vb2_digest_size(key->hash_alg); + if (!digest_size) + return VB2_ERROR_VDATA_DIGEST_SIZE; + + digest = vb2_workbuf_alloc(&wblocal, digest_size); + if (!digest) + return VB2_ERROR_VDATA_WORKBUF_DIGEST; + + /* Hashing requires temp space for the context */ + dc = vb2_workbuf_alloc(&wblocal, sizeof(*dc)); + if (!dc) + return VB2_ERROR_VDATA_WORKBUF_HASHING; + + rv = vb2_digest_init(dc, key->hash_alg); + if (rv) + return rv; + + rv = vb2_digest_extend(dc, data, size); + if (rv) + return rv; + + rv = vb2_digest_finalize(dc, digest, digest_size); + if (rv) + return rv; + + vb2_workbuf_free(&wblocal, sizeof(*dc)); + + return vb2_verify_digest2(key, sig, digest, &wblocal); +} + +int vb2_verify_keyblock2(struct vb2_keyblock2 *block, + uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + uint32_t min_offset = 0, sig_offset; + int rv, i; + + /* Check magic number */ + if (block->c.magic != VB2_MAGIC_KEYBLOCK2) + return VB2_ERROR_KEYBLOCK_MAGIC; + + /* Make sure common header is good */ + rv = vb2_verify_common_header(block, size); + if (rv) + return rv; + + /* + * Check for compatible version. No need to check minor version, since + * that's compatible across readers matching the major version, and we + * haven't added any new fields. + */ + if (block->c.struct_version_major != VB2_KEYBLOCK2_VERSION_MAJOR) + return VB2_ERROR_KEYBLOCK_HEADER_VERSION; + + /* Make sure header is big enough */ + if (block->c.fixed_size < sizeof(*block)) + return VB2_ERROR_KEYBLOCK_SIZE; + + /* Make sure data key is inside */ + rv = vb2_verify_common_subobject(block, &min_offset, block->key_offset); + if (rv) + return rv; + + /* Loop over signatures */ + sig_offset = block->sig_offset; + for (i = 0; i < block->sig_count; i++, sig_offset = min_offset) { + struct vb2_signature2 *sig; + + /* Make sure signature is inside keyblock */ + rv = vb2_verify_common_subobject(block, &min_offset, + sig_offset); + if (rv) + return rv; + + sig = (struct vb2_signature2 *)((uint8_t *)block + sig_offset); + + /* Verify the signature integrity */ + rv = vb2_verify_signature2(sig, + block->c.total_size - sig_offset); + if (rv) + return rv; + + /* Skip signature if it doesn't match the key GUID */ + if (memcmp(&sig->guid, key->guid, GUID_SIZE)) + continue; + + /* Make sure we signed the right amount of data */ + if (sig->data_size != block->sig_offset) + return VB2_ERROR_KEYBLOCK_SIGNED_SIZE; + + return vb2_verify_data2(block, block->sig_offset, sig, key, wb); + } + + /* If we're still here, no signature matched the key GUID */ + return VB2_ERROR_KEYBLOCK_SIG_GUID; +} + +int vb2_verify_fw_preamble2(struct vb2_fw_preamble2 *preamble, + uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + struct vb2_signature2 *sig; + uint32_t min_offset = 0, hash_offset; + int rv, i; + + /* Check magic number */ + if (preamble->c.magic != VB2_MAGIC_FW_PREAMBLE2) + return VB2_ERROR_PREAMBLE_MAGIC; + + /* Make sure common header is good */ + rv = vb2_verify_common_header(preamble, size); + if (rv) + return rv; + + /* + * Check for compatible version. No need to check minor version, since + * that's compatible across readers matching the major version, and we + * haven't added any new fields. + */ + if (preamble->c.struct_version_major != VB2_FW_PREAMBLE2_VERSION_MAJOR) + return VB2_ERROR_PREAMBLE_HEADER_VERSION; + + /* Make sure header is big enough */ + if (preamble->c.fixed_size < sizeof(*preamble)) + return VB2_ERROR_PREAMBLE_SIZE; + + /* Make sure all hash signatures are inside */ + hash_offset = preamble->hash_offset; + for (i = 0; i < preamble->hash_count; i++, hash_offset = min_offset) { + /* Make sure signature is inside preamble */ + rv = vb2_verify_common_subobject(preamble, &min_offset, + hash_offset); + if (rv) + return rv; + + sig = (struct vb2_signature2 *) + ((uint8_t *)preamble + hash_offset); + + /* Verify the signature integrity */ + rv = vb2_verify_signature2( + sig, preamble->c.total_size - hash_offset); + if (rv) + return rv; + + /* Hashes must all be unsigned */ + if (sig->sig_alg != VB2_SIG_NONE) + return VB2_ERROR_PREAMBLE_HASH_SIGNED; + } + + /* Make sure signature is inside preamble */ + rv = vb2_verify_common_subobject(preamble, &min_offset, + preamble->sig_offset); + if (rv) + return rv; + + /* Verify preamble signature */ + sig = (struct vb2_signature2 *)((uint8_t *)preamble + + preamble->sig_offset); + + rv = vb2_verify_data2(preamble, preamble->sig_offset, sig, key, wb); + if (rv) + return rv; + + return VB2_SUCCESS; +} diff --git a/firmware/lib21/misc.c b/firmware/lib21/misc.c new file mode 100644 index 00000000..dbdf95c0 --- /dev/null +++ b/firmware/lib21/misc.c @@ -0,0 +1,238 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Misc functions which need access to vb2_context but are not public APIs + */ + +#include "2sysincludes.h" +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2secdata.h" +#include "2sha.h" +#include "2rsa.h" + +/** + * Read an object with a common struct header from a verified boot resource. + * + * On success, an object buffer will be allocated in the work buffer, the + * object will be stored into the buffer, and *buf_ptr will point to the + * object. + * + * @param ctx Vboot context + * @param index Resource index to read + * @param offset Byte offset within resource to start at + * @param buf_ptr Destination for object pointer + * @return VB2_SUCCESS, or error code on error. + */ +int vb2_read_resource_object(struct vb2_context *ctx, + enum vb2_resource_index index, + uint32_t offset, + struct vb2_workbuf *wb, + void **buf_ptr) +{ + struct vb2_struct_common c; + void *buf; + int rv; + + *buf_ptr = NULL; + + /* Read the common header */ + rv = vb2ex_read_resource(ctx, index, offset, &c, sizeof(c)); + if (rv) + return rv; + + /* Allocate a buffer for the object, now that we know how big it is */ + buf = vb2_workbuf_alloc(wb, c.total_size); + if (!buf) + return VB2_ERROR_READ_RESOURCE_OBJECT_BUF; + + /* Read the object */ + rv = vb2ex_read_resource(ctx, index, offset, buf, c.total_size); + if (rv) { + vb2_workbuf_free(wb, c.total_size); + return rv; + } + + /* Save the pointer */ + *buf_ptr = buf; + return VB2_SUCCESS; +} + +int vb2_load_fw_keyblock2(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_key2 *packed_key; + struct vb2_public_key root_key; + struct vb2_keyblock2 *kb; + + 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_key2(&root_key, key_data, key_size); + if (rv) + return rv; + + /* + * Load the firmware keyblock common header into the work buffer after + * the root key. + */ + rv = vb2_read_resource_object(ctx, VB2_RES_FW_VBLOCK, 0, &wb, + (void **)&kb); + if (rv) + return rv; + + /* Verify the keyblock */ + rv = vb2_verify_keyblock2(kb, kb->c.total_size, &root_key, &wb); + if (rv) + return rv; + + /* Preamble follows the keyblock in the vblock */ + sd->vblock_preamble_offset = kb->c.total_size; + + /* Read the secure key version */ + rv = vb2_secdata_get(ctx, VB2_SECDATA_VERSIONS, &sec_version); + if (rv) + return rv; + + packed_key = (struct vb2_packed_key2 *)((uint8_t *)kb + kb->key_offset); + + /* Key version is the upper 16 bits of the composite firmware version */ + if (packed_key->key_version > 0xffff) + return VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE; + if (packed_key->key_version < (sec_version >> 16)) + return VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK; + + sd->fw_version = packed_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. + * + * Use memmove() instead of memcpy(). In theory, the destination will + * never overlap 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, packed_key->c.total_size); + packed_key = (struct vb2_packed_key2 *)key_data; + + /* 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->c.total_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; +} + +int vb2_load_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_preamble2 *pre; + + 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_key2(&data_key, key_data, key_size); + if (rv) + return rv; + + /* Load the firmware preamble */ + rv = vb2_read_resource_object(ctx, VB2_RES_FW_VBLOCK, + sd->vblock_preamble_offset, &wb, + (void **)&pre); + if (rv) + return rv; + + /* Work buffer now contains the data subkey data and the preamble */ + + /* Verify the preamble */ + rv = vb2_verify_fw_preamble2(pre, pre->c.total_size, &data_key, &wb); + if (rv) + return rv; + + /* Move the preamble down now that the data key is no longer used */ + memmove(key_data, pre, pre->c.total_size); + pre = (struct vb2_fw_preamble2 *)key_data; + + /* Data key is now gone */ + sd->workbuf_data_key_offset = sd->workbuf_data_key_size = 0; + + /* 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_load_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->c.total_size; + + /* Preamble will persist in work buffer after we return */ + ctx->workbuf_used = sd->workbuf_preamble_offset + + sd->workbuf_preamble_size; + + return VB2_SUCCESS; +} diff --git a/firmware/lib21/packed_key.c b/firmware/lib21/packed_key.c new file mode 100644 index 00000000..4019213b --- /dev/null +++ b/firmware/lib21/packed_key.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Key unpacking functions + */ + +#include "2sysincludes.h" +#include "2common.h" +#include "2rsa.h" + +int vb2_unpack_key2_data(struct vb2_public_key *key, + const uint8_t *key_data, + uint32_t key_size) +{ + const uint32_t *buf32 = (const uint32_t *)key_data; + uint32_t expected_key_size = vb2_packed_key_size(key->sig_alg); + + /* Make sure buffer is the correct length */ + if (!expected_key_size || expected_key_size != key_size) { + VB2_DEBUG("Wrong key size for algorithm\n"); + return VB2_ERROR_UNPACK_KEY_SIZE; + } + + /* Check for alignment */ + if (!vb2_aligned(buf32, sizeof(uint32_t))) + return VB2_ERROR_UNPACK_KEY_ALIGN; + + key->arrsize = buf32[0]; + + /* Sanity check key array size */ + if (key->arrsize * sizeof(uint32_t) != vb2_rsa_sig_size(key->sig_alg)) + return VB2_ERROR_UNPACK_KEY_ARRAY_SIZE; + + key->n0inv = buf32[1]; + + /* Arrays point inside the key data */ + key->n = buf32 + 2; + key->rr = buf32 + 2 + key->arrsize; + + return VB2_SUCCESS; +} + +int vb2_unpack_key2(struct vb2_public_key *key, + const uint8_t *buf, + uint32_t size) +{ + const struct vb2_packed_key2 *pkey = + (const struct vb2_packed_key2 *)buf; + uint32_t sig_size; + uint32_t min_offset = 0; + int rv; + + /* + * Check magic number. + * + * If it doesn't match, pass through to the old packed key format. + * + * TODO: remove passthru when signing scripts have switched over to + * use the new format. + */ + if (pkey->c.magic != VB2_MAGIC_PACKED_KEY2) + return vb2_unpack_key(key, buf, size); + + rv = vb2_verify_common_header(buf, size); + if (rv) + return rv; + + /* Make sure key data is inside */ + rv = vb2_verify_common_member(pkey, &min_offset, + pkey->key_offset, pkey->key_size); + if (rv) + return rv; + + /* + * Check for compatible version. No need to check minor version, since + * that's compatible across readers matching the major version, and we + * haven't added any new fields. + */ + if (pkey->c.struct_version_major != VB2_PACKED_KEY2_VERSION_MAJOR) + return VB2_ERROR_UNPACK_KEY_STRUCT_VERSION; + + /* Copy key algorithms */ + key->hash_alg = pkey->hash_alg; + if (!vb2_digest_size(key->hash_alg)) + return VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM; + + key->sig_alg = pkey->sig_alg; + if (key->sig_alg != VB2_SIG_NONE) { + sig_size = vb2_rsa_sig_size(key->sig_alg); + if (!sig_size) + return VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM; + rv = vb2_unpack_key2_data( + key, + (const uint8_t *)pkey + pkey->key_offset, + pkey->key_size); + if (rv) + return rv; + } + + /* Key description */ + key->desc = vb2_common_desc(pkey); + key->version = pkey->key_version; + key->guid = &pkey->guid; + + return VB2_SUCCESS; +} |