diff options
Diffstat (limited to 'futility/file_type_rwsig.c')
-rw-r--r-- | futility/file_type_rwsig.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c new file mode 100644 index 00000000..6b757fff --- /dev/null +++ b/futility/file_type_rwsig.c @@ -0,0 +1,185 @@ +/* + * 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. + */ + +/* + * Some instances of the Chrome OS embedded controller firmware can't do a + * normal software sync handshake at boot, but will verify their own RW images + * instead. This is typically done by putting a struct vb2_packed_key in the RO + * image and a corresponding struct vb2_signature in the RW image. + * + * This file provides the basic implementation for that approach. + */ + +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> + +#include "2sysincludes.h" +#include "2common.h" +#include "2rsa.h" +#include "2sha.h" +#include "file_type.h" +#include "futility.h" +#include "futility_options.h" +#include "vb2_common.h" +#include "host_common.h" +#include "host_key2.h" +#include "host_signature2.h" +#include "util_misc.h" + +/* + * Reserved space for the public key and signature. This may not be enough for + * larger key sizes since the vb2 structs are more than just the raw bits. + */ +#define PUBKEY_RSVD_SIZE 2048 +#define SIGNATURE_RSVD_SIZE 1024 + +/* True if start + size > max */ +static int bigger_than(uint32_t start, uint32_t size, uint32_t max) +{ + int r = start > max || size > max || start > max - size; + if (r) + Debug("%s: 0x%x + 0x%x > 0x%x\n", __func__, start, size, max); + return r; +} + +/* True if one region overlaps the other */ +static int overlaps(uint32_t start_a, uint32_t size_a, + uint32_t start_b, uint32_t size_b) +{ + if (start_a < start_b && start_a <= start_b - size_a) + return 0; + if (start_b < start_a && start_b <= start_a - size_b) + return 0; + Debug("%s: 0x%x + 0x%x overlaps 0x%x + 0x%x\n", + __func__, start_a, size_a, start_b, size_b); + return 1; +} + +/* Return 1 if okay, 0 if not */ +static int parse_size_opts(const uint8_t *buf, uint32_t len, + uint32_t *rw_offset_ptr, uint32_t *rw_size_ptr, + uint32_t *pkey_offset_ptr, uint32_t *sig_offset_ptr) +{ + uint32_t rw_offset, rw_size, pkey_offset, sig_offset; + + /* Start with defaults */ + + /* The image has both RO and RW, evenly split, RO first. */ + rw_size = rw_offset = len / 2; + + /* The public key is up against the end of the RO half */ + pkey_offset = rw_offset - PUBKEY_RSVD_SIZE; + + /* The signature key is up against the end of the whole image */ + sig_offset = len - SIGNATURE_RSVD_SIZE; + + /* The RW image to be signed doesn't include the signature */ + rw_size -= SIGNATURE_RSVD_SIZE; + + /* FIXME: Override the defaults here by looking for an FMAP or similar + * structure telling us where the parts are. */ + + /* We can override any of that with explicit args */ + if (sign_option.rw_offset != 0xffffffff) + rw_offset = sign_option.rw_offset; + if (sign_option.rw_size != 0xffffffff) + rw_size = sign_option.rw_size; + if (sign_option.pkey_offset != 0xffffffff) + pkey_offset = sign_option.pkey_offset; + if (sign_option.sig_offset != 0xffffffff) + sig_offset = sign_option.sig_offset; + + Debug("pkey_offset 0x%08x\n", pkey_offset); + Debug("rw_offset 0x%08x\n", rw_offset); + Debug("rw_size 0x%08x\n", rw_size); + Debug("sig_offset 0x%08x\n", sig_offset); + + /* Now let's do some sanity checks. */ + if (bigger_than(rw_offset, rw_size, len) || + overlaps(rw_offset, rw_size, pkey_offset, PUBKEY_RSVD_SIZE) || + overlaps(rw_offset, rw_size, sig_offset, SIGNATURE_RSVD_SIZE) || + overlaps(pkey_offset, PUBKEY_RSVD_SIZE, + sig_offset, SIGNATURE_RSVD_SIZE)) { + printf("size/offset values are bogus\n"); + return 0; + } + + *rw_offset_ptr = rw_offset; + *rw_size_ptr = rw_size; + *pkey_offset_ptr = pkey_offset; + *sig_offset_ptr = sig_offset; + + return 1; +} + +int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *data) +{ + struct vb2_signature *sig = 0; + int retval = 1; + uint32_t rw_offset, rw_size; /* what to sign */ + uint32_t pkey_offset, sig_offset; /* where to put blobs */ + uint32_t r; + + Debug("%s(): name %s\n", __func__, name); + Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); + + /* Figure out what to sign and where to put the blobs */ + if (!parse_size_opts(buf, len, + &rw_offset, &rw_size, + &pkey_offset, &sig_offset)) + goto done; + + /* Sign the blob */ + r = vb2_sign_data(&sig, buf + rw_offset, rw_size, + sign_option.prikey, 0); + if (r) { + fprintf(stderr, + "Unable to sign data (error 0x%08x, if that helps)\n", + r); + goto done; + } + + Debug("sig_offset 0x%08x\n", sig_offset); + Debug("sig_size 0x%08x\n", sig->c.total_size); + + if (sig->c.total_size > SIGNATURE_RSVD_SIZE) + fprintf(stderr, "WARNING: The signature may be too large" + " (0x%08x > %08x)\n", + sig->c.total_size, SIGNATURE_RSVD_SIZE); + + /* Update the signature */ + memcpy(buf + sig_offset, sig, sig->c.total_size); + + /* If weren't given a public key, we're done */ + if (!sign_option.pkey) { + fprintf(stderr, "No public key given; not updating RO\n"); + retval = 0; + goto done; + } + + Debug("pkey_offset 0x%08x\n", pkey_offset); + Debug("pkey_size 0x%08x\n", sign_option.pkey->c.total_size); + + if (sign_option.pkey->c.total_size > PUBKEY_RSVD_SIZE) + fprintf(stderr, "WARNING: The public key may be too large" + " (0x%08x > %08x)\n", + sign_option.pkey->c.total_size, PUBKEY_RSVD_SIZE); + + /* Update the public key */ + memcpy(buf + pkey_offset, sign_option.pkey, + sign_option.pkey->c.total_size); + + /* Finally */ + retval = 0; +done: + if (sign_option.prikey) + vb2_private_key_free(sign_option.prikey); + if (sign_option.pkey) + free(sign_option.pkey); + + return retval; +} |