summaryrefslogtreecommitdiff
path: root/firmware/lib21
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/lib21')
-rw-r--r--firmware/lib21/api.c146
-rw-r--r--firmware/lib21/common.c444
-rw-r--r--firmware/lib21/misc.c238
-rw-r--r--firmware/lib21/packed_key.c107
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;
+}