summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2016-09-19 16:46:37 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-10-04 21:19:09 -0700
commit9928e2ffc29ee55c21c98b3e0e495f6186012606 (patch)
treed9ad0d4beb3b55bb9f86af66e305bb560650d99d
parent351bc294ed73b57706e2b1650d6fbdae9418dd61 (diff)
downloadvboot-9928e2ffc29ee55c21c98b3e0e495f6186012606.tar.gz
bdb: Add 'bdb --resign' to futility
'resign' sub-command signs a BDB using keys provided. It can resign only the data key, the hashes, or both. Required keys vary depending on what part of BDB is invalid and on what public key is specified in the command line. It then detects what key is needed based on the verification result and fails if the required key is not provided. BUG=chromium:649554 BRANCH=none TEST=make runtests. Ran futility bdb --create, --add, --resign, --verify Change-Id: I589a5972f1d7e5066eb56e1c5efb4ee7089d41cd Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/387118 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--Makefile4
-rw-r--r--firmware/bdb/host.c72
-rw-r--r--firmware/bdb/host.h20
-rw-r--r--futility/cmd_bdb.c240
4 files changed, 326 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 1bd44e51..d56b97e2 100644
--- a/Makefile
+++ b/Makefile
@@ -312,6 +312,7 @@ FWLIB20 = ${BUILD}/vboot_fw20.a
# Vboot 2.1 (not yet ready - see firmware/README)
FWLIB21 = ${BUILD}/vboot_fw21.a
+# Static library containing firmware APIs for common boot flow
BDBLIB = ${BUILD}/bdb.a
# Firmware library sources needed by VbInit() call
@@ -446,6 +447,8 @@ ALL_OBJS += ${FWLIB_OBJS} ${FWLIB2X_OBJS} ${FWLIB20_OBJS} ${FWLIB21_OBJS} \
# Intermediate library for the vboot_reference utilities to link against.
UTILLIB = ${BUILD}/libvboot_util.a
+
+# Static library containing both host and firmware APIs
UTILBDB = ${BUILD}/libvboot_utilbdb.a
UTILLIB_SRCS = \
@@ -479,6 +482,7 @@ UTILLIB_SRCS = \
UTILLIB_OBJS = ${UTILLIB_SRCS:%.c=${BUILD}/%.o}
ALL_OBJS += ${UTILLIB_OBJS}
+# Source files containing host side APIs for common boot flow
UTILBDB_SRCS += \
firmware/bdb/host.c
diff --git a/firmware/bdb/host.c b/firmware/bdb/host.c
index 62b2c410..730d17d9 100644
--- a/firmware/bdb/host.c
+++ b/firmware/bdb/host.c
@@ -5,10 +5,10 @@
* Host functions for signing
*/
-#include <stdio.h>
-#include <string.h>
#include <unistd.h>
+#include "2sysincludes.h"
+#include "2common.h"
#include "2sha.h"
#include "bdb.h"
#include "host.h"
@@ -224,6 +224,74 @@ struct bdb_sig *bdb_create_sig(const void *data,
return sig;
}
+int bdb_sign_datakey(uint8_t **bdb, struct rsa_st *key)
+{
+ const struct bdb_header *header = bdb_get_header(*bdb);
+ const struct bdb_key *bdbkey = bdb_get_bdbkey(*bdb);
+ const void *oem = bdb_get_oem_area_0(*bdb);
+ const struct bdb_sig *sig = bdb_get_header_sig(*bdb);
+ struct bdb_sig *new_sig;
+ uint8_t *new_bdb, *src, *dst;
+ size_t len;
+
+ new_sig = bdb_create_sig(oem, header->signed_size,
+ key, bdbkey->sig_alg, NULL);
+ new_bdb = calloc(1, header->bdb_size
+ + (new_sig->struct_size - sig->struct_size));
+ if (!new_bdb)
+ return BDB_ERROR_UNKNOWN;
+
+ /* copy up to sig */
+ src = *bdb;
+ dst = new_bdb;
+ len = bdb_offset_of_header_sig(*bdb);
+ memcpy(dst, src, len);
+
+ /* copy new sig */
+ src += len;
+ dst += len;
+ memcpy(dst, new_sig, new_sig->struct_size);
+
+ /* copy the rest */
+ src += sig->struct_size;
+ dst += new_sig->struct_size;
+ len = bdb_size_of(*bdb) - vb2_offset_of(*bdb, src);
+ memcpy(dst, src, len);
+
+ free(*bdb);
+ free(new_sig);
+ *bdb = new_bdb;
+
+ return BDB_SUCCESS;
+}
+
+int bdb_sign_data(uint8_t **bdb, struct rsa_st *key)
+{
+ const struct bdb_key *datakey = bdb_get_datakey(*bdb);
+ const struct bdb_data *data = bdb_get_data(*bdb);
+ const uint64_t sig_offset = vb2_offset_of(*bdb, bdb_get_data_sig(*bdb));
+ struct bdb_sig *new_sig;
+ uint8_t *new_bdb;
+
+ new_sig = bdb_create_sig(data, data->signed_size,
+ key, datakey->sig_alg, NULL);
+ new_bdb = calloc(1, sig_offset + new_sig->struct_size);
+ if (!new_bdb)
+ return BDB_ERROR_UNKNOWN;
+
+ /* copy all data up to the data sig */
+ memcpy(new_bdb, *bdb, sig_offset);
+
+ /* copy the new signature */
+ memcpy(new_bdb + sig_offset, new_sig, new_sig->struct_size);
+
+ free(*bdb);
+ free(new_sig);
+ *bdb = new_bdb;
+
+ return BDB_SUCCESS;
+}
+
struct bdb_header *bdb_create(struct bdb_create_params *p)
{
size_t bdb_size = 0;
diff --git a/firmware/bdb/host.h b/firmware/bdb/host.h
index 474d82d1..105d668c 100644
--- a/firmware/bdb/host.h
+++ b/firmware/bdb/host.h
@@ -157,6 +157,26 @@ struct bdb_create_params
};
/**
+ * Sign data key in BDB
+ *
+ * @param bdb (IN/OUT) Buffer is freed upon successful call. Caller is
+ * responsible for freeing the newly allocated buffer.
+ * @param key Private BDB key to be signed with
+ * @return BDB_SUCCESS on success or BDB_ERROR_* otherwise.
+ */
+int bdb_sign_datakey(uint8_t **bdb, struct rsa_st *key);
+
+/**
+ * Sign data section of BDB
+ *
+ * @param bdb (IN/OUT) Buffer is freed upon successful call. Caller is
+ * responsible for freeing the newly allocated buffer.
+ * @param key Private data key to be signed with
+ * @return BDB_SUCCESS on success or BDB_ERROR_* otherwise.
+ */
+int bdb_sign_data(uint8_t **bdb, struct rsa_st *key);
+
+/**
* Create a new BDB
*
* Caller must free() returned object.
diff --git a/futility/cmd_bdb.c b/futility/cmd_bdb.c
index a79bd9a0..22778046 100644
--- a/futility/cmd_bdb.c
+++ b/futility/cmd_bdb.c
@@ -210,18 +210,239 @@ exit:
return rv;
}
+static int install_bdbkey(uint8_t **bdb, const struct bdb_key *new_key)
+{
+ struct bdb_header *header;
+ const struct bdb_key *key;
+ uint8_t *p, *q;
+ uint8_t *new_bdb;
+ size_t new_size;
+ size_t l;
+
+ header = (struct bdb_header *)bdb_get_header(*bdb);
+ key = bdb_get_bdbkey(*bdb);
+ new_size = bdb_size_of(*bdb) + new_key->struct_size - key->struct_size;
+ new_bdb = calloc(1, new_size);
+ if (!new_bdb) {
+ fprintf(stderr, "Unable to allocate memory\n");
+ return -1;
+ }
+
+ /* copy BDB header */
+ p = *bdb;
+ q = new_bdb;
+ l = header->struct_size;
+ memcpy(q, p, l);
+
+ /* copy new BDB key */
+ p += l;
+ q += l;
+ memcpy(q, new_key, new_key->struct_size);
+
+ /* copy the rest */
+ p += key->struct_size;
+ q += new_key->struct_size;
+ l = bdb_size_of(*bdb) - vb2_offset_of(*bdb, p);
+ memcpy(q, p, l);
+
+ /* update size */
+ header = (struct bdb_header *)bdb_get_header(new_bdb);
+ header->bdb_size = new_size;
+
+ free(*bdb);
+ *bdb = new_bdb;
+
+ return 0;
+}
+
+static int install_datakey(uint8_t **bdb, const struct bdb_key *new_key)
+{
+ struct bdb_header *header;
+ struct bdb_key *key;
+ uint8_t *p, *q;
+ uint8_t *new_bdb;
+ size_t new_size;
+ uint32_t l;
+
+ key = (struct bdb_key *)bdb_get_datakey(*bdb);
+ new_size = bdb_size_of(*bdb) + new_key->struct_size - key->struct_size;
+ new_bdb = calloc(1, new_size);
+ if (!new_bdb) {
+ fprintf(stderr, "Unable to allocate memory\n");
+ return -1;
+ }
+
+ /* copy the stuff up to datakey */
+ p = *bdb;
+ q = new_bdb;
+ l = bdb_offset_of_datakey(*bdb);
+ memcpy(q, p, l);
+
+ /* copy new data key */
+ p += l;
+ q += l;
+ memcpy(q, new_key, new_key->struct_size);
+
+ /* copy the rest */
+ p += key->struct_size;
+ q += new_key->struct_size;
+ l = bdb_size_of(*bdb) - vb2_offset_of(*bdb, p);
+ memcpy(q, p, l);
+
+ /* update size */
+ header = (struct bdb_header *)bdb_get_header(new_bdb);
+ header->bdb_size = new_size;
+ header->signed_size = header->oem_area_0_size + new_key->struct_size;
+
+ free(*bdb);
+ *bdb = new_bdb;
+
+ return 0;
+}
/**
* Resign a BDB using new keys
*
- * This resigns data key with a new pair of BDB keys. Optionally, it resigns
- * hash entries with a new pair of data keys if they're provided.
- *
- * @return
+ * It first installs given public keys to the BDB, then, runs verification.
+ * If verification fails due to an invalid signature, it tries to 'fix' it
+ * by resigning it using a given private key, then runs verification again.
+ * Whether a key is required or not depends on which signature is invalid.
+ * If a private key is required but not provided, it returns an error.
*/
-static int do_resign(void)
+static int do_resign(const char *bdb_filename,
+ const char *bdbkey_pri_filename,
+ const char *bdbkey_pub_filename,
+ uint32_t bdbkey_version,
+ const char *datakey_pri_filename,
+ const char *datakey_pub_filename,
+ uint32_t datakey_version)
{
- fprintf(stderr, "'resign' command is not implemented\n");
- return -1;
+ uint8_t *bdb = NULL;
+ struct rsa_st *bdbkey_pri = NULL;
+ struct rsa_st *datakey_pri = NULL;
+ uint32_t bdb_size;
+ int resigned = 0;
+ int rv = -1;
+
+ if (!bdb_filename) {
+ fprintf(stderr, "BDB file must be specified\n");
+ goto exit;
+ }
+
+ bdb = read_file(bdb_filename, &bdb_size);
+ if (!bdb) {
+ fprintf(stderr, "Unable to read %s\n", bdb_filename);
+ goto exit;
+ }
+
+ if (bdbkey_pub_filename) {
+ struct bdb_key *key = bdb_create_key(bdbkey_pub_filename,
+ bdbkey_version, NULL);
+ if (!key) {
+ fprintf(stderr, "Unable to read BDB key\n");
+ goto exit;
+ }
+ if (install_bdbkey(&bdb, key)) {
+ fprintf(stderr, "Unable to install new BDB key\n");
+ goto exit;
+ }
+ }
+
+ if (datakey_pub_filename) {
+ struct bdb_key *key = bdb_create_key(datakey_pub_filename,
+ datakey_version, NULL);
+ if (!key) {
+ fprintf(stderr, "Unable to read data key\n");
+ goto exit;
+ }
+ if (install_datakey(&bdb, key)) {
+ fprintf(stderr, "Unable to install new data key\n");
+ goto exit;
+ }
+ }
+
+ /* Check validity for the new bdb key */
+ rv = bdb_verify(bdb, bdb_size_of(bdb), NULL);
+ if (rv == BDB_ERROR_HEADER_SIG) {
+ /* This is expected failure if we installed a new BDB key.
+ * Let's resign to fix it. */
+ resigned = 1;
+ fprintf(stderr, "Data key signature is invalid. Need to resign "
+ "the key.\n");
+ if (!bdbkey_pri_filename) {
+ fprintf(stderr, "Private BDB key is required but not "
+ "provided.\n");
+ goto exit;
+ }
+ bdbkey_pri = read_pem(bdbkey_pri_filename);
+ rv = bdb_sign_datakey(&bdb, bdbkey_pri);
+ if (rv) {
+ fprintf(stderr, "Failed to resign data key: %d\n", rv);
+ goto exit;
+ }
+ fprintf(stderr, "Data key is resigned.\n");
+ } else {
+ fprintf(stderr, "Resigning data key is not required.\n");
+ }
+
+ /* Check validity for the new data key */
+ rv = bdb_verify(bdb, bdb_size_of(bdb), NULL);
+ switch (rv) {
+ case BDB_ERROR_DATA_SIG:
+ case BDB_ERROR_DATA_CHECK_SIG:
+ /* This is expected failure if we installed a new data key
+ * or sig is corrupted, which happens when a new hash is added
+ * by 'add' sub-command. Let's resign the data */
+ resigned = 1;
+ fprintf(stderr,
+ "Data signature is invalid. Need to resign data.\n");
+ if (!datakey_pri_filename) {
+ fprintf(stderr, "Private data key is required but not "
+ "provided.\n");
+ goto exit;
+ }
+ datakey_pri = read_pem(datakey_pri_filename);
+ rv = bdb_sign_data(&bdb, datakey_pri);
+ if (rv) {
+ fprintf(stderr, "Failed to resign hashes: %d\n", rv);
+ goto exit;
+ }
+ fprintf(stderr, "Data is resigned.\n");
+ break;
+ case BDB_GOOD_OTHER_THAN_KEY:
+ case BDB_SUCCESS:
+ fprintf(stderr, "Resigning the data is not required.\n");
+ break;
+ default:
+ fprintf(stderr, "Verifying BDB failed unexpectedly: %d\n", rv);
+ goto exit;
+ }
+
+ if (!resigned)
+ goto exit;
+
+ /* Check validity one last time */
+ rv = bdb_verify(bdb, bdb_size_of(bdb), NULL);
+ if (rv && rv != BDB_GOOD_OTHER_THAN_KEY) {
+ /* This is not expected. We installed new keys and resigned
+ * BDB but it's still invalid. */
+ fprintf(stderr, "BDB is resigned but it's invalid: %d\n", rv);
+ goto exit;
+ }
+
+ rv = write_file(bdb_filename, bdb, bdb_size_of(bdb));
+ if (rv) {
+ fprintf(stderr, "Unable to write BDB.\n");
+ goto exit;
+ }
+
+ fprintf(stderr, "Successfully resigned BDB.\n");
+
+exit:
+ free(bdb);
+ RSA_free(bdbkey_pri);
+ RSA_free(datakey_pri);
+
+ return rv;
}
static int do_verify(void)
@@ -383,7 +604,10 @@ static int do_bdb(int argc, char *argv[])
datakey_pri_filename, datakey_pub_filename,
datakey_version);
case OPT_MODE_RESIGN:
- return do_resign();
+ return do_resign(bdb_filename, bdbkey_pri_filename,
+ bdbkey_pub_filename, bdbkey_version,
+ datakey_pri_filename, datakey_pub_filename,
+ datakey_version);
case OPT_MODE_VERIFY:
return do_verify();
case OPT_MODE_NONE: