Files
OpenCellular/firmware/bdb/host.c
Daisuke Nojiri 9928e2ffc2 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>
2016-10-04 21:19:09 -07:00

420 lines
9.8 KiB
C

/* 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.
*
* Host functions for signing
*/
#include <unistd.h>
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.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);
free(kdata);
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 (vb2_digest_buffer((uint8_t *)data, size,
VB2_HASH_SHA256,
digest + sizeof(info), BDB_SHA256_DIGEST_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;
}
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;
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->datakey->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->datakey->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 datakey */
memcpy(bnext, p->datakey, p->datakey->struct_size);
bnext += p->datakey->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 datakey */
sig = bdb_create_sig(data, data->signed_size, p->private_datakey,
p->datakey->sig_alg, p->data_sig_description);
memcpy(bnext, sig, sig->struct_size);
/* Return the BDB */
return h;
}