summaryrefslogtreecommitdiff
path: root/bdb/bdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'bdb/bdb.c')
-rw-r--r--bdb/bdb.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/bdb/bdb.c b/bdb/bdb.c
new file mode 100644
index 00000000..d3327a51
--- /dev/null
+++ b/bdb/bdb.c
@@ -0,0 +1,398 @@
+/* Copyright (c) 2015 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.
+ *
+ * Boot descriptor block firmware functions
+ */
+
+#include <string.h>
+#include "bdb.h"
+
+/*****************************************************************************/
+
+/**
+ * Check if string contains a null terminator.
+ *
+ * Bytes after the null terminator do not need to be null.
+ *
+ * @param s String to check
+ * @param size Size of string buffer in characters
+ * @return 1 if string has a null terminator, 0 if not
+ */
+int string_has_null(const char *s, size_t size)
+{
+ for (; size; size--) {
+ if (*s++ == 0)
+ return 1;
+ }
+ return 0;
+}
+
+int bdb_check_header(const struct bdb_header *p, size_t size)
+{
+ if (size < sizeof(*p) || size < p->struct_size)
+ return BDB_ERROR_BUF_SIZE;
+
+ if (p->struct_magic != BDB_HEADER_MAGIC)
+ return BDB_ERROR_STRUCT_MAGIC;
+
+ if (p->struct_major_version != BDB_HEADER_VERSION_MAJOR)
+ return BDB_ERROR_STRUCT_VERSION;
+
+ /* Note that minor version doesn't matter yet */
+
+ if (p->struct_size < sizeof(*p))
+ return BDB_ERROR_STRUCT_SIZE;
+
+ if (p->oem_area_0_size & 3)
+ return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
+
+ /*
+ * Make sure the BDB is at least big enough for us. At this point, all
+ * the caller may have loaded is this header We'll check if there's
+ * space for everything else after we load it.
+ */
+ if (p->bdb_size < sizeof(*p))
+ return BDB_ERROR_BDB_SIZE;
+
+ /*
+ * The rest of the fields don't matter yet; we'll check them when we
+ * check the BDB itself.
+ */
+ return BDB_SUCCESS;
+}
+
+int bdb_check_key(const struct bdb_key *p, size_t size)
+{
+ size_t expect_key_size = 0;
+
+ if (size < sizeof(*p) || size < p->struct_size)
+ return BDB_ERROR_BUF_SIZE;
+
+ if (p->struct_magic != BDB_KEY_MAGIC)
+ return BDB_ERROR_STRUCT_MAGIC;
+
+ if (p->struct_major_version != BDB_KEY_VERSION_MAJOR)
+ return BDB_ERROR_STRUCT_VERSION;
+
+ /* Note that minor version doesn't matter yet */
+
+ if (!string_has_null(p->description, sizeof(p->description)))
+ return BDB_ERROR_DESCRIPTION;
+
+ /* We currently only support SHA-256 */
+ if (p->hash_alg != BDB_HASH_ALG_SHA256)
+ return BDB_ERROR_HASH_ALG;
+
+ /* Make sure signature algorithm and size are correct */
+ switch (p->sig_alg) {
+ case BDB_SIG_ALG_RSA4096:
+ expect_key_size = BDB_RSA4096_KEY_DATA_SIZE;
+ break;
+ case BDB_SIG_ALG_ECSDSA521:
+ expect_key_size = BDB_ECDSA521_KEY_DATA_SIZE;
+ break;
+ case BDB_SIG_ALG_RSA3072B:
+ expect_key_size = BDB_RSA3072B_KEY_DATA_SIZE;
+ break;
+ default:
+ return BDB_ERROR_SIG_ALG;
+ }
+
+ if (p->struct_size < sizeof(*p) + expect_key_size)
+ return BDB_ERROR_STRUCT_SIZE;
+
+ return BDB_SUCCESS;
+}
+
+int bdb_check_sig(const struct bdb_sig *p, size_t size)
+{
+ size_t expect_sig_size = 0;
+
+ if (size < sizeof(*p) || size < p->struct_size)
+ return BDB_ERROR_BUF_SIZE;
+
+ if (p->struct_magic != BDB_SIG_MAGIC)
+ return BDB_ERROR_STRUCT_MAGIC;
+
+ if (p->struct_major_version != BDB_SIG_VERSION_MAJOR)
+ return BDB_ERROR_STRUCT_VERSION;
+
+ /* Note that minor version doesn't matter yet */
+
+ if (!string_has_null(p->description, sizeof(p->description)))
+ return BDB_ERROR_DESCRIPTION;
+
+ /* We currently only support SHA-256 */
+ if (p->hash_alg != BDB_HASH_ALG_SHA256)
+ return BDB_ERROR_HASH_ALG;
+
+ /* Make sure signature algorithm and size are correct */
+ switch (p->sig_alg) {
+ case BDB_SIG_ALG_RSA4096:
+ expect_sig_size = BDB_RSA4096_SIG_SIZE;
+ break;
+ case BDB_SIG_ALG_ECSDSA521:
+ expect_sig_size = BDB_ECDSA521_SIG_SIZE;
+ break;
+ case BDB_SIG_ALG_RSA3072B:
+ expect_sig_size = BDB_RSA3072B_SIG_SIZE;
+ break;
+ default:
+ return BDB_ERROR_SIG_ALG;
+ }
+
+ if (p->struct_size < sizeof(*p) + expect_sig_size)
+ return BDB_ERROR_STRUCT_SIZE;
+
+ return BDB_SUCCESS;
+}
+
+int bdb_check_data(const struct bdb_data *p, size_t size)
+{
+ size_t need_size;
+
+ if (size < sizeof(*p) || size < p->signed_size)
+ return BDB_ERROR_BUF_SIZE;
+
+ if (p->struct_magic != BDB_DATA_MAGIC)
+ return BDB_ERROR_STRUCT_MAGIC;
+
+ if (p->struct_major_version != BDB_DATA_VERSION_MAJOR)
+ return BDB_ERROR_STRUCT_VERSION;
+
+ /* Note that minor version doesn't matter yet */
+
+ if (!string_has_null(p->description, sizeof(p->description)))
+ return BDB_ERROR_DESCRIPTION;
+
+ if (p->struct_size < sizeof(*p))
+ return BDB_ERROR_STRUCT_SIZE;
+
+ if (p->hash_entry_size < sizeof(struct bdb_hash))
+ return BDB_ERROR_HASH_ENTRY_SIZE;
+
+ /* Calculate expected size */
+ need_size = p->struct_size + p->num_hashes * p->hash_entry_size;
+
+ /* Make sure OEM area size doesn't cause wraparound */
+ if (need_size + p->oem_area_1_size < need_size)
+ return BDB_ERROR_OEM_AREA_SIZE;
+ if (p->oem_area_1_size & 3)
+ return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
+ need_size += p->oem_area_1_size;
+
+ if (p->signed_size < need_size)
+ return BDB_ERROR_SIGNED_SIZE;
+
+ return BDB_SUCCESS;
+}
+
+/*****************************************************************************/
+
+const struct bdb_header *bdb_get_header(const void *buf)
+{
+ return buf;
+}
+
+const struct bdb_key *bdb_get_bdbkey(const void *buf)
+{
+ const struct bdb_header *h = bdb_get_header(buf);
+ const uint8_t *b8 = buf;
+
+ /* BDB key follows header */
+ return (const struct bdb_key *)(b8 + h->struct_size);
+}
+
+const void *bdb_get_oem_area_0(const void *buf)
+{
+ const struct bdb_key *k = bdb_get_bdbkey(buf);
+ const uint8_t *b8 = (const uint8_t *)k;
+
+ /* OEM area 0 follows BDB key */
+ return b8 + k->struct_size;
+}
+
+const struct bdb_key *bdb_get_subkey(const void *buf)
+{
+ const struct bdb_header *h = bdb_get_header(buf);
+ const uint8_t *b8 = bdb_get_oem_area_0(buf);
+
+ /* Subkey follows OEM area 0 */
+ return (const struct bdb_key *)(b8 + h->oem_area_0_size);
+}
+
+const struct bdb_sig *bdb_get_header_sig(const void *buf)
+{
+ const struct bdb_header *h = bdb_get_header(buf);
+ const uint8_t *b8 = bdb_get_oem_area_0(buf);
+
+ /* Header signature starts after signed data */
+ return (const struct bdb_sig *)(b8 + h->signed_size);
+}
+
+const struct bdb_data *bdb_get_data(const void *buf)
+{
+ const struct bdb_sig *s = bdb_get_header_sig(buf);
+ const uint8_t *b8 = (const uint8_t *)s;
+
+ /* Data follows header signature */
+ return (const struct bdb_data *)(b8 + s->struct_size);
+}
+
+const void *bdb_get_oem_area_1(const void *buf)
+{
+ const struct bdb_data *p = bdb_get_data(buf);
+ const uint8_t *b8 = (const uint8_t *)p;
+
+ /* OEM area 1 follows BDB data */
+ return b8 + p->struct_size;
+}
+
+const struct bdb_hash *bdb_get_hash(const void *buf, enum bdb_data_type type)
+{
+ const struct bdb_data *data = bdb_get_data(buf);
+ const uint8_t *b8 = bdb_get_oem_area_1(buf);
+ int i;
+
+ /* Hashes follow OEM area 0 */
+ b8 += data->oem_area_1_size;
+
+ /* Search for a matching hash */
+ for (i = 0; i < data->num_hashes; i++, b8 += data->hash_entry_size) {
+ const struct bdb_hash *h = (const struct bdb_hash *)b8;
+
+ if (h->type == type)
+ return h;
+ }
+
+ return NULL;
+}
+
+const struct bdb_sig *bdb_get_data_sig(const void *buf)
+{
+ const struct bdb_data *data = bdb_get_data(buf);
+ const uint8_t *b8 = (const uint8_t *)data;
+
+ /* Data signature starts after signed data */
+ return (const struct bdb_sig *)(b8 + data->signed_size);
+}
+
+/*****************************************************************************/
+
+int bdb_verify_sig(const struct bdb_key *key,
+ const struct bdb_sig *sig,
+ const uint8_t *digest)
+{
+ /* Key and signature algorithms must match */
+ if (key->sig_alg != sig->sig_alg)
+ return BDB_ERROR_SIG_ALG;
+
+ switch (key->sig_alg) {
+ case BDB_SIG_ALG_RSA4096:
+ if (bdb_rsa4096_verify(key->key_data, sig->sig_data, digest))
+ return BDB_ERROR_VERIFY_SIG;
+ break;
+ case BDB_SIG_ALG_ECSDSA521:
+ if (bdb_ecdsa521_verify(key->key_data, sig->sig_data, digest))
+ return BDB_ERROR_VERIFY_SIG;
+ break;
+ case BDB_SIG_ALG_RSA3072B:
+ if (bdb_rsa3072b_verify(key->key_data, sig->sig_data, digest))
+ return BDB_ERROR_VERIFY_SIG;
+ break;
+ default:
+ return BDB_ERROR_VERIFY_SIG;
+ }
+
+ return BDB_SUCCESS;
+}
+
+int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest)
+{
+ const uint8_t *end = (const uint8_t *)buf + size;
+ const struct bdb_header *h;
+ const struct bdb_key *bdbkey, *subkey;
+ const struct bdb_sig *sig;
+ const struct bdb_data *data;
+ const void *oem;
+ uint8_t digest[BDB_SHA256_DIGEST_SIZE];
+ int bdb_digest_mismatch;
+
+ /* Make sure buffer doesn't wrap around address space */
+ if (end < (const uint8_t *)buf)
+ return BDB_ERROR_BUF_SIZE;
+
+ /*
+ * Check header now that we've actually loaded it. We can't guarantee
+ * this is the same header which was checked before.
+ */
+ h = bdb_get_header(buf);
+ if (bdb_check_header(h, size))
+ return BDB_ERROR_HEADER;
+
+ /* Sanity-check BDB key */
+ bdbkey = bdb_get_bdbkey(buf);
+ if (bdb_check_key(bdbkey, end - (const uint8_t *)bdbkey))
+ return BDB_ERROR_BDBKEY;
+
+ /* Calculate BDB key digest and compare with expected */
+ if (bdb_sha256(digest, bdbkey, bdbkey->struct_size))
+ return BDB_ERROR_DIGEST;
+
+ bdb_digest_mismatch = memcmp(digest, bdb_key_digest, sizeof(digest));
+
+ /* Make sure OEM area 0 fits */
+ oem = bdb_get_oem_area_0(buf);
+ if (h->oem_area_0_size > end - (const uint8_t *)oem)
+ return BDB_ERROR_OEM_AREA_0;
+
+ /* Sanity-check subkey */
+ subkey = bdb_get_subkey(buf);
+ if (bdb_check_key(subkey, end - (const uint8_t *)subkey))
+ return BDB_ERROR_SUBKEY;
+
+ /* Make sure enough data was signed, and the signed data fits */
+ if (h->oem_area_0_size + subkey->struct_size > h->signed_size ||
+ h->signed_size > end - (const uint8_t *)oem)
+ return BDB_ERROR_BDB_SIGNED_SIZE;
+
+ /* Sanity-check header signature */
+ sig = bdb_get_header_sig(buf);
+ if (bdb_check_sig(sig, end - (const uint8_t *)sig))
+ return BDB_ERROR_HEADER_SIG;
+
+ /* Make sure it signed the right amount of data */
+ if (sig->signed_size != h->signed_size)
+ return BDB_ERROR_HEADER_SIG;
+
+ /* Calculate header digest and compare with expected signature */
+ if (bdb_sha256(digest, oem, h->signed_size))
+ return BDB_ERROR_DIGEST;
+ if (bdb_verify_sig(bdbkey, sig, digest))
+ return BDB_ERROR_HEADER_SIG;
+
+ /*
+ * Sanity-check data struct. This also checks that OEM area 1 and the
+ * hashes fit in the remaining buffer.
+ */
+ data = bdb_get_data(buf);
+ if (bdb_check_data(data, end - (const uint8_t *)data))
+ return BDB_ERROR_DATA;
+
+ /* Sanity-check data signature */
+ sig = bdb_get_data_sig(buf);
+ if (bdb_check_sig(sig, end - (const uint8_t *)sig))
+ return BDB_ERROR_DATA_SIG;
+ if (sig->signed_size != data->signed_size)
+ return BDB_ERROR_DATA_SIG;
+
+ /* Calculate data digest and compare with expected signature */
+ if (bdb_sha256(digest, data, data->signed_size))
+ return BDB_ERROR_DIGEST;
+ if (bdb_verify_sig(subkey, sig, digest))
+ return BDB_ERROR_DATA_SIG;
+
+ /* Return success or success-other-than-BDB-key-mismatch */
+ return bdb_digest_mismatch ? BDB_GOOD_OTHER_THAN_KEY : BDB_SUCCESS;
+}