summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2016-05-03 09:29:16 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-05-04 11:34:08 -0700
commitef4ae8953cdef17940c8b3b75e28cb919fdf34fa (patch)
treeb1d636b1ccd57f673c1290fdcf51814bcb7a199d
parent48b1a50b086e39332d2e1e51a73434e39c40b329 (diff)
downloadvboot-ef4ae8953cdef17940c8b3b75e28cb919fdf34fa.tar.gz
bdb: Copy files from bdb to firmware/bdb
Test files are copied to tests/ and the rest are copied to firmware/bdb/. BUG=chrome-os-partner:51908 BRANCH=tot TEST=make runtests Change-Id: I19f66c398e69037f00812a789854340a9690fef5 Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/342090 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--firmware/bdb/LICENSE27
-rw-r--r--firmware/bdb/README30
-rw-r--r--firmware/bdb/bdb.c398
-rw-r--r--firmware/bdb/bdb.h181
-rw-r--r--firmware/bdb/bdb_struct.h268
-rw-r--r--firmware/bdb/dump_rsa.c200
-rw-r--r--firmware/bdb/ecdsa.c17
-rw-r--r--firmware/bdb/host.c347
-rw-r--r--firmware/bdb/host.h171
-rw-r--r--firmware/bdb/rsa.c339
-rw-r--r--firmware/bdb/sha.c210
-rw-r--r--tests/bdb_test.c492
-rw-r--r--tests/testkeys/bdbkey.crt33
-rw-r--r--tests/testkeys/bdbkey.keybbin0 -> 1032 bytes
-rw-r--r--tests/testkeys/bdbkey.pem51
-rw-r--r--tests/testkeys/subkey.crt26
-rw-r--r--tests/testkeys/subkey.keybbin0 -> 776 bytes
-rw-r--r--tests/testkeys/subkey.pem39
18 files changed, 2829 insertions, 0 deletions
diff --git a/firmware/bdb/LICENSE b/firmware/bdb/LICENSE
new file mode 100644
index 00000000..d2514965
--- /dev/null
+++ b/firmware/bdb/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/firmware/bdb/README b/firmware/bdb/README
new file mode 100644
index 00000000..ec34961b
--- /dev/null
+++ b/firmware/bdb/README
@@ -0,0 +1,30 @@
+BDB library and utilities
+
+Building:
+---------
+The host-side library and utilities requires OpenSSL.
+
+Do 'make runtests' to ensure everything is working.
+
+Generating a BDB:
+-----------------
+Edit the options in bdb_create.c. Then 'make bdb'.
+
+In the next release, this will take a config file rather than
+requiring recompilation each time. Also, the BDB header and data will
+be signed in two separate steps, so that the private BDB key is not
+required each time.
+
+Revision History:
+-----------------
+v0.1.2 24-Nov-2015 Add support for RSA-3072B keys and signatures.
+ Add dump_rsa utility and 'make testkeys' to create
+ new keys.
+ Use a RSA-3072B (exponent 3) key for the subkey so
+ the exponent 3 code gets tested.
+
+v0.1.1 17-Nov-2015 Add support for ECDSA-521 data types. Note that
+ only the data types are supported; there is not a
+ C implementation for ECDSA.
+
+v0.1.0 15-Sep-2015 Initial version.
diff --git a/firmware/bdb/bdb.c b/firmware/bdb/bdb.c
new file mode 100644
index 00000000..d3327a51
--- /dev/null
+++ b/firmware/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;
+}
diff --git a/firmware/bdb/bdb.h b/firmware/bdb/bdb.h
new file mode 100644
index 00000000..177deeae
--- /dev/null
+++ b/firmware/bdb/bdb.h
@@ -0,0 +1,181 @@
+/* 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
+ */
+
+#ifndef VBOOT_REFERENCE_BDB_H_
+#define VBOOT_REFERENCE_BDB_H_
+
+#include <stdlib.h>
+#include "bdb_struct.h"
+
+/*****************************************************************************/
+/*
+Expected calling sequence:
+
+Load and check just the header
+bdb_check_header(buf, size);
+
+Load and verify the entire BDB
+bdb_verify(buf, size, bdb_key_hash, dev_mode_flag);
+
+Check RW subkey version. If normal boot from primary BDB, roll forward
+
+Check data version. If normal boot from primary BDB, roll forward
+*/
+
+/*****************************************************************************/
+/* Codes for functions returning numeric error codes */
+
+enum bdb_return_code {
+ /* Success */
+ BDB_SUCCESS = 0,
+
+ /* BDB key did not match hash, but other than that the BDB was
+ * fully verified. */
+ BDB_GOOD_OTHER_THAN_KEY = 1,
+
+ /* Other errors */
+ BDB_ERROR_UNKNOWN = 100,
+
+ /* Buffer size too small or wraps around */
+ BDB_ERROR_BUF_SIZE,
+
+ /* Bad fields in structures */
+ BDB_ERROR_STRUCT_MAGIC,
+ BDB_ERROR_STRUCT_VERSION,
+ BDB_ERROR_STRUCT_SIZE,
+ BDB_ERROR_SIGNED_SIZE,
+ BDB_ERROR_BDB_SIZE,
+ BDB_ERROR_OEM_AREA_SIZE,
+ BDB_ERROR_HASH_ENTRY_SIZE,
+ BDB_ERROR_HASH_ALG,
+ BDB_ERROR_SIG_ALG,
+ BDB_ERROR_DESCRIPTION,
+
+ /* Bad components of BDB in bdb_verify() */
+ BDB_ERROR_HEADER,
+ BDB_ERROR_BDBKEY,
+ BDB_ERROR_OEM_AREA_0,
+ BDB_ERROR_SUBKEY,
+ BDB_ERROR_BDB_SIGNED_SIZE,
+ BDB_ERROR_HEADER_SIG,
+ BDB_ERROR_DATA,
+ BDB_ERROR_DATA_SIG,
+
+ /* Other errors in bdb_verify() */
+ BDB_ERROR_DIGEST, /* Error calculating digest */
+ BDB_ERROR_VERIFY_SIG, /* Error verifying signature */
+};
+
+/*****************************************************************************/
+/* Functions */
+
+/**
+ * Sanity-check BDB structures.
+ *
+ * This checks for known version numbers, magic numbers, algorithms, etc. and
+ * ensures the sizes are consistent with those parameters.
+ *
+ * @param p Pointer to structure to check
+ * @param size Size of structure buffer
+ * @return 0 if success, non-zero error code if error.
+ */
+int bdb_check_header(const struct bdb_header *p, size_t size);
+int bdb_check_key(const struct bdb_key *p, size_t size);
+int bdb_check_sig(const struct bdb_sig *p, size_t size);
+int bdb_check_data(const struct bdb_data *p, size_t size);
+
+/**
+ * Verify the entire BDB
+ *
+ * @param buf Data to hash
+ * @param size Size of data in bytes
+ * @param bdb_key_digest Pointer to expected digest for BDB key.
+ * Must be BDB_SHA256_DIGEST_SIZE bytes long.
+ *
+ * @return 0 if success, non-zero error code if error. Note that error code
+ * BDB_GOOD_OTHER_THAN_KEY may still indicate an acceptable BDB if the Boot
+ * Verified fuse has not been set, or in developer mode.
+ */
+int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest);
+
+/**
+ * Functions to extract things from a verified BDB buffer.
+ *
+ * Do not call these externally until after bdb_verify()! These methods
+ * assume data structures have already been verified.
+ *
+ * @param buf Pointer to BDB buffer
+ * @param type Data type, for bdb_get_hash()
+ * @return A pointer to the requested data, or NULL if error / not present.
+ */
+const struct bdb_header *bdb_get_header(const void *buf);
+const struct bdb_key *bdb_get_bdbkey(const void *buf);
+const void *bdb_get_oem_area_0(const void *buf);
+const struct bdb_key *bdb_get_subkey(const void *buf);
+const struct bdb_sig *bdb_get_header_sig(const void *buf);
+const struct bdb_data *bdb_get_data(const void *buf);
+const void *bdb_get_oem_area_1(const void *buf);
+const struct bdb_hash *bdb_get_hash(const void *buf, enum bdb_data_type type);
+const struct bdb_sig *bdb_get_data_sig(const void *buf);
+
+/*****************************************************************************/
+/* Functions probably provided by the caller */
+
+/**
+ * Calculate a SHA-256 digest of a buffer.
+ *
+ * @param digest Pointer to the digest buffer. Must be
+ * BDB_SHA256_DIGEST_SIZE bytes long.
+ * @param buf Data to hash
+ * @param size Size of data in bytes
+ * @return 0 if success, non-zero error code if error.
+ */
+__attribute__((weak))
+int bdb_sha256(void *digest, const void *buf, size_t size);
+
+/**
+ * Verify a RSA-4096 signed digest
+ *
+ * @param key_data Key data to use (BDB_RSA4096_KEY_DATA_SIZE bytes)
+ * @param sig_data Signature to verify (BDB_RSA4096_SIG_SIZE bytes)
+ * @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
+ * @return 0 if success, non-zero error code if error.
+ */
+__attribute__((weak))
+int bdb_rsa4096_verify(const uint8_t *key_data,
+ const uint8_t *sig,
+ const uint8_t *digest);
+
+/**
+ * Verify a RSA-3072B signed digest
+ *
+ * @param key_data Key data to use (BDB_RSA3072B_KEY_DATA_SIZE bytes)
+ * @param sig_data Signature to verify (BDB_RSA3072B_SIG_SIZE bytes)
+ * @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
+ * @return 0 if success, non-zero error code if error.
+ */
+__attribute__((weak))
+int bdb_rsa3072b_verify(const uint8_t *key_data,
+ const uint8_t *sig,
+ const uint8_t *digest);
+
+/**
+ * Verify a ECDSA-521 signed digest
+ *
+ * @param key_data Key data to use (BDB_ECDSA521_KEY_DATA_SIZE bytes)
+ * @param sig_data Signature to verify (BDB_ECDSA521_SIG_SIZE bytes)
+ * @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
+ * @return 0 if success, non-zero error code if error.
+ */
+__attribute__((weak))
+int bdb_ecdsa521_verify(const uint8_t *key_data,
+ const uint8_t *sig,
+ const uint8_t *digest);
+
+/*****************************************************************************/
+
+#endif /* VBOOT_REFERENCE_BDB_H_ */
diff --git a/firmware/bdb/bdb_struct.h b/firmware/bdb/bdb_struct.h
new file mode 100644
index 00000000..f8d2b321
--- /dev/null
+++ b/firmware/bdb/bdb_struct.h
@@ -0,0 +1,268 @@
+/* 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 structures
+ */
+
+#ifndef VBOOT_REFERENCE_BDB_STRUCT_H_
+#define VBOOT_REFERENCE_BDB_STRUCT_H_
+
+#include <stdint.h>
+
+/* Size of SHA256 digest in bytes */
+#define BDB_SHA256_DIGEST_SIZE 32
+
+/* Size of RSA4096 key data in bytes */
+#define BDB_RSA4096_KEY_DATA_SIZE 1032
+
+/* Size of RSA4096 signature in bytes */
+#define BDB_RSA4096_SIG_SIZE 512
+
+/* Size of ECDSA521 key data in bytes = ceil(521/8) * 2 */
+#define BDB_ECDSA521_KEY_DATA_SIZE 132
+
+/* Size of ECDSA521 signature in bytes = ceil(521/8) * 2 */
+#define BDB_ECDSA521_SIG_SIZE 132
+
+/* Size of RSA3072B key data in bytes */
+#define BDB_RSA3072B_KEY_DATA_SIZE 776
+
+/* Size of RSA3072B signature in bytes */
+#define BDB_RSA3072B_SIG_SIZE 384
+
+/*****************************************************************************/
+/* Header for BDB */
+
+/* Magic number for bdb_header.struct_magic */
+#define BDB_HEADER_MAGIC 0x30426442
+
+/* Current version of bdb_header struct */
+#define BDB_HEADER_VERSION_MAJOR 1
+#define BDB_HEADER_VERSION_MINOR 0
+
+/* Expected size of bdb_header struct in bytes */
+#define BDB_HEADER_EXPECTED_SIZE 32
+
+struct bdb_header {
+ /* Magic number to identify struct = BDB_HEADER_MAGIC. */
+ uint32_t struct_magic;
+
+ /* Structure version = BDB_HEADER_VERSION{MAJOR,MINOR} */
+ uint8_t struct_major_version;
+ uint8_t struct_minor_version;
+
+ /* Size of structure in bytes */
+ uint16_t struct_size;
+
+ /* Recommended address in SP SRAM to load BDB. Set to -1 to use
+ * default address. */
+ uint64_t bdb_load_address;
+
+ /* Size of the entire BDB in bytes */
+ uint32_t bdb_size;
+
+ /* Number of bytes following the BDB key which are signed by the BDB
+ * header signature. */
+ uint32_t signed_size;
+
+ /* Size of OEM area 0 in bytes, or 0 if not present */
+ uint32_t oem_area_0_size;
+
+ /* Reserved; set 0 */
+ uint8_t reserved0[8];
+} __attribute__((packed));
+
+/*****************************************************************************/
+/* Public key structure for BDB */
+
+/* Magic number for bdb_key.struct_magic */
+#define BDB_KEY_MAGIC 0x73334256
+
+/* Current version of bdb_key struct */
+#define BDB_KEY_VERSION_MAJOR 1
+#define BDB_KEY_VERSION_MINOR 0
+
+/* Supported hash algorithms */
+enum bdb_hash_alg {
+ BDB_HASH_ALG_INVALID = 0, /* Not used; invalid */
+ BDB_HASH_ALG_SHA256 = 2, /* SHA-256 */
+};
+
+/* Supported signature algorithms */
+enum bdb_sig_alg {
+ BDB_SIG_ALG_INVALID = 0, /* Not used; invalid */
+ BDB_SIG_ALG_RSA4096 = 3, /* RSA-4096, exponent 65537 */
+ BDB_SIG_ALG_ECSDSA521 = 5, /* ECDSA-521 */
+ BDB_SIG_ALG_RSA3072B = 7, /* RSA_3072, exponent 3 */
+};
+
+/*
+ * Expected size of bdb_key struct in bytes, not counting variable-length key
+ * data at end.
+ */
+#define BDB_KEY_EXPECTED_SIZE 80
+
+struct bdb_key {
+ /* Magic number to identify struct = BDB_KEY_MAGIC. */
+ uint32_t struct_magic;
+
+ /* Structure version = BDB_KEY_VERSION{MAJOR,MINOR} */
+ uint8_t struct_major_version;
+ uint8_t struct_minor_version;
+
+ /* Size of structure in bytes, including variable-length key data */
+ uint16_t struct_size;
+
+ /* Hash algorithm (enum bdb_hash_alg) */
+ uint8_t hash_alg;
+
+ /* Signature algorithm (enum bdb_sig_alg) */
+ uint8_t sig_alg;
+
+ /* Reserved; set 0 */
+ uint8_t reserved0[2];
+
+ /* Key version */
+ uint32_t key_version;
+
+ /* Description; null-terminated ASCII */
+ char description[128];
+
+ /*
+ * Key data. Variable-length; size is struct_size -
+ * offset_of(bdb_key, key_data).
+ */
+ uint8_t key_data[0];
+} __attribute__((packed));
+
+/*****************************************************************************/
+/* Signature structure for BDB */
+
+/* Magic number for bdb_sig.struct_magic */
+#define BDB_SIG_MAGIC 0x6b334256
+
+/* Current version of bdb_sig struct */
+#define BDB_SIG_VERSION_MAJOR 1
+#define BDB_SIG_VERSION_MINOR 0
+
+struct bdb_sig {
+ /* Magic number to identify struct = BDB_SIG_MAGIC. */
+ uint32_t struct_magic;
+
+ /* Structure version = BDB_SIG_VERSION{MAJOR,MINOR} */
+ uint8_t struct_major_version;
+ uint8_t struct_minor_version;
+
+ /* Size of structure in bytes, including variable-length signature
+ * data. */
+ uint16_t struct_size;
+
+ /* Hash algorithm used for this signature (enum bdb_hash_alg) */
+ uint8_t hash_alg;
+
+ /* Signature algorithm (enum bdb_sig_alg) */
+ uint8_t sig_alg;
+
+ /* Reserved; set 0 */
+ uint8_t reserved0[2];
+
+ /* Number of bytes of data signed by this signature */
+ uint32_t signed_size;
+
+ /* Description; null-terminated ASCII */
+ char description[128];
+
+ /* Signature data. Variable-length; size is struct_size -
+ * offset_of(bdb_sig, sig_data). */
+ uint8_t sig_data[0];
+} __attribute__((packed));
+
+/*****************************************************************************/
+/* Data structure for BDB */
+
+/* Magic number for bdb_data.struct_magic */
+#define BDB_DATA_MAGIC 0x31426442
+
+/* Current version of bdb_sig struct */
+#define BDB_DATA_VERSION_MAJOR 1
+#define BDB_DATA_VERSION_MINOR 0
+
+struct bdb_data {
+ /* Magic number to identify struct = BDB_DATA_MAGIC. */
+ uint32_t struct_magic;
+
+ /* Structure version = BDB_DATA_VERSION{MAJOR,MINOR} */
+ uint8_t struct_major_version;
+ uint8_t struct_minor_version;
+
+ /* Size of structure in bytes, NOT including hashes which follow. */
+ uint16_t struct_size;
+
+ /* Version of data (RW firmware) contained */
+ uint32_t data_version;
+
+ /* Size of OEM area 1 in bytes, or 0 if not present */
+ uint32_t oem_area_1_size;
+
+ /* Number of hashes which follow */
+ uint8_t num_hashes;
+
+ /* Size of each hash entry in bytes */
+ uint8_t hash_entry_size;
+
+ /* Reserved; set 0 */
+ uint8_t reserved0[2];
+
+ /* Number of bytes of data signed by the subkey, including this
+ * header */
+ uint32_t signed_size;
+
+ /* Reserved; set 0 */
+ uint8_t reserved1[8];
+
+ /* Description; null-terminated ASCII */
+ char description[128];
+} __attribute__((packed));
+
+/* Type of data for bdb_hash.type */
+enum bdb_data_type {
+ /* Types of data for boot descriptor blocks */
+ BDB_DATA_SP_RW = 1, /* SP-RW firmware */
+ BDB_DATA_AP_RW = 2, /* AP-RW firmware */
+ BDB_DATA_MCU = 3, /* MCU firmware */
+
+ /* Types of data for kernel descriptor blocks */
+ BDB_DATA_KERNEL = 128, /* Kernel */
+ BDB_DATA_CMD_LINE = 129, /* Command line */
+ BDB_DATA_HEADER16 = 130, /* 16-bit vmlinuz header */
+};
+
+/* Hash entries which follow the structure */
+struct bdb_hash {
+ /* Offset of data from start of partition */
+ uint64_t offset;
+
+ /* Size of data in bytes */
+ uint32_t size;
+
+ /* Partition number containing data */
+ uint8_t partition;
+
+ /* Type of data; enum bdb_data_type */
+ uint8_t type;
+
+ /* Reserved; set 0 */
+ uint8_t reserved0[2];
+
+ /* Address in RAM to load data. -1 means use default. */
+ uint64_t load_address;
+
+ /* SHA-256 hash digest */
+ uint8_t digest[BDB_SHA256_DIGEST_SIZE];
+} __attribute__((packed));
+
+/*****************************************************************************/
+
+#endif /* VBOOT_REFERENCE_BDB_STRUCT_H_ */
+
diff --git a/firmware/bdb/dump_rsa.c b/firmware/bdb/dump_rsa.c
new file mode 100644
index 00000000..c40f803a
--- /dev/null
+++ b/firmware/bdb/dump_rsa.c
@@ -0,0 +1,200 @@
+/* Copyright (c) 2010 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.
+ */
+
+/*
+ * C port of DumpPublicKey.java from the Android Open source project with
+ * support for additional RSA key sizes. (platform/system/core,git/libmincrypt
+ * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
+ */
+
+#include <openssl/pem.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Command line tool to extract RSA public keys from X.509 certificates and
+ * output a pre-processed version of keys for use by RSA verification routines.
+ */
+
+int check(RSA *key)
+{
+ int public_exponent = BN_get_word(key->e);
+ int modulus = BN_num_bits(key->n);
+
+ if (public_exponent != 65537 && public_exponent != 3) {
+ fprintf(stderr, "WARNING: Non-standard public exponent %d.\n",
+ public_exponent);
+ }
+
+ if (modulus != 1024 && modulus != 2048 && modulus != 3072 &&
+ modulus != 4096 && modulus != 8192) {
+ fprintf(stderr, "WARNING: Non-standard modulus length = %d.\n",
+ modulus);
+ }
+ return 1;
+}
+
+/**
+ * Pre-processes and outputs RSA public key to standard output.
+ */
+void output(RSA *key)
+{
+ BIGNUM *N;
+ BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
+ BIGNUM *B = NULL;
+ BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL;
+ BIGNUM *NnumBits = NULL;
+ BIGNUM *n = NULL, *rr = NULL;
+ BN_CTX *bn_ctx = BN_CTX_new();
+ uint32_t n0invout;
+ int nwords, i;
+
+ N = key->n;
+ /* Output size of RSA key in 32-bit words */
+ nwords = BN_num_bits(N) / 32;
+ if (-1 == write(1, &nwords, sizeof(nwords)))
+ goto failure;
+
+ /* Initialize BIGNUMs */
+ Big1 = BN_new();
+ Big2 = BN_new();
+ Big32 = BN_new();
+ BigMinus1 = BN_new();
+ N0inv= BN_new();
+ R = BN_new();
+ RR = BN_new();
+ RRTemp = BN_new();
+ NnumBits = BN_new();
+ n = BN_new();
+ rr = BN_new();
+
+ BN_set_word(Big1, 1L);
+ BN_set_word(Big2, 2L);
+ BN_set_word(Big32, 32L);
+ BN_sub(BigMinus1, Big1, Big2);
+
+ B = BN_new();
+ BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
+
+ /* Calculate and output N0inv = -1 / N[0] mod 2^32 */
+ BN_mod_inverse(N0inv, N, B, bn_ctx);
+ BN_sub(N0inv, B, N0inv);
+ n0invout = BN_get_word(N0inv);
+ if (-1 == write(1, &n0invout, sizeof(n0invout)))
+ goto failure;
+
+ /* Calculate R = 2^(# of key bits) */
+ BN_set_word(NnumBits, BN_num_bits(N));
+ BN_exp(R, Big2, NnumBits, bn_ctx);
+
+ /* Calculate RR = R^2 mod N */
+ BN_copy(RR, R);
+ BN_mul(RRTemp, RR, R, bn_ctx);
+ BN_mod(RR, RRTemp, N, bn_ctx);
+
+ /* Write out modulus as little endian array of integers. */
+ for (i = 0; i < nwords; ++i) {
+ uint32_t nout;
+
+ BN_mod(n, N, B, bn_ctx); /* n = N mod B */
+ nout = BN_get_word(n);
+ if (-1 == write(1, &nout, sizeof(nout)))
+ goto failure;
+
+ BN_rshift(N, N, 32); /* N = N/B */
+ }
+
+ /* Write R^2 as little endian array of integers. */
+ for (i = 0; i < nwords; ++i) {
+ uint32_t rrout;
+
+ BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
+ rrout = BN_get_word(rr);
+ if (-1 == write(1, &rrout, sizeof(rrout)))
+ goto failure;
+
+ BN_rshift(RR, RR, 32); /* RR = RR/B */
+ }
+
+ failure:
+ /* Free BIGNUMs. */
+ BN_free(Big1);
+ BN_free(Big2);
+ BN_free(Big32);
+ BN_free(BigMinus1);
+ BN_free(N0inv);
+ BN_free(R);
+ BN_free(RRTemp);
+ BN_free(NnumBits);
+ BN_free(n);
+ BN_free(rr);
+
+}
+
+int main(int argc, char* argv[]) {
+ int cert_mode = 0;
+ FILE* fp;
+ X509* cert = NULL;
+ RSA* pubkey = NULL;
+ EVP_PKEY* key;
+ char *progname;
+
+ if (argc != 3 ||
+ (strcmp(argv[1], "-cert") && strcmp(argv[1], "-pub"))) {
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+ fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", progname);
+ return -1;
+ }
+
+ if (!strcmp(argv[1], "-cert"))
+ cert_mode = 1;
+
+ fp = fopen(argv[2], "r");
+
+ if (!fp) {
+ fprintf(stderr, "Couldn't open file %s!\n", argv[2]);
+ return -1;
+ }
+
+ if (cert_mode) {
+ /* Read the certificate */
+ if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
+ fprintf(stderr, "Couldn't read certificate.\n");
+ goto fail;
+ }
+
+ /* Get the public key from the certificate. */
+ key = X509_get_pubkey(cert);
+
+ /* Convert to a RSA_style key. */
+ if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
+ fprintf(stderr, "Couldn't convert to RSA style key.\n");
+ goto fail;
+ }
+ } else {
+ /* Read the pubkey in .PEM format. */
+ if (!(pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL))) {
+ fprintf(stderr, "Couldn't read public key file.\n");
+ goto fail;
+ }
+ }
+
+ if (check(pubkey)) {
+ output(pubkey);
+ }
+
+ fail:
+ X509_free(cert);
+ RSA_free(pubkey);
+ fclose(fp);
+
+ return 0;
+}
diff --git a/firmware/bdb/ecdsa.c b/firmware/bdb/ecdsa.c
new file mode 100644
index 00000000..f4d17287
--- /dev/null
+++ b/firmware/bdb/ecdsa.c
@@ -0,0 +1,17 @@
+/* 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 ECDSA stub
+ */
+
+#include <string.h>
+#include "bdb.h"
+
+int bdb_ecdsa521_verify(const uint8_t *key_data,
+ const uint8_t *sig,
+ const uint8_t *digest)
+{
+ /* This is just a stub */
+ return BDB_ERROR_DIGEST;
+}
diff --git a/firmware/bdb/host.c b/firmware/bdb/host.c
new file mode 100644
index 00000000..24d94652
--- /dev/null
+++ b/firmware/bdb/host.c
@@ -0,0 +1,347 @@
+/* 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.
+ *
+ * Host functions for signing
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bdb.h"
+#include "host.h"
+
+char *strzcpy(char *dest, const char *src, size_t size)
+{
+ strncpy(dest, src, size);
+ dest[size - 1] = 0;
+ return dest;
+}
+
+uint8_t *read_file(const char *filename, uint32_t *size_ptr)
+{
+ FILE *f;
+ uint8_t *buf;
+ long size;
+
+ *size_ptr = 0;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ fprintf(stderr, "Unable to open file %s\n", filename);
+ return NULL;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ rewind(f);
+
+ if (size < 0 || size > UINT32_MAX) {
+ fclose(f);
+ return NULL;
+ }
+
+ buf = malloc(size);
+ if (!buf) {
+ fclose(f);
+ return NULL;
+ }
+
+ if (1 != fread(buf, size, 1, f)) {
+ fprintf(stderr, "Unable to read file %s\n", filename);
+ fclose(f);
+ free(buf);
+ return NULL;
+ }
+
+ fclose(f);
+
+ *size_ptr = size;
+ return buf;
+}
+
+int write_file(const char *filename, const void *buf, uint32_t size)
+{
+ FILE *f = fopen(filename, "wb");
+
+ if (!f) {
+ fprintf(stderr, "Unable to open file %s\n", filename);
+ return 1;
+ }
+
+ if (1 != fwrite(buf, size, 1, f)) {
+ fprintf(stderr, "Unable to write to file %s\n", filename);
+ fclose(f);
+ unlink(filename); /* Delete any partial file */
+ return 1;
+ }
+
+ fclose(f);
+ return 0;
+}
+
+struct rsa_st *read_pem(const char *filename)
+{
+ struct rsa_st *pem;
+ FILE *f;
+
+ /* Read private key */
+ f = fopen(filename, "rb");
+ if (!f) {
+ fprintf(stderr, "%s: unable to read key from %s\n",
+ __func__, filename);
+ return NULL;
+ }
+
+ pem = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
+ fclose(f);
+
+ return pem;
+}
+
+struct bdb_key *bdb_create_key(const char *filename,
+ uint32_t key_version,
+ const char *desc)
+{
+ uint32_t sig_alg;
+ size_t key_size = sizeof(struct bdb_key);
+ struct bdb_key *k;
+ uint8_t *kdata;
+ uint32_t kdata_size = 0;
+
+ /*
+ * Read key data. Somewhat lame assumption that we can determine the
+ * signature algorithm from the key size, but it's true right now.
+ */
+ kdata = read_file(filename, &kdata_size);
+ if (kdata_size == BDB_RSA4096_KEY_DATA_SIZE) {
+ sig_alg = BDB_SIG_ALG_RSA4096;
+ } else if (kdata_size == BDB_RSA3072B_KEY_DATA_SIZE) {
+ sig_alg = BDB_SIG_ALG_RSA3072B;
+ } else {
+ fprintf(stderr, "%s: bad key size from %s\n",
+ __func__, filename);
+ return NULL;
+ }
+ key_size += kdata_size;
+
+ /* Allocate buffer */
+ k = (struct bdb_key *)calloc(key_size, 1);
+ if (!k) {
+ free(kdata);
+ return NULL;
+ }
+
+ k->struct_magic = BDB_KEY_MAGIC;
+ k->struct_major_version = BDB_KEY_VERSION_MAJOR;
+ k->struct_minor_version = BDB_KEY_VERSION_MINOR;
+ k->struct_size = key_size;
+ k->hash_alg = BDB_HASH_ALG_SHA256;
+ k->sig_alg = sig_alg;
+ k->key_version = key_version;
+
+ /* Copy description, if any */
+ if (desc)
+ strzcpy(k->description, desc, sizeof(k->description));
+
+ /* Copy key data */
+ memcpy(k->key_data, kdata, kdata_size);
+ free(kdata);
+
+ return k;
+}
+
+struct bdb_sig *bdb_create_sig(const void *data,
+ size_t size,
+ struct rsa_st *key,
+ uint32_t sig_alg,
+ const char *desc)
+{
+ static const uint8_t info[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20
+ };
+
+ size_t sig_size = sizeof(struct bdb_sig);
+ uint8_t digest[sizeof(info) + BDB_SHA256_DIGEST_SIZE];
+ struct bdb_sig *sig;
+
+ if (size >= UINT32_MAX)
+ return NULL;
+
+ switch(sig_alg) {
+ case BDB_SIG_ALG_RSA4096:
+ sig_size += BDB_RSA4096_SIG_SIZE;
+ break;
+ case BDB_SIG_ALG_RSA3072B:
+ sig_size += BDB_RSA3072B_SIG_SIZE;
+ break;
+ default:
+ fprintf(stderr, "%s: bad signature algorithm %d\n",
+ __func__, sig_alg);
+ return NULL;
+ }
+
+ /* Allocate buffer */
+ sig = (struct bdb_sig *)calloc(sig_size, 1);
+ if (!sig)
+ return NULL;
+
+ sig->struct_magic = BDB_SIG_MAGIC;
+ sig->struct_major_version = BDB_SIG_VERSION_MAJOR;
+ sig->struct_minor_version = BDB_SIG_VERSION_MINOR;
+ sig->struct_size = sig_size;
+ sig->hash_alg = BDB_HASH_ALG_SHA256;
+ sig->sig_alg = sig_alg;
+ sig->signed_size = size;
+
+ /* Copy description, if any */
+ if (desc)
+ strzcpy(sig->description, desc, sizeof(sig->description));
+
+ /* Calculate info-padded digest */
+ memcpy(digest, info, sizeof(info));
+ if (bdb_sha256(digest + sizeof(info), data, size)) {
+ free(sig);
+ return NULL;
+ }
+
+ /* RSA-encrypt the signature */
+ if (RSA_private_encrypt(sizeof(digest),
+ digest,
+ sig->sig_data,
+ key,
+ RSA_PKCS1_PADDING) == -1) {
+ free(sig);
+ return NULL;
+ }
+ return sig;
+}
+
+struct bdb_header *bdb_create(struct bdb_create_params *p)
+{
+ size_t bdb_size = 0;
+ size_t sig_size = sizeof(struct bdb_sig) + BDB_RSA4096_SIG_SIZE;
+ size_t hashes_size = sizeof(struct bdb_hash) * p->num_hashes;
+ uint8_t *buf, *bnext;
+ struct bdb_header *h;
+ struct bdb_sig *sig;
+ struct bdb_data *data;
+ const void *oem;
+
+ /* We can do some checks before we even allocate the buffer */
+
+ /* Make sure OEM sizes are aligned */
+ if ((p->oem_area_0_size & 3) || (p->oem_area_1_size & 3)) {
+ fprintf(stderr, "%s: OEM areas not 32-bit aligned\n",
+ __func__);
+ return NULL;
+ }
+
+ /* Hash count must fit in uint8_t */
+ if (p->num_hashes > 255) {
+ fprintf(stderr, "%s: too many hashes\n", __func__);
+ return NULL;
+ }
+
+ /* Calculate BDB size */
+ bdb_size = sizeof(struct bdb_header);
+ bdb_size += p->bdbkey->struct_size;
+ bdb_size += p->oem_area_0_size;
+ bdb_size += p->subkey->struct_size;
+ bdb_size += sig_size;
+ bdb_size += sizeof(struct bdb_data);
+ bdb_size += p->oem_area_1_size;
+ bdb_size += sizeof(struct bdb_hash) * p->num_hashes;
+ bdb_size += sig_size;
+
+ /* Make sure it fits */
+ if (bdb_size > UINT32_MAX) {
+ fprintf(stderr, "%s: BDB size > UINT32_MAX\n", __func__);
+ return NULL;
+ }
+
+ /* Allocate a buffer */
+ bnext = buf = calloc(bdb_size, 1);
+ if (!buf) {
+ fprintf(stderr, "%s: can't allocate buffer\n", __func__);
+ return NULL;
+ }
+
+ /* Fill in the header */
+ h = (struct bdb_header *)bnext;
+ h->struct_magic = BDB_HEADER_MAGIC;
+ h->struct_major_version = BDB_HEADER_VERSION_MAJOR;
+ h->struct_minor_version = BDB_HEADER_VERSION_MINOR;
+ h->struct_size = sizeof(*h);
+ h->bdb_load_address = p->bdb_load_address;
+ h->bdb_size = bdb_size;
+ h->signed_size = p->oem_area_0_size + p->subkey->struct_size;
+ h->oem_area_0_size = p->oem_area_0_size;
+ bnext += h->struct_size;
+
+ /* Copy BDB key */
+ memcpy(bnext, p->bdbkey, p->bdbkey->struct_size);
+ bnext += p->bdbkey->struct_size;
+
+ /* Copy OEM area 0 */
+ oem = bnext;
+ if (p->oem_area_0_size) {
+ memcpy(bnext, p->oem_area_0, p->oem_area_0_size);
+ bnext += p->oem_area_0_size;
+ }
+
+ /* Copy subkey */
+ memcpy(bnext, p->subkey, p->subkey->struct_size);
+ bnext += p->subkey->struct_size;
+
+ /*
+ * Create header signature using private BDB key.
+ *
+ * TODO: create the header signature in a totally separate step. That
+ * way, the private BDB key is not required each time a BDB is created.
+ */
+ sig = bdb_create_sig(oem, h->signed_size, p->private_bdbkey,
+ p->bdbkey->sig_alg, p->header_sig_description);
+ memcpy(bnext, sig, sig->struct_size);
+ bnext += sig->struct_size;
+
+ /* Fill in the data */
+ data = (struct bdb_data *)bnext;
+ data->struct_magic = BDB_DATA_MAGIC;
+ data->struct_major_version = BDB_DATA_VERSION_MAJOR;
+ data->struct_minor_version = BDB_DATA_VERSION_MINOR;
+ data->struct_size = sizeof(struct bdb_data);
+ data->data_version = p->data_version;
+ data->oem_area_1_size = p->oem_area_1_size;
+ data->num_hashes = p->num_hashes;
+ data->hash_entry_size = sizeof(struct bdb_hash);
+ data->signed_size = data->struct_size + data->oem_area_1_size +
+ hashes_size;
+ if (p->data_description) {
+ strzcpy(data->description, p->data_description,
+ sizeof(data->description));
+ }
+ bnext += data->struct_size;
+
+ /* Copy OEM area 1 */
+ oem = bnext;
+ if (p->oem_area_1_size) {
+ memcpy(bnext, p->oem_area_1, p->oem_area_1_size);
+ bnext += p->oem_area_1_size;
+ }
+
+ /* Copy hashes */
+ memcpy(bnext, p->hash, hashes_size);
+ bnext += hashes_size;
+
+ /* Create data signature using private subkey */
+ sig = bdb_create_sig(data, data->signed_size, p->private_subkey,
+ p->subkey->sig_alg, p->data_sig_description);
+ memcpy(bnext, sig, sig->struct_size);
+
+ /* Return the BDB */
+ return h;
+}
diff --git a/firmware/bdb/host.h b/firmware/bdb/host.h
new file mode 100644
index 00000000..9334680b
--- /dev/null
+++ b/firmware/bdb/host.h
@@ -0,0 +1,171 @@
+/* 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 host functions
+ */
+
+#ifndef VBOOT_REFERENCE_BDB_HOST_H_
+#define VBOOT_REFERENCE_BDB_HOST_H_
+
+#include <stdlib.h>
+#include <openssl/pem.h>
+#include "bdb_struct.h"
+
+/*****************************************************************************/
+/*
+Expected calling sequence:
+
+Load and check just the header
+bdb_check_header(buf, size);
+
+Load and verify the entire BDB
+bdb_verify(buf, size, bdb_key_hash, dev_mode_flag);
+
+ bdb_check_header() again - paranoia against bad storage devices
+
+ bdb_check_key() on BDB key
+ bdb_sha256() on BDB key
+ Compare with appropriate root key hash
+ If dev_mode_flag(), mismatch is not fatal
+
+ bdb_check_sig() on BDB header sig
+ bdb_sha256() on OEM area 1, RW subkey
+ bdb_rsa_verify() on digest using BDB key
+
+ bdb_check_key() on RW subkey
+
+ bdb_check_data() on RW data
+ bdb_check_sig() on data sig
+ bdb_sha256() on data, OEM area 1, hashes
+ bdb_rsa_verify() on digest using RW subkey
+
+Check RW subkey version. If normal boot from primary BDB, roll forward
+Check data version. If normal boot from primary BDB, roll forward
+*/
+
+/*****************************************************************************/
+/* Codes for functions returning numeric error codes */
+
+enum bdb_host_return_code {
+ /* All/any of bdb_return_code, and the following... */
+
+ /* Other errors */
+ BDB_ERROR_HOST = 200,
+};
+
+/*****************************************************************************/
+/* Functions */
+
+/**
+ * Like strncpy, but guaranteeing null termination
+ */
+char *strzcpy(char *dest, const char *src, size_t size);
+
+/**
+ * Read a file.
+ *
+ * Caller must free() the returned buffer.
+ *
+ * @param filename Path to file
+ * @param size_ptr Destination for size of buffer
+ * @return A newly allocated buffer containing the data, or NULL if error.
+ */
+uint8_t *read_file(const char *filename, uint32_t *size_ptr);
+
+/**
+ * Write a file.
+ *
+ * @param buf Data to write
+ * @param size Size of data in bytes
+ * @return 0 if success, non-zero error code if error.
+ */
+int write_file(const char *filename, const void *buf, uint32_t size);
+
+/**
+ * Read a PEM from a file.
+ *
+ * Caller must free the PEM with RSA_free().
+ *
+ * @param filename Path to file
+ * @return A newly allocated PEM object, or NULL if error.
+ */
+struct rsa_st *read_pem(const char *filename);
+
+/**
+ * Create a BDB public key object.
+ *
+ * Caller must free() the returned key.
+ *
+ * @param filename Path to file containing public key (.keyb)
+ * @param key_version Version for key
+ * @param desc Description. Optional; may be NULL.
+ * @return A newly allocated public key, or NULL if error.
+ */
+struct bdb_key *bdb_create_key(const char *filename,
+ uint32_t key_version,
+ const char *desc);
+
+/**
+ * Create a BDB signature object.
+ *
+ * Caller must free() the returned signature.
+ *
+ * @param data Data to sign
+ * @param size Size of data in bytes
+ * @param key PEM key
+ * @param sig_alg Signature algorithm
+ * @param desc Description. Optional; may be NULL.
+ * @return A newly allocated signature, or NULL if error.
+ */
+struct bdb_sig *bdb_create_sig(const void *data,
+ size_t size,
+ struct rsa_st *key,
+ uint32_t sig_alg,
+ const char *desc);
+
+struct bdb_create_params
+{
+ /* Load address */
+ uint64_t bdb_load_address;
+
+ /* OEM areas. Size may be 0, in which case the buffer is ignored */
+ uint8_t *oem_area_0;
+ uint32_t oem_area_0_size;
+ uint8_t *oem_area_1;
+ uint32_t oem_area_1_size;
+
+ /* Public BDB key and subkey */
+ struct bdb_key *bdbkey;
+ struct bdb_key *subkey;
+
+ /* Private BDB key and subkey */
+ struct rsa_st *private_bdbkey;
+ struct rsa_st *private_subkey;
+
+ /* Descriptions for header and data signatures */
+ char *header_sig_description;
+ char *data_sig_description;
+
+ /* Data description and version */
+ char *data_description;
+ uint32_t data_version;
+
+ /* Data hashes and count */
+ struct bdb_hash *hash;
+ uint32_t num_hashes;
+};
+
+/**
+ * Create a new BDB
+ *
+ * Caller must free() returned object.
+ *
+ * @param p Creation parameters
+ * @return A newly allocated BDB, or NULL if error.
+ */
+struct bdb_header *bdb_create(struct bdb_create_params *p);
+
+/*****************************************************************************/
+
+#endif /* VBOOT_REFERENCE_BDB_HOST_H_ */
diff --git a/firmware/bdb/rsa.c b/firmware/bdb/rsa.c
new file mode 100644
index 00000000..932c9733
--- /dev/null
+++ b/firmware/bdb/rsa.c
@@ -0,0 +1,339 @@
+/* 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 RSA
+ */
+
+#include <string.h>
+#include "bdb.h"
+
+/* Public key structure in RAM */
+struct public_key {
+ uint32_t arrsize; /* Size of n[] and rr[] arrays in elements */
+ uint32_t n0inv; /* -1 / n[0] mod 2^32 */
+ const uint32_t *n; /* Modulus as little endian array */
+ const uint32_t *rr; /* R^2 as little endian array */
+};
+
+/**
+ * a[] -= mod
+ */
+static void subM(const struct public_key *key, uint32_t *a)
+{
+ int64_t A = 0;
+ uint32_t i;
+ for (i = 0; i < key->arrsize; ++i) {
+ A += (uint64_t)a[i] - key->n[i];
+ a[i] = (uint32_t)A;
+ A >>= 32;
+ }
+}
+
+/**
+ * Return a[] >= mod
+ */
+int vb2_mont_ge(const struct public_key *key, uint32_t *a)
+{
+ uint32_t i;
+ for (i = key->arrsize; i;) {
+ --i;
+ if (a[i] < key->n[i])
+ return 0;
+ if (a[i] > key->n[i])
+ return 1;
+ }
+ return 1; /* equal */
+}
+
+/**
+ * Montgomery c[] += a * b[] / R % mod
+ */
+static void montMulAdd(const struct public_key *key,
+ uint32_t *c,
+ const uint32_t a,
+ const uint32_t *b)
+{
+ uint64_t A = (uint64_t)a * b[0] + c[0];
+ uint32_t d0 = (uint32_t)A * key->n0inv;
+ uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+ uint32_t i;
+
+ for (i = 1; i < key->arrsize; ++i) {
+ A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+ B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+ c[i - 1] = (uint32_t)B;
+ }
+
+ A = (A >> 32) + (B >> 32);
+
+ c[i - 1] = (uint32_t)A;
+
+ if (A >> 32) {
+ subM(key, c);
+ }
+}
+
+/**
+ * Montgomery c[] = a[] * b[] / R % mod
+ */
+static void montMul(const struct public_key *key,
+ uint32_t *c,
+ const uint32_t *a,
+ const uint32_t *b)
+{
+ uint32_t i;
+ for (i = 0; i < key->arrsize; ++i) {
+ c[i] = 0;
+ }
+ for (i = 0; i < key->arrsize; ++i) {
+ montMulAdd(key, c, a[i], b);
+ }
+}
+
+int vb2_safe_memcmp(const void *s1, const void *s2, size_t size)
+{
+ const unsigned char *us1 = s1;
+ const unsigned char *us2 = s2;
+ int result = 0;
+
+ if (0 == size)
+ return 0;
+
+ /*
+ * Code snippet without data-dependent branch due to Nate Lawson
+ * (nate@root.org) of Root Labs.
+ */
+ while (size--)
+ result |= *us1++ ^ *us2++;
+
+ return result != 0;
+}
+
+/*
+ * PKCS 1.5 padding (from the RSA PKCS#1 v2.1 standard)
+ *
+ * Depending on the RSA key size and hash function, the padding is calculated
+ * as follows:
+ *
+ * 0x00 || 0x01 || PS || 0x00 || T
+ *
+ * T: DER Encoded DigestInfo value which depends on the hash function used.
+ *
+ * SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.
+ *
+ * Length(T) = 51 octets for SHA-256
+ *
+ * PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF
+ */
+static const uint8_t sha256_tail[] = {
+ 0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,
+ 0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,
+ 0x05,0x00,0x04,0x20
+};
+
+int vb2_check_padding(const uint8_t *sig, const struct public_key *key,
+ uint32_t pad_size)
+{
+ /* Determine padding to use depending on the signature type */
+ const uint32_t tail_size = sizeof(sha256_tail);
+ int result = 0;
+ int i;
+
+ /* First 2 bytes are always 0x00 0x01 */
+ result |= *sig++ ^ 0x00;
+ result |= *sig++ ^ 0x01;
+
+ /* Then 0xff bytes until the tail */
+ for (i = 0; i < pad_size - tail_size - 2; i++)
+ result |= *sig++ ^ 0xff;
+
+ /*
+ * Then the tail. Even though there are probably no timing issues
+ * here, we use vb2_safe_memcmp() just to be on the safe side.
+ */
+ result |= vb2_safe_memcmp(sig, sha256_tail, tail_size);
+
+ return result ? BDB_ERROR_DIGEST : BDB_SUCCESS;
+}
+
+/* Array size for RSA4096 */
+#define ARRSIZE4096 (4096 / 32)
+
+/**
+ * In-place public exponentiation. (exponent 65537, key size 4096 bits)
+ *
+ * @param key Key to use in signing
+ * @param inout Input and output big-endian byte array
+ */
+static void modpowF4(const struct public_key *key, uint8_t *inout)
+{
+ uint32_t a[ARRSIZE4096];
+ uint32_t aR[ARRSIZE4096];
+ uint32_t aaR[ARRSIZE4096];
+ uint32_t *aaa = aaR; /* Re-use location. */
+ int i;
+
+ /* Convert from big endian byte array to little endian word array. */
+ for (i = 0; i < ARRSIZE4096; ++i) {
+ uint32_t tmp =
+ (inout[((ARRSIZE4096 - 1 - i) * 4) + 0] << 24) |
+ (inout[((ARRSIZE4096 - 1 - i) * 4) + 1] << 16) |
+ (inout[((ARRSIZE4096 - 1 - i) * 4) + 2] << 8) |
+ (inout[((ARRSIZE4096 - 1 - i) * 4) + 3] << 0);
+ a[i] = tmp;
+ }
+
+ montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
+ for (i = 0; i < 16; i+=2) {
+ montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
+ montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */
+ }
+ montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */
+
+ /* Make sure aaa < mod; aaa is at most 1x mod too large. */
+ if (vb2_mont_ge(key, aaa)) {
+ subM(key, aaa);
+ }
+
+ /* Convert to bigendian byte array */
+ for (i = ARRSIZE4096 - 1; i >= 0; --i) {
+ uint32_t tmp = aaa[i];
+ *inout++ = (uint8_t)(tmp >> 24);
+ *inout++ = (uint8_t)(tmp >> 16);
+ *inout++ = (uint8_t)(tmp >> 8);
+ *inout++ = (uint8_t)(tmp >> 0);
+ }
+}
+
+int bdb_rsa4096_verify(const uint8_t *key_data,
+ const uint8_t *sig,
+ const uint8_t *digest)
+{
+ const uint32_t *kdata32 = (const uint32_t *)key_data;
+ struct public_key key;
+ uint8_t sig_work[BDB_RSA4096_SIG_SIZE];
+ uint32_t pad_size;
+ int rv;
+
+ /* Unpack key */
+ if (kdata32[0] != ARRSIZE4096)
+ return BDB_ERROR_DIGEST; /* Wrong key size */
+
+ key.arrsize = kdata32[0];
+ key.n0inv = kdata32[1];
+ key.n = kdata32 + 2;
+ key.rr = kdata32 + 2 + key.arrsize;
+
+ /* Copy signature to work buffer */
+ memcpy(sig_work, sig, sizeof(sig_work));
+
+ modpowF4(&key, sig_work);
+
+ /*
+ * Check padding. Continue on to check the digest even if error to
+ * reduce the risk of timing based attacks.
+ */
+ pad_size = key.arrsize * sizeof(uint32_t) - BDB_SHA256_DIGEST_SIZE;
+ rv = vb2_check_padding(sig_work, &key, pad_size);
+
+ /*
+ * Check digest. Even though there are probably no timing issues here,
+ * use vb2_safe_memcmp() just to be on the safe side. (That's also why
+ * we don't return before this check if the padding check failed.)
+ */
+ if (vb2_safe_memcmp(sig_work + pad_size, digest,
+ BDB_SHA256_DIGEST_SIZE))
+ rv = BDB_ERROR_DIGEST;
+
+ return rv;
+}
+
+/* Array size for RSA3072B */
+#define ARRSIZE3072B (3072 / 32)
+
+/**
+ * In-place public exponentiation. (exponent 3, key size 3072 bits)
+ *
+ * @param key Key to use in signing
+ * @param inout Input and output big-endian byte array
+ */
+static void modpow3(const struct public_key *key, uint8_t *inout)
+{
+ uint32_t a[ARRSIZE3072B];
+ uint32_t aR[ARRSIZE3072B];
+ uint32_t aaR[ARRSIZE3072B];
+ uint32_t *aaa = aR; /* Re-use location */
+ int i;
+
+ /* Convert from big endian byte array to little endian word array. */
+ for (i = 0; i < ARRSIZE3072B; ++i) {
+ uint32_t tmp =
+ (inout[((ARRSIZE3072B - 1 - i) * 4) + 0] << 24) |
+ (inout[((ARRSIZE3072B - 1 - i) * 4) + 1] << 16) |
+ (inout[((ARRSIZE3072B - 1 - i) * 4) + 2] << 8) |
+ (inout[((ARRSIZE3072B - 1 - i) * 4) + 3] << 0);
+ a[i] = tmp;
+ }
+
+ montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
+ montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
+ montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */
+
+ /* Make sure aaa < mod; aaa is at most 1x mod too large. */
+ if (vb2_mont_ge(key, aaa)) {
+ subM(key, aaa);
+ }
+
+ /* Convert to bigendian byte array */
+ for (i = ARRSIZE3072B - 1; i >= 0; --i) {
+ uint32_t tmp = aaa[i];
+ *inout++ = (uint8_t)(tmp >> 24);
+ *inout++ = (uint8_t)(tmp >> 16);
+ *inout++ = (uint8_t)(tmp >> 8);
+ *inout++ = (uint8_t)(tmp >> 0);
+ }
+}
+
+int bdb_rsa3072b_verify(const uint8_t *key_data,
+ const uint8_t *sig,
+ const uint8_t *digest)
+{
+ const uint32_t *kdata32 = (const uint32_t *)key_data;
+ struct public_key key;
+ uint8_t sig_work[BDB_RSA3072B_SIG_SIZE];
+ uint32_t pad_size;
+ int rv;
+
+ /* Unpack key */
+ if (kdata32[0] != ARRSIZE3072B)
+ return BDB_ERROR_DIGEST; /* Wrong key size */
+
+ key.arrsize = kdata32[0];
+ key.n0inv = kdata32[1];
+ key.n = kdata32 + 2;
+ key.rr = kdata32 + 2 + key.arrsize;
+
+ /* Copy signature to work buffer */
+ memcpy(sig_work, sig, sizeof(sig_work));
+
+ modpow3(&key, sig_work);
+
+ /*
+ * Check padding. Continue on to check the digest even if error to
+ * reduce the risk of timing based attacks.
+ */
+ pad_size = key.arrsize * sizeof(uint32_t) - BDB_SHA256_DIGEST_SIZE;
+ rv = vb2_check_padding(sig_work, &key, pad_size);
+
+ /*
+ * Check digest. Even though there are probably no timing issues here,
+ * use vb2_safe_memcmp() just to be on the safe side. (That's also why
+ * we don't return before this check if the padding check failed.)
+ */
+ if (vb2_safe_memcmp(sig_work + pad_size, digest,
+ BDB_SHA256_DIGEST_SIZE))
+ rv = BDB_ERROR_DIGEST;
+
+ return rv;
+}
diff --git a/firmware/bdb/sha.c b/firmware/bdb/sha.c
new file mode 100644
index 00000000..1e83662d
--- /dev/null
+++ b/firmware/bdb/sha.c
@@ -0,0 +1,210 @@
+/* SHA-256 and SHA-512 implementation based on code by Oliver Gay
+ * <olivier.gay@a3.epfl.ch> under a BSD-style license. See below.
+ */
+/*
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date: 04/30/2005
+ *
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include "bdb.h"
+
+#define VB2_SHA256_DIGEST_SIZE 32
+#define VB2_SHA256_BLOCK_SIZE 64
+
+struct vb2_sha256_context {
+ uint32_t h[8];
+ uint32_t total_size;
+ uint32_t size;
+ uint8_t block[2 * VB2_SHA256_BLOCK_SIZE];
+};
+
+#define SHFR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define CH(x, y, z) ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
+#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
+#define UNPACK32(x, str) \
+ { \
+ *((str) + 3) = (uint8_t) ((x) ); \
+ *((str) + 2) = (uint8_t) ((x) >> 8); \
+ *((str) + 1) = (uint8_t) ((x) >> 16); \
+ *((str) + 0) = (uint8_t) ((x) >> 24); \
+ }
+#define PACK32(str, x) \
+ { \
+ *(x) = ((uint32_t) *((str) + 3) ) \
+ | ((uint32_t) *((str) + 2) << 8) \
+ | ((uint32_t) *((str) + 1) << 16) \
+ | ((uint32_t) *((str) + 0) << 24); \
+ }
+#define SHA256_SCR(i) \
+ { \
+ w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \
+ + SHA256_F3(w[i - 15]) + w[i - 16]; \
+ }
+
+static const uint32_t sha256_h0[8] = {
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+};
+
+static const uint32_t sha256_k[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* SHA-256 implementation from verified boot library */
+void vb2_sha256_init(struct vb2_sha256_context *ctx)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ ctx->h[i] = sha256_h0[i];
+ }
+ ctx->size = 0;
+ ctx->total_size = 0;
+}
+
+static void vb2_sha256_transform(struct vb2_sha256_context *ctx,
+ const uint8_t *message,
+ unsigned int block_nb)
+{
+ /* Note that these arrays use 72*4=288 bytes of stack */
+ uint32_t w[64];
+ uint32_t wv[8];
+ uint32_t t1, t2;
+ const unsigned char *sub_block;
+ int i;
+ int j;
+ for (i = 0; i < (int) block_nb; i++) {
+ sub_block = message + (i << 6);
+ for (j = 0; j < 16; j++) {
+ PACK32(&sub_block[j << 2], &w[j]);
+ }
+ for (j = 16; j < 64; j++) {
+ SHA256_SCR(j);
+ }
+ for (j = 0; j < 8; j++) {
+ wv[j] = ctx->h[j];
+ }
+ for (j = 0; j < 64; j++) {
+ t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+ + sha256_k[j] + w[j];
+ t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+ wv[7] = wv[6];
+ wv[6] = wv[5];
+ wv[5] = wv[4];
+ wv[4] = wv[3] + t1;
+ wv[3] = wv[2];
+ wv[2] = wv[1];
+ wv[1] = wv[0];
+ wv[0] = t1 + t2;
+ }
+ for (j = 0; j < 8; j++) {
+ ctx->h[j] += wv[j];
+ }
+ }
+}
+
+void vb2_sha256_update(struct vb2_sha256_context *ctx,
+ const uint8_t *data,
+ uint32_t size)
+{
+ unsigned int block_nb;
+ unsigned int new_size, rem_size, tmp_size;
+ const uint8_t *shifted_data;
+ tmp_size = VB2_SHA256_BLOCK_SIZE - ctx->size;
+ rem_size = size < tmp_size ? size : tmp_size;
+ memcpy(&ctx->block[ctx->size], data, rem_size);
+ if (ctx->size + size < VB2_SHA256_BLOCK_SIZE) {
+ ctx->size += size;
+ return;
+ }
+ new_size = size - rem_size;
+ block_nb = new_size / VB2_SHA256_BLOCK_SIZE;
+ shifted_data = data + rem_size;
+ vb2_sha256_transform(ctx, ctx->block, 1);
+ vb2_sha256_transform(ctx, shifted_data, block_nb);
+ rem_size = new_size % VB2_SHA256_BLOCK_SIZE;
+ memcpy(ctx->block, &shifted_data[block_nb << 6],
+ rem_size);
+ ctx->size = rem_size;
+ ctx->total_size += (block_nb + 1) << 6;
+}
+
+void vb2_sha256_finalize(struct vb2_sha256_context *ctx, uint8_t *digest)
+{
+ unsigned int block_nb;
+ unsigned int pm_size;
+ unsigned int size_b;
+ int i;
+ block_nb = (1 + ((VB2_SHA256_BLOCK_SIZE - 9)
+ < (ctx->size % VB2_SHA256_BLOCK_SIZE)));
+ size_b = (ctx->total_size + ctx->size) << 3;
+ pm_size = block_nb << 6;
+ memset(ctx->block + ctx->size, 0, pm_size - ctx->size);
+ ctx->block[ctx->size] = 0x80;
+ UNPACK32(size_b, ctx->block + pm_size - 4);
+ vb2_sha256_transform(ctx, ctx->block, block_nb);
+ for (i = 0 ; i < 8; i++) {
+ UNPACK32(ctx->h[i], &digest[i << 2]);
+ }
+}
+
+int bdb_sha256(void *digest, const void *buf, size_t size)
+{
+ struct vb2_sha256_context ctx;
+
+ vb2_sha256_init(&ctx);
+ vb2_sha256_update(&ctx, buf, size);
+ vb2_sha256_finalize(&ctx, digest);
+
+ return BDB_SUCCESS;
+}
diff --git a/tests/bdb_test.c b/tests/bdb_test.c
new file mode 100644
index 00000000..6c9022dc
--- /dev/null
+++ b/tests/bdb_test.c
@@ -0,0 +1,492 @@
+/* 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.
+ *
+ * Unit tests
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bdb.h"
+#include "host.h"
+
+#define TEST_EQ(got, want) test_eq(got, want, #got, #want, __LINE__)
+
+void test_eq(int got, int want, const char *gotstr, const char *wantstr,
+ int line)
+{
+ if (got == want)
+ return;
+
+ fprintf(stderr, "Fail(%d): %s != %s\n"
+ "got: 0x%08x (%d)\n"
+ "wanted: 0x%08x (%d)\n",
+ line, gotstr, wantstr, got, got, want, want);
+ exit(1);
+}
+
+void check_header_tests(void)
+{
+ struct bdb_header sgood = {
+ .struct_magic = BDB_HEADER_MAGIC,
+ .struct_major_version = BDB_HEADER_VERSION_MAJOR,
+ .struct_minor_version = BDB_HEADER_VERSION_MINOR,
+ .struct_size = sizeof(struct bdb_header),
+ .bdb_load_address = -1,
+ .bdb_size = 1024,
+ .signed_size = 512,
+ .oem_area_0_size = 256,
+ };
+ const size_t ssize = sgood.struct_size;
+ struct bdb_header s;
+
+ s = sgood;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_SUCCESS);
+ TEST_EQ(bdb_check_header(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size++;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size--;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_SIZE);
+
+ s = sgood;
+ s.struct_magic++;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
+
+ s = sgood;
+ s.struct_major_version++;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_VERSION);
+
+ s = sgood;
+ s.oem_area_0_size++;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_OEM_AREA_SIZE);
+
+ s = sgood;
+ s.bdb_size = ssize - 1;
+ TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_BDB_SIZE);
+}
+
+void check_key_tests(void)
+{
+ struct bdb_key sgood = {
+ .struct_magic = BDB_KEY_MAGIC,
+ .struct_major_version = BDB_KEY_VERSION_MAJOR,
+ .struct_minor_version = BDB_KEY_VERSION_MINOR,
+ .struct_size = (sizeof(struct bdb_key) +
+ BDB_RSA4096_KEY_DATA_SIZE),
+ .hash_alg = BDB_HASH_ALG_SHA256,
+ .sig_alg = BDB_SIG_ALG_RSA4096,
+ .key_version = 1,
+ .description = "Test key",
+ };
+ const size_t ssize = sgood.struct_size;
+ struct bdb_key s;
+
+ s = sgood;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS);
+ TEST_EQ(bdb_check_key(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size++;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size--;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_SIZE);
+
+ s = sgood;
+ s.struct_magic++;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
+
+ s = sgood;
+ s.struct_major_version++;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_VERSION);
+
+ /* Description must contain a null */
+ s = sgood;
+ memset(s.description, 'x', sizeof(s.description));
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_DESCRIPTION);
+
+ /* Data AFTER the null is explicitly allowed, though */
+ s = sgood;
+ s.description[100] = 'x';
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS);
+
+ /* Limited algorithm choices at present */
+ s = sgood;
+ s.hash_alg = BDB_HASH_ALG_INVALID;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_HASH_ALG);
+
+ /* This works because ECDSA521 signatures are smaller than RSA4096 */
+ s = sgood;
+ s.sig_alg = BDB_SIG_ALG_ECSDSA521;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS);
+
+ s = sgood;
+ s.sig_alg = BDB_SIG_ALG_INVALID;
+ TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_SIG_ALG);
+}
+
+void check_sig_tests(void)
+{
+ struct bdb_sig sgood = {
+ .struct_magic = BDB_SIG_MAGIC,
+ .struct_major_version = BDB_SIG_VERSION_MAJOR,
+ .struct_minor_version = BDB_SIG_VERSION_MINOR,
+ .struct_size = sizeof(struct bdb_sig) + BDB_RSA4096_SIG_SIZE,
+ .hash_alg = BDB_HASH_ALG_SHA256,
+ .sig_alg = BDB_SIG_ALG_RSA4096,
+ .signed_size = 123,
+ .description = "Test sig",
+ };
+ const size_t ssize = sgood.struct_size;
+ struct bdb_sig s;
+
+ s = sgood;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS);
+ TEST_EQ(bdb_check_sig(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size++;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size--;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_SIZE);
+
+ s = sgood;
+ s.struct_magic++;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
+
+ s = sgood;
+ s.struct_major_version++;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_VERSION);
+
+ /* Description must contain a null */
+ s = sgood;
+ memset(s.description, 'x', sizeof(s.description));
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_DESCRIPTION);
+
+ /* Data AFTER the null is explicitly allowed, though */
+ s = sgood;
+ s.description[100] = 'x';
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS);
+
+ /* Limited algorithm choices at present */
+ s = sgood;
+ s.hash_alg = BDB_HASH_ALG_INVALID;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_HASH_ALG);
+
+ /* This works because ECDSA521 signatures are smaller than RSA4096 */
+ s = sgood;
+ s.sig_alg = BDB_SIG_ALG_ECSDSA521;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS);
+
+ s = sgood;
+ s.sig_alg = BDB_SIG_ALG_INVALID;
+ TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_SIG_ALG);
+}
+
+void check_data_tests(void)
+{
+ struct bdb_data sgood = {
+ .struct_magic = BDB_DATA_MAGIC,
+ .struct_major_version = BDB_DATA_VERSION_MAJOR,
+ .struct_minor_version = BDB_DATA_VERSION_MINOR,
+ .struct_size = sizeof(struct bdb_data),
+ .data_version = 1,
+ .oem_area_1_size = 256,
+ .num_hashes = 3,
+ .hash_entry_size = sizeof(struct bdb_hash),
+ .signed_size = 2048,
+ .description = "Test data",
+ };
+ const size_t ssize = sgood.signed_size;
+ struct bdb_data s;
+
+ s = sgood;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS);
+ TEST_EQ(bdb_check_data(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
+
+ s = sgood;
+ s.struct_size--;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_SIZE);
+
+ s = sgood;
+ s.struct_magic++;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
+
+ s = sgood;
+ s.struct_major_version++;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_VERSION);
+
+ /* Description must contain a null */
+ s = sgood;
+ memset(s.description, 'x', sizeof(s.description));
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_DESCRIPTION);
+
+ /* Data AFTER the null is explicitly allowed, though */
+ s = sgood;
+ s.description[100] = 'x';
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS);
+
+ s = sgood;
+ s.hash_entry_size--;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_HASH_ENTRY_SIZE);
+
+ s = sgood;
+ s.oem_area_1_size++;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_OEM_AREA_SIZE);
+
+ /* Check exact size needed */
+ s = sgood;
+ s.signed_size = sizeof(s) + s.num_hashes * sizeof(struct bdb_hash) +
+ s.oem_area_1_size;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS);
+ s.signed_size--;
+ TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_SIGNED_SIZE);
+
+ /*
+ * TODO: Verify wraparound check works. That can only be tested on a
+ * platform where size_t is uint32_t, because otherwise a 32-bit
+ * oem_area_1_size can't cause wraparound.
+ */
+}
+
+/**
+ * Test bdb_verify() and bdb_create()
+ */
+void check_bdb_verify(void)
+{
+ uint8_t oem_area_0[32] = "Some OEM area.";
+ uint8_t oem_area_1[64] = "Some other OEM area.";
+
+ struct bdb_hash hash[2] = {
+ {
+ .offset = 0x10000,
+ .size = 0x18000,
+ .partition = 1,
+ .type = BDB_DATA_SP_RW,
+ .load_address = 0x100000,
+ .digest = {0x11, 0x11, 0x11, 0x10},
+ },
+ {
+ .offset = 0x28000,
+ .size = 0x20000,
+ .partition = 1,
+ .type = BDB_DATA_AP_RW,
+ .load_address = 0x200000,
+ .digest = {0x22, 0x22, 0x22, 0x20},
+ },
+ };
+
+ struct bdb_create_params p = {
+ .bdb_load_address = 0x11223344,
+ .oem_area_0 = oem_area_0,
+ .oem_area_0_size = sizeof(oem_area_0),
+ .oem_area_1 = oem_area_1,
+ .oem_area_1_size = sizeof(oem_area_1),
+ .header_sig_description = "The header sig",
+ .data_sig_description = "The data sig",
+ .data_description = "Test BDB data",
+ .data_version = 3,
+ .hash = hash,
+ .num_hashes = 2,
+ };
+
+ uint8_t bdbkey_digest[BDB_SHA256_DIGEST_SIZE];
+ struct bdb_header *hgood, *h;
+ size_t hsize;
+
+ /* Load keys */
+ p.bdbkey = bdb_create_key("testkeys/bdbkey.keyb", 100, "BDB key");
+ p.subkey = bdb_create_key("testkeys/subkey.keyb", 200, "Subkey");
+ p.private_bdbkey = read_pem("testkeys/bdbkey.pem");
+ p.private_subkey = read_pem("testkeys/subkey.pem");
+ if (!p.bdbkey || !p.subkey || !p.private_bdbkey || !p.private_subkey) {
+ fprintf(stderr, "Unable to load test keys\n");
+ exit(2);
+ }
+
+ bdb_sha256(bdbkey_digest, p.bdbkey, p.bdbkey->struct_size);
+
+ /* Create the test BDB */
+ hgood = bdb_create(&p);
+ if (!hgood) {
+ fprintf(stderr, "Unable to create test BDB\n");
+ exit(2);
+ }
+ hsize = hgood->bdb_size;
+
+ /* Allocate a copy we can mangle */
+ h = calloc(hsize, 1);
+
+ /* As created, it should pass */
+ memcpy(h, hgood, hsize);
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_SUCCESS);
+
+ /* Mangle each component in turn */
+ memcpy(h, hgood, hsize);
+ h->struct_magic++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_key *)bdb_get_bdbkey(h))->struct_magic++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_BDBKEY);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_key *)bdb_get_bdbkey(h))->key_version++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_GOOD_OTHER_THAN_KEY);
+
+ memcpy(h, hgood, hsize);
+ h->oem_area_0_size += hsize;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_OEM_AREA_0);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_key *)bdb_get_subkey(h))->struct_magic++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_SUBKEY);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_key *)bdb_get_subkey(h))->struct_size += 4;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_BDB_SIGNED_SIZE);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_sig *)bdb_get_header_sig(h))->struct_magic++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_sig *)bdb_get_header_sig(h))->signed_size--;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_sig *)bdb_get_header_sig(h))->sig_data[0] ^= 0x42;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
+
+ /* Also make sure the header sig really covers all the fields */
+ memcpy(h, hgood, hsize);
+ ((struct bdb_key *)bdb_get_subkey(h))->key_version++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((uint8_t *)bdb_get_oem_area_0(h))[0] ^= 0x42;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((uint8_t *)bdb_get_oem_area_0(h))[p.oem_area_0_size - 1] ^= 0x24;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
+
+ /* Check data header */
+ memcpy(h, hgood, hsize);
+ ((struct bdb_data *)bdb_get_data(h))->struct_magic++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_sig *)bdb_get_data_sig(h))->struct_magic++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_sig *)bdb_get_data_sig(h))->signed_size--;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_sig *)bdb_get_data_sig(h))->sig_data[0] ^= 0x42;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ /* Also make sure the data sig really covers all the fields */
+ memcpy(h, hgood, hsize);
+ ((struct bdb_data *)bdb_get_data(h))->data_version--;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((uint8_t *)bdb_get_oem_area_1(h))[0] ^= 0x42;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((uint8_t *)bdb_get_oem_area_1(h))[p.oem_area_1_size - 1] ^= 0x24;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_hash *)bdb_get_hash(h, BDB_DATA_SP_RW))->offset++;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ memcpy(h, hgood, hsize);
+ ((struct bdb_hash *)bdb_get_hash(h, BDB_DATA_AP_RW))->digest[0] ^= 0x96;
+ TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
+
+ /*
+ * This is also a convenient place to test that all the parameters we
+ * fed into bdb_create() also worked. That also tests all the
+ * bdb_get_*() functions.
+ */
+ memcpy(h, hgood, hsize);
+ TEST_EQ(h->bdb_load_address, p.bdb_load_address);
+
+ TEST_EQ(strcmp(bdb_get_bdbkey(h)->description,
+ p.bdbkey->description), 0);
+ TEST_EQ(bdb_get_bdbkey(h)->key_version, p.bdbkey->key_version);
+
+ TEST_EQ(h->oem_area_0_size, p.oem_area_0_size);
+ TEST_EQ(memcmp(bdb_get_oem_area_0(h), oem_area_0, sizeof(oem_area_0)),
+ 0);
+
+ TEST_EQ(strcmp(bdb_get_subkey(h)->description, p.subkey->description),
+ 0);
+ TEST_EQ(bdb_get_subkey(h)->key_version, p.subkey->key_version);
+
+ TEST_EQ(strcmp(bdb_get_header_sig(h)->description,
+ p.header_sig_description), 0);
+
+ TEST_EQ(strcmp(bdb_get_data(h)->description, p.data_description), 0);
+ TEST_EQ(bdb_get_data(h)->data_version, p.data_version);
+ TEST_EQ(bdb_get_data(h)->num_hashes, p.num_hashes);
+
+ TEST_EQ(bdb_get_data(h)->oem_area_1_size, p.oem_area_1_size);
+ TEST_EQ(memcmp(bdb_get_oem_area_1(h), oem_area_1, sizeof(oem_area_1)),
+ 0);
+
+ TEST_EQ(strcmp(bdb_get_data_sig(h)->description,
+ p.data_sig_description), 0);
+
+ /* Test getting hash entries */
+ memcpy(h, hgood, hsize);
+ TEST_EQ(bdb_get_hash(h, BDB_DATA_SP_RW)->offset, hash[0].offset);
+ TEST_EQ(bdb_get_hash(h, BDB_DATA_AP_RW)->offset, hash[1].offset);
+ /* And a non-existent one */
+ TEST_EQ(bdb_get_hash(h, BDB_DATA_MCU)!=NULL, 0);
+
+ /*
+ * TODO: Verify wraparound checks works. That can only be tested on a
+ * platform where size_t is uint32_t, because otherwise a 32-bit
+ * oem_area_1_size can't cause wraparound.
+ */
+
+ /* Free keys and buffers */
+ free(p.bdbkey);
+ free(p.subkey);
+ RSA_free(p.private_bdbkey);
+ RSA_free(p.private_subkey);
+ free(hgood);
+ free(h);
+}
+
+/*****************************************************************************/
+
+int main(void)
+{
+ printf("Running tests...\n");
+
+ check_header_tests();
+ check_key_tests();
+ check_sig_tests();
+ check_data_tests();
+ check_bdb_verify();
+
+ printf("All tests passed!\n");
+
+ return 0;
+}
diff --git a/tests/testkeys/bdbkey.crt b/tests/testkeys/bdbkey.crt
new file mode 100644
index 00000000..383216a3
--- /dev/null
+++ b/tests/testkeys/bdbkey.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIJANitnQKymb5VMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTAwOTI5MTgxNjM4WhcNMTAxMDI5MTgxNjM4WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAm5v71oqFynujT4FVq5lKaYxpmKfXdeBNKDmLzgu7fXLUKaEqTGEDsseE
+5qyaaP+dmTnQKfne7G31zgf46//YEl+u5Gt/S4oAgYyvs3rjymzD5kVOLEAzgrIX
+AwyhDFARRzAFWos43hypunHGvu4fDBAzZ3zGVulhjgAzD/gNjToVYCP7bj6kTaDx
+1u9siCKdYN09vGwSUt9WuV+yort7kns/B8ArVxt3bFSjsAxuWel/dJyLwCMQ9XAx
+dgWpg3RBUsK/KgekQybPLrhLYJn1AeOApwzJ4HoJSqU/1jCEaGrKA/KtCRXiurZz
+6lBi7sElsigjBvEZH0iCmmRgH3Oi/cbpHIs1C6YHvCCbO90ntwgtDf0+2WJtFtbG
+t5Do3CXri0tcsXBWqISSK3VzzjHH691BVwLuoBvF1XICMEjmq9aJ+MdbEe4E+GU8
+TV9NnRnuYyOUoxeisyXiArUUI9+1qL6pIgulTlY2Ch51QZY5n2aYY97PtosNotbS
+ylMrLvWXGiiQWxux12eOnB3c/3wNYWey8Km4cmOhEOYz7hLz2r1uIoC/SzM5wLnn
+TEQmaiUDNV9R3Gj3E3xkpTq3UNSSPsV7k8lInMtWqzps6aTvBw1k6i6CUvWbEZqm
+t/0bimQHOEdg3OrJjQpwTKSp4ouSyVu0IphDwy1yjKCfNWKRzrUCAwEAAaOBpzCB
+pDAdBgNVHQ4EFgQUyBKBgFg+vONV1sbup7QtFa7DR78wdQYDVR0jBG4wbIAUyBKB
+gFg+vONV1sbup7QtFa7DR7+hSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
+b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDY
+rZ0Cspm+VTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQA0wtlLEAKR
+ctB41x/V10SMFIg5eLbDrUKQQT33BddrhFu0blc7U5jgXjqTYS80xIlOC0hXtN7D
+Z478st3NAxjtvBKxNMWB9Ppz6+15UENnXNGLElhRPaeAbxBs7zVB64b8fY69EJRe
+JOJNp6+c4WJsHWzxrmfHD0Wx18pJ877ThRi/ZH0QP2TjPc0gZT4szP1taoOJ7SXy
+gO10WfPoF1GgI/VXhPLnk2zXpTlFdp+qyKOtDFxOOK/cVKdXAxDDDO9DAw6cvrEn
+mPS2Zml9HI25/CrE00y+k4w7bqzNeGNzhSGPBvq5Yqnefc1dJSdDQZ3XLG9Fis4a
+nVfuSTvP1MUrFEGEvuxRcA0rWPwQtYSHHs8ZnpT6eayTPcpDvWSihe4xUywirXTT
+kbWgeABGQGaoAnFJYhjqBROGdVb4V3vbsjbCi8k2r4IIcqOzp6OIJxha2LvkZ+iu
+f+OlMVAO/C1LbRsVQkfJp7NxEt6PVewQV5Kgnwlf+x7Q2tUfZfdpLd/EMtojv3BD
+Ewx5X2yHGXcYZG/C1kNzyGTfg97/+55mtNlkTmo8elcPxlpnEuMXEv4JthnRy90x
+ZLflcR9q0pOiV+n//KyQvfjH99JmRtVJGG8xlDEtRbJWjFQD/uSEBxeS0T6INrza
+0WTaiIOZB1vMPe6CDYDWDzrFdQrD6HoWDQ==
+-----END CERTIFICATE-----
diff --git a/tests/testkeys/bdbkey.keyb b/tests/testkeys/bdbkey.keyb
new file mode 100644
index 00000000..515aaa98
--- /dev/null
+++ b/tests/testkeys/bdbkey.keyb
Binary files differ
diff --git a/tests/testkeys/bdbkey.pem b/tests/testkeys/bdbkey.pem
new file mode 100644
index 00000000..204b440f
--- /dev/null
+++ b/tests/testkeys/bdbkey.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+Cbo6jQKCAQBtvmt4e1kz8Ehy92n9NVQ+cyy0HklXEkiiu9BSmA4LRPefuBqNKaN0
+ROaJ+z+GoO4br+ZTL4kwb8FU9Py8CfUib+TGOjPuYhFpVONcTfVuF2yeUTf6cYsZ
+sco1Fi8WbPV9ZX8zXvoFjVCnGYP31SbVa6dwJCmTK4JbwMZRUEQlXOd74Dk5A9cC
+qWPg0fyRajrhc9dOgzWj17tTIDlKm0fZ2phkLd5inayK2CIXvKZUy6PTu7medJFQ
+4v7cqNJPFJ/xdkLR3psqDsXTUlBSNnrr24a5QuVA0QV4j2DZZC6+Acgneqz+0Uu6
+t66vMuSdH620bV2n84wh1xXc7qkjDYMTAoIBAQC6DsTyBGNNI0/DGwAsae5Zri8w
+T/SOER7Tc/PCgQyFUNsJJc/OmSy66PPiH2HzqLjl6/jeiJP++oCnfO6pNTq1Fjz4
+Le2iS1szlcuJ9QLdtn2LTqORzdQVpka42X+o+NqJEdzkZb/N6eBA4PPQdTxHIiu1
+WGBpDc5vGkpuzLm9SVCw/4SD84z+Nhs0pqOvwWhmQWCtl28fgqU4LMeOX1Wz5P8E
+IledlgbCZh2KwXuv3BJdkawuwrSPsahnZmoJapx2dE+FkNl4equaBwImfLf5Qifj
+IhIN5GueO9k/D2/7/XvW2qJ3Vy0z0xMMNiTVYufVpbh77Kn2ebKfROlkzMEU
+-----END RSA PRIVATE KEY-----
diff --git a/tests/testkeys/subkey.crt b/tests/testkeys/subkey.crt
new file mode 100644
index 00000000..fad23f48
--- /dev/null
+++ b/tests/testkeys/subkey.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEWzCCAsOgAwIBAgIJAKOPQrNCNRSqMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTIzMjA1MTI4WhcNMTUxMjIzMjA1MTI4WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIB
+iAKCAYEA1/JAfpirBAFZdm+m6AcX5QQt7vWDV6OAN4T5nWxAtedRb0XLeV9ICJJI
+4CwxWKhvM4pe8pUwwldjoIHIs7viDjBnFVO6+41Gwdsp54AO9qpd//izjarwrOFq
+QcZIn7KVOaYxTlMWlfIDOS3K0rr2adBuR/Yq4Nj61XevNsxRelojDLAQeamRia8t
+hB3vR1LHQJlMT7R8VXy3AOHx+YcYKUFCgf5A7J+pdAaOIgkFKuakZZNDC+7D0Ykq
+jPHadHKzT1SCJxILD/YkCUCOn5bdh7oQekCnhJgf3bwPRDaGEsJL3nnxhc8hx3tI
+7DHwKmz9naz63S41Zqd+VvWam7Jttyb6lQjj7Y8TauXGAL3vEiI/u0iiVyw6b3c3
+77/W4RWuRIiMAn6FAkUY6oxKB+67ZA1PoRLfiIxFGot1KEpTeLzdsyYx7fB1Scj+
+DDzx3EaHQAz12E4anTozljvdAPMoZ6i9Dar3eZvA0KFsM5sdNy0Hp96PBeBNiw1O
+XZYUrYgVAgEDo1AwTjAdBgNVHQ4EFgQUiwtVmJWqyIBZWV1xvn7iMbLeE30wHwYD
+VR0jBBgwFoAUiwtVmJWqyIBZWV1xvn7iMbLeE30wDAYDVR0TBAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAYEAJXgsYoQ7QDNf1GF0bQQKnSqIKMssecD8x66rrJQr1Yon
+biJeUfN9N/pAca7CI/vjeTC1w6BlXEbUDNNwLKQYVfTOBdmbW1qDANMUP05PaiAG
+cZHVKMZZrR/5+z+LWG157cP7HzHPGfw78LopVXUDZmd5fRD2d7MnqYCrswp/dORM
+brHTwzpPhH75uNQmq6w2RowjrntDnKhGT0tSY57/OI+Gke3ch3XPPg3juhREaVUm
+4ZXSCwajKmPBju+7adT46gY/LjAYv/rAEiUN5YHOBVHxHdoMOIqsf6VfPVc8USpg
+5fsV7NWGNSuvpiA7xBIBKai3dZl8nztFHvjn7z4x6XrqQ1KTMBvnGONHAHopeUtj
+a3WPd3GpBmLMpSfDWtcaSSuwOAgZro6vGqcHN4FybKASbtS0RynPRL42DBbLiarn
+nA0hhR1YR03Kqc6hexrsg7zrjcNL6b8dzu6o+VxD30ecj68D7IliORtouJahvk5/
+F8MyzvHvAa+K7Hb/IcJv
+-----END CERTIFICATE-----
diff --git a/tests/testkeys/subkey.keyb b/tests/testkeys/subkey.keyb
new file mode 100644
index 00000000..33ed4ace
--- /dev/null
+++ b/tests/testkeys/subkey.keyb
Binary files differ
diff --git a/tests/testkeys/subkey.pem b/tests/testkeys/subkey.pem
new file mode 100644
index 00000000..2a8885e0
--- /dev/null
+++ b/tests/testkeys/subkey.pem
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----