summaryrefslogtreecommitdiff
path: root/bdb/host.c
diff options
context:
space:
mode:
Diffstat (limited to 'bdb/host.c')
-rw-r--r--bdb/host.c347
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;
+}