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