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