| /* 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; |
| } |