diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | firmware/bdb/host.c | 72 | ||||
-rw-r--r-- | firmware/bdb/host.h | 20 | ||||
-rw-r--r-- | futility/cmd_bdb.c | 240 |
4 files changed, 326 insertions, 10 deletions
@@ -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: |