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