summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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: