summaryrefslogtreecommitdiff
path: root/futility/file_type_rwsig.c
blob: 6b757fff09bf0a2e52de2192ea91823d37f70c3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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;
}