diff options
Diffstat (limited to 'bdb/host.c')
-rw-r--r-- | bdb/host.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/bdb/host.c b/bdb/host.c new file mode 100644 index 00000000..24d94652 --- /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; +} |