Add BDB library

This is a standalone library for verifying the BDB structures in the
common boot flow document, and a bdb_create utility to create test BDB
structures.  Eventually, creating these structures will be rolled into
futility.

BUG=chrome-os-partner:48448
BRANCH=none
TEST=cd bdb && make runtests

Change-Id: Ic57c26ca84137205da3b6c7d532f5324c93b4285
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/317275
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
diff --git a/bdb/LICENSE b/bdb/LICENSE
new file mode 100644
index 0000000..d251496
--- /dev/null
+++ b/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/bdb/Makefile b/bdb/Makefile
new file mode 100644
index 0000000..2140b67
--- /dev/null
+++ b/bdb/Makefile
@@ -0,0 +1,130 @@
+# Copyright 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.
+
+# This Makefile normally builds in a 'build' subdir, but use
+#
+#    make BUILD=<dir>
+#
+# to put the output somewhere else.
+
+##############################################################################
+# Configuration variables come first.
+
+# Verbose? Use V=1
+ifeq (${V},)
+Q := @
+endif
+
+# Quiet? Use QUIET=1
+ifeq (${QUIET},)
+PRINTF := printf
+else
+PRINTF := :
+endif
+
+CC ?= gcc
+LD = ${CC}
+PKG_CONFIG ?= pkg-config
+
+SRCDIR := $(shell pwd)
+export SRCDIR
+BUILD = ${SRCDIR}/build
+export BUILD
+KEYDIR = ${SRCDIR}/testkeys
+
+CFLAGS = -Wall -Werror
+
+# Create / use dependency files
+CFLAGS += -MMD -MF $@.d
+
+##############################################################################
+# Create output directories if necessary.  Do this via explicit shell commands
+# so it happens before trying to generate/include dependencies.
+_dir_create := $(shell [ -d ${BUILD} ] || mkdir -p ${BUILD}))
+_keydir_create := $(shell [ -d ${KEYDIR} ] || mkdir -p ${KEYDIR}))
+
+INC_PATH := $(shell ${PKG_CONFIG} --cflags libcrypto)
+CFLAGS += ${INC_PATH}
+
+CRYPTO_LIBS := $(shell ${PKG_CONFIG} --libs libcrypto)
+LDLIBS += ${CRYPTO_LIBS}
+
+##############################################################################
+# Sources
+
+LIBSRC = bdb.c host.c sha.c rsa.c
+LIBOBJ = ${LIBSRC:%.c=${BUILD}/%.o}
+
+BDBTESTSRC = bdb_test.c
+BDBTESTOBJ = ${BDBTESTSRC:%.c=${BUILD}/%.o}
+BDBTEST = ${BUILD}/bdb_test
+
+BDBCREATESRC = bdb_create.c
+BDBCREATEOBJ = ${BDBCREATESRC:%.c=${BUILD}/%.o}
+BDBCREATE = ${BUILD}/bdb_create
+
+DUMPRSASRC = dump_rsa.c
+DUMPRSAOBJ = ${DUMPRSASRC:%.c=${BUILD}/%.o}
+DUMPRSA = ${BUILD}/dump_rsa
+
+ALL_OBJS = ${LIBOBJ} ${BDBTESTOBJ} ${BDBCREATEOBJ}
+ALL_EXES = ${BDBTEST} ${BDBCREATE} ${DUMPRSA}
+
+##############################################################################
+# Targets
+
+.PHONY: all
+all: ${ALL_EXES}
+
+.PHONY: clean
+clean:
+	${Q}/bin/rm -rf ${BUILD}
+
+.PHONY: bdb
+bdb: ${BDBCREATE}
+	${Q}${BDBCREATE}
+
+.PHONY: runtests
+runtests: ${BDBTEST}
+	${Q}${BDBTEST}
+
+.PHONY: testkeys
+testkeys: ${DUMPRSA}
+	${Q}openssl genrsa -F4 -out ${KEYDIR}/bdbkey.pem 4096
+	${Q}openssl req -batch -new -x509 -key ${KEYDIR}/bdbkey.pem \
+		-out ${KEYDIR}/bdbkey.crt
+	${Q}${DUMPRSA} -cert ${KEYDIR}/bdbkey.crt > ${KEYDIR}/bdbkey.keyb
+
+	${Q}openssl genrsa -3 -out ${KEYDIR}/subkey.pem 3072
+	${Q}openssl req -batch -new -x509 -key ${KEYDIR}/subkey.pem \
+		-out ${KEYDIR}/subkey.crt
+	${Q}${DUMPRSA} -cert ${KEYDIR}/subkey.crt > ${KEYDIR}/subkey.keyb
+
+${BDBTEST}: ${BDBTESTOBJ} ${LIBOBJ}
+	@$(PRINTF) "    LD            $(subst ${BUILD}/,,$@)\n"
+	${Q}${LD} -o ${BDBTEST} ${CFLAGS} $^ ${LIBS} ${LDLIBS}
+
+${BDBCREATE}: ${BDBCREATEOBJ} ${LIBOBJ}
+	@$(PRINTF) "    LD            $(subst ${BUILD}/,,$@)\n"
+	${Q}${LD} -o ${BDBCREATE} ${CFLAGS} $^ ${LIBS} ${LDLIBS}
+
+${DUMPRSA}: ${DUMPRSAOBJ} ${LIBOBJ}
+	@$(PRINTF) "    LD            $(subst ${BUILD}/,,$@)\n"
+	${Q}${LD} -o ${DUMPRSA} ${CFLAGS} $^ ${LIBS} ${LDLIBS}
+
+##############################################################################
+# Generic build rules. LIBS and OBJS can be overridden to tweak the generic
+# rules for specific targets.
+
+${BUILD}/%: ${BUILD}/%.o ${OBJS} ${LIBS}
+	@${PRINTF} "    LD            $(subst ${BUILD}/,,$@)\n"
+	${Q}${LD} -o $@ ${CFLAGS} ${LDFLAGS} $< ${OBJS} ${LIBS} ${LDLIBS}
+
+${BUILD}/%.o: %.c
+	@${PRINTF} "    CC            $(subst ${BUILD}/,,$@)\n"
+	${Q}${CC} ${CFLAGS} ${INCLUDES} -c -o $@ $<
+
+# Include generated dependencies
+ALL_DEPS += ${ALL_OBJS:%.o=%.o.d}
+-include ${ALL_DEPS}
diff --git a/bdb/README b/bdb/README
new file mode 100644
index 0000000..ec34961
--- /dev/null
+++ b/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/bdb/bdb.c b/bdb/bdb.c
new file mode 100644
index 0000000..d3327a5
--- /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;
+}
diff --git a/bdb/bdb.h b/bdb/bdb.h
new file mode 100644
index 0000000..177deea
--- /dev/null
+++ b/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/bdb/bdb_create.c b/bdb/bdb_create.c
new file mode 100644
index 0000000..fdbf71d
--- /dev/null
+++ b/bdb/bdb_create.c
@@ -0,0 +1,232 @@
+/* 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.
+ *
+ * Create a BDB
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bdb.h"
+#include "host.h"
+
+/* Parameters for creating a BDB hash entry */
+struct create_hash {
+	/* File containing data */
+	const char *filename;
+
+	/* Type of data; enum bdb_data_type */
+	uint8_t type;
+
+	/* Address in RAM to load data.  -1 means use default. */
+	uint64_t load_address;
+
+	/* Partition number containing data, or -1 to use the same partition as
+	 * the BDB. */
+	uint8_t partition;
+
+	/*
+	 * Offset of data from start of partition.
+	 *
+	 * TODO: if -1, append after BDB.  But need to know how big the BDB
+	 * is, and need to round up offset to 32-bit boundary.
+	 */
+	 uint64_t offset;
+};
+
+/* Parameters for a key */
+struct create_key {
+	/* Description */
+	const char *description;
+
+	/* Key version (not meaningful for BDB key) */
+	uint32_t key_version;
+
+	/* Public key filename (.keyb) */
+	const char *public_filename;
+
+	/* Private key filename (.pem) */
+	const char *private_filename;
+};
+
+struct create_params_2 {
+	/* Destination filename */
+	const char *filename;
+
+	/* Partition to contain the BDB */
+	uint8_t partition;
+
+	/* OEM area files.  NULL means there is no data for that area. */
+	const char *oem_area_0_filename;
+	const char *oem_area_1_filename;
+
+	/* BDB key and subkey */
+	struct create_key bdbkey;
+	struct create_key subkey;
+};
+
+/*****************************************************************************/
+/* FILL THIS IN WITH YOUR SOURCE DATA */
+
+/*
+ * Creation parameters.  Hash and num_hashes will be filled in automatically
+ * by create().
+ */
+struct bdb_create_params p = {
+	.bdb_load_address = 0x11223344,
+	.header_sig_description = "The header sig",
+	.data_sig_description = "The data sig",
+	.data_description = "Test BDB data",
+	.data_version = 3,
+};
+
+/* Additional parameters */
+struct create_params_2 p2 = {
+	.filename = "build/bdb.bin",
+	.partition = 1,
+	.oem_area_0_filename = "testdata/oem0.bin",
+	.oem_area_1_filename = "testdata/oem1.bin",
+	.bdbkey = {
+		.description = "Test BDB key",
+		.key_version = 3,
+		.public_filename = "testkeys/bdbkey.keyb",
+		.private_filename = "testkeys/bdbkey.pem",
+	},
+	.subkey = {
+		.description = "Test Subkey",
+		.key_version = 4,
+		.public_filename = "testkeys/subkey.keyb",
+		.private_filename = "testkeys/subkey.pem",
+	},
+};
+
+/* List of hash entries, terminated by one with a NULL filename */
+struct create_hash hash_entries[] = {
+	{
+		.filename = "testdata/sp-rw.bin",
+		.type = BDB_DATA_SP_RW,
+		.load_address = -1,
+		.partition = -1,
+		.offset = 0x10000,
+	},
+	{
+		.filename = "testdata/ap-rw.bin",
+		.type = BDB_DATA_AP_RW,
+		.load_address = 0x200000,
+		.partition = -1,
+		.offset = 0x28000,
+	},
+	{
+		.filename = NULL
+	},
+};
+
+/*****************************************************************************/
+
+int create(void)
+{
+	struct bdb_hash *hash;
+	struct bdb_header *h;
+	int i;
+
+	/* Count the number of hash entries */
+	for (p.num_hashes = 0; hash_entries[p.num_hashes].filename;
+	     p.num_hashes++)
+		;
+	printf("Found %d hash entries\n", p.num_hashes);
+
+	/* Calculate hashes */
+	p.hash = hash = calloc(sizeof(struct bdb_hash), p.num_hashes);
+	for (i = 0; i < p.num_hashes; i++, hash++) {
+		const struct create_hash *he = hash_entries + i;
+
+		/* Read file and calculate size and hash */
+		uint8_t *buf = read_file(he->filename, &hash->size);
+		if (!buf)
+			return 1;
+		if (bdb_sha256(hash->digest, buf, hash->size)) {
+			fprintf(stderr, "Unable to calculate hash\n");
+			return 1;
+		}
+		free(buf);
+
+		hash->type = he->type;
+		hash->load_address = he->load_address;
+
+		hash->partition = he->partition == -1 ? p2.partition :
+			he->partition;
+
+		hash->offset = he->offset;
+	}
+
+	/* Read OEM data */
+	if (p2.oem_area_0_filename) {
+		p.oem_area_0 = read_file(p2.oem_area_0_filename,
+					 &p.oem_area_0_size);
+		if (!p.oem_area_0)
+			return 1;
+
+		if (p.oem_area_0_size & 3) {
+			fprintf(stderr,
+				"OEM area 0 size isn't 32-bit aligned\n");
+			return 1;
+		}
+	}
+
+	if (p2.oem_area_1_filename) {
+		p.oem_area_1 = read_file(p2.oem_area_1_filename,
+					 &p.oem_area_1_size);
+		if (!p.oem_area_1)
+			return 1;
+
+		if (p.oem_area_1_size & 3) {
+			fprintf(stderr,
+				"OEM area 1 size isn't 32-bit aligned\n");
+			return 1;
+		}
+	}
+
+	/* Load keys */
+	p.bdbkey = bdb_create_key(p2.bdbkey.public_filename,
+				  p2.bdbkey.key_version,
+				  p2.bdbkey.description);
+	p.subkey = bdb_create_key(p2.subkey.public_filename,
+				  p2.subkey.key_version,
+				  p2.subkey.description);
+	p.private_bdbkey = read_pem(p2.bdbkey.private_filename);
+	p.private_subkey = read_pem(p2.subkey.private_filename);
+	if (!p.bdbkey || !p.subkey || !p.private_bdbkey || !p.private_subkey) {
+		fprintf(stderr, "Unable to load keys\n");
+		return 1;
+	}
+
+	/* Create the BDB */
+	h = bdb_create(&p);
+	if (!h) {
+		fprintf(stderr, "Unable to create BDB\n");
+		return 1;
+	}
+
+	/* Write it */
+	if (write_file(p2.filename, h, h->bdb_size))
+		return 1;
+
+	/* Free keys and buffers */
+	free(p.bdbkey);
+	free(p.subkey);
+	RSA_free(p.private_bdbkey);
+	RSA_free(p.private_subkey);
+	free(h);
+	free(p.hash);
+
+	return 0;
+}
+
+/*****************************************************************************/
+
+int main(void)
+{
+	return create();
+}
diff --git a/bdb/bdb_struct.h b/bdb/bdb_struct.h
new file mode 100644
index 0000000..f8d2b32
--- /dev/null
+++ b/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/bdb/bdb_test.c b/bdb/bdb_test.c
new file mode 100644
index 0000000..6c9022d
--- /dev/null
+++ b/bdb/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/bdb/dump_rsa.c b/bdb/dump_rsa.c
new file mode 100644
index 0000000..c40f803
--- /dev/null
+++ b/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/bdb/ecdsa.c b/bdb/ecdsa.c
new file mode 100644
index 0000000..f4d1728
--- /dev/null
+++ b/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/bdb/host.c b/bdb/host.c
new file mode 100644
index 0000000..24d9465
--- /dev/null
+++ b/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/bdb/host.h b/bdb/host.h
new file mode 100644
index 0000000..9334680
--- /dev/null
+++ b/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/bdb/rsa.c b/bdb/rsa.c
new file mode 100644
index 0000000..932c973
--- /dev/null
+++ b/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/bdb/sha.c b/bdb/sha.c
new file mode 100644
index 0000000..1e83662
--- /dev/null
+++ b/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/bdb/testdata/ap-rw.bin b/bdb/testdata/ap-rw.bin
new file mode 100644
index 0000000..ed74f8e
--- /dev/null
+++ b/bdb/testdata/ap-rw.bin
@@ -0,0 +1 @@
+This is a pretend ap-rw.bin image.  Exciting.
diff --git a/bdb/testdata/oem0.bin b/bdb/testdata/oem0.bin
new file mode 100644
index 0000000..0b35ed6
--- /dev/null
+++ b/bdb/testdata/oem0.bin
@@ -0,0 +1 @@
+This is some OEM0 data.
diff --git a/bdb/testdata/oem1.bin b/bdb/testdata/oem1.bin
new file mode 100644
index 0000000..a65fc5b
--- /dev/null
+++ b/bdb/testdata/oem1.bin
@@ -0,0 +1 @@
+This is some OEM1 data of some sort
diff --git a/bdb/testdata/sp-rw.bin b/bdb/testdata/sp-rw.bin
new file mode 100644
index 0000000..df31026
--- /dev/null
+++ b/bdb/testdata/sp-rw.bin
@@ -0,0 +1 @@
+This is a pretend sp-rw.bin image.
diff --git a/bdb/testkeys/bdbkey.crt b/bdb/testkeys/bdbkey.crt
new file mode 100644
index 0000000..383216a
--- /dev/null
+++ b/bdb/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/bdb/testkeys/bdbkey.keyb b/bdb/testkeys/bdbkey.keyb
new file mode 100644
index 0000000..515aaa9
--- /dev/null
+++ b/bdb/testkeys/bdbkey.keyb
Binary files differ
diff --git a/bdb/testkeys/bdbkey.pem b/bdb/testkeys/bdbkey.pem
new file mode 100644
index 0000000..204b440
--- /dev/null
+++ b/bdb/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/bdb/testkeys/subkey.crt b/bdb/testkeys/subkey.crt
new file mode 100644
index 0000000..fad23f4
--- /dev/null
+++ b/bdb/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/bdb/testkeys/subkey.keyb b/bdb/testkeys/subkey.keyb
new file mode 100644
index 0000000..33ed4ac
--- /dev/null
+++ b/bdb/testkeys/subkey.keyb
Binary files differ
diff --git a/bdb/testkeys/subkey.pem b/bdb/testkeys/subkey.pem
new file mode 100644
index 0000000..2a8885e
--- /dev/null
+++ b/bdb/testkeys/subkey.pem
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG4wIBAAKCAYEA1/JAfpirBAFZdm+m6AcX5QQt7vWDV6OAN4T5nWxAtedRb0XL
+eV9ICJJI4CwxWKhvM4pe8pUwwldjoIHIs7viDjBnFVO6+41Gwdsp54AO9qpd//iz
+jarwrOFqQcZIn7KVOaYxTlMWlfIDOS3K0rr2adBuR/Yq4Nj61XevNsxRelojDLAQ
+eamRia8thB3vR1LHQJlMT7R8VXy3AOHx+YcYKUFCgf5A7J+pdAaOIgkFKuakZZND
+C+7D0YkqjPHadHKzT1SCJxILD/YkCUCOn5bdh7oQekCnhJgf3bwPRDaGEsJL3nnx
+hc8hx3tI7DHwKmz9naz63S41Zqd+VvWam7Jttyb6lQjj7Y8TauXGAL3vEiI/u0ii
+Vyw6b3c377/W4RWuRIiMAn6FAkUY6oxKB+67ZA1PoRLfiIxFGot1KEpTeLzdsyYx
+7fB1Scj+DDzx3EaHQAz12E4anTozljvdAPMoZ6i9Dar3eZvA0KFsM5sdNy0Hp96P
+BeBNiw1OXZYUrYgVAgEDAoIBgQCP9tWpuxytVjukSm9FWg/uAslJ+QI6bQAlA1ET
+nYB5ROD02TJQ6jAFtttAHXY7GvTNBun3DiCBj5fAVoXNJ+wJdZoON9H9CNnWkhvv
+qrSkcZP/+yJecfXIlkbWhDBqdw4mbsuJjLm5TAImHoc3J07xNZ7apByV5fyOT8ok
+iDZRkWyzIAr7xmEGdMkCvp+E4dorEN2KeFLjqHoAlqFRBLrGK4Gr/tXzFRuirwls
+BgNx7xhDt4IH9IKLsMcIoTxNocul86msFzeUDFXM/ruLFa34DBtT935WhGn+18+/
+bYA+qM4GoC8N/4sSQtENQ8bX21aUHRs1MIOa82rJEmkCWW+Tgw8kYSSi8gVmd7Ly
+QFp4XN17ReaIZZKBYJt/3XamcV2tcapxwqBNiGOLPQM4wnnuinkiZD7rXf1u/NFV
+rHiVFu1+ORZaTX34+Vq9/wdj18E3cm1ghQsN3BQNzCE1qtulDcs5w2O1iZlP29wq
+mtgeU7YHK7CzqNuUiPJWJardCWsCgcEA8VAXoPMSAJByCk3l92eDTOe8n5IMZ307
+qXpGMlSUT+SctpgVdoN9iV+7bUQE636Hn37E5czANNpERIoVjFJRm0ioYW/9jOEN
+B8zPnvwtLN1vVD1u5XNlumDGWGU1bQCdjgA07DtCM0S5xfhWOACCUJ4l4TDE3DAz
+eNfnaXXP8UNfpL6gaWb/xZZwwTesS+hcX8zw14Gzn/GHkyGEtSMA+lezI5C37bzk
+ao2pWr+W26HeDPwdG/38gBQceUujidJ9AoHBAOUW7AP8JbFTMUt0j1eO1UbAKty7
+XZtURTX+EXK9sWTgeh3xlXpMU6K3U+sIQPsldCACjSeYr8lgGeTP54vZ9L6Zu30H
+L2xC/kllafZhOjC5hC4iWaUgePMFiFeOb3prBDJd12ufUlqzydO4bvrKgi2fdAxL
+vEtPFXs4U75RzqfXGdER7/0VOI68hS4GunqaiQ0UYPAE1mh+je5oJntP3fW8WRN1
+KfuQdm5J+JjzQi4NmJAg6NxlB6wrxmMR8NgneQKBwQCg4A/AogwAYEwG3plPmleI
+mn2/trLvqNJw/C7MOGLf7b3PEA5PAlOw6nzzgq3yVFpqVIND3dV4kYLYXA5djDZn
+hcWWSqkIlgiv3d+/UsjIk5+NfknuTO58QIQ67iOeAGkJVXidfNbM2HvZUDl6qwGL
+FBlAyy3oICJQj++bo9/2LOpt1Grw71UuZEsrenLdRZLqiKCPq80VS6+3a63OF1X8
+OndtCyVJKJhHCRuR1Q89FpQIqBNn/qhVYr2mMm0GjFMCgcEAmLnyrVLDy4zLh6MK
+Ol842dVx6HzpEjguI/62TH52Q0BRaUu4/DLibHo38gWAp25NaqxeGmXKhkARQzVF
+B+ajKbvSU1ofnYH+25jxTut8IHutdBbmbhWl91kFj7RKUZytduk6R7+MPHfb4nr0
+pzGsHmpNXYfS3N9jp3rifuE0b+S74Laf/g4ltH2uHq8m/GcGCLhAoAM5mv8JSZrE
+UjU+o9LmDPjGp7WkSYalu0zWyV5ltWtF6ENacsfZl2FLOsT7AoHARP9ZNA7JMYMs
+dbx2l7eLaTtdOeeSE9AxssmzXBsNHWklEx8DTI3MAFcIXPNesqnm++D/T5sXsIy6
+kd3srldHGYHoh1IT5mOO7S8SlKLdYTIlsVIg+I4jpqmHTgk7DWDDpjYzkmJz3A29
+q4HHmNx92SXNZD+mX0GnDdP9XDbT3tbt3ANuR1LoTcI3wAUMTN/NQiF8qj2NHzb1
+CFOpBOJ9cuUgkWcntjqLJ6mUAMcQ8kF7Pyuhn46qDhY5ceVhrPt9
+-----END RSA PRIVATE KEY-----