summaryrefslogtreecommitdiff
path: root/firmware/lib/vboot_common.c
blob: b1c6d3394bfc6437d57b5972c9cc77d74776596e (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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* Copyright (c) 2013 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.
 *
 * Common functions between firmware and kernel verified boot.
 * (Firmware portion)
 */

#include "2sysincludes.h"
#include "2common.h"
#include "2misc.h"
#include "2rsa.h"
#include "2sha.h"
#include "vboot_api.h"
#include "vboot_common.h"
#include "utility.h"

const char *kVbootErrors[VBOOT_ERROR_MAX] = {
	"Success.",
	"Key block invalid.",
	"Key block signature failed.",
	"Key block hash failed.",
	"Public key invalid.",
	"Preamble invalid.",
	"Preamble signature check failed.",
	"Shared data invalid."
};

/* Helper functions to get data pointed to by a public key or signature. */

uint8_t *GetPublicKeyData(struct vb2_packed_key *key)
{
	return (uint8_t *)key + key->key_offset;
}

const uint8_t *GetPublicKeyDataC(const struct vb2_packed_key *key)
{
	return (const uint8_t *)key + key->key_offset;
}

uint8_t *GetSignatureData(VbSignature *sig)
{
	return (uint8_t *)sig + sig->sig_offset;
}

const uint8_t *GetSignatureDataC(const VbSignature *sig)
{
	return (const uint8_t *)sig + sig->sig_offset;
}

/*
 * Helper functions to verify the data pointed to by a subfield is inside
 * the parent data.
 */

vb2_error_t VerifyPublicKeyInside(const void *parent, uint64_t parent_size,
				  const struct vb2_packed_key *key)
{
	return vb2_verify_member_inside(parent, parent_size,
					key, sizeof(struct vb2_packed_key),
					key->key_offset, key->key_size);
}

vb2_error_t VerifySignatureInside(const void *parent, uint64_t parent_size,
				  const VbSignature *sig)
{
	return vb2_verify_member_inside(parent, parent_size,
					sig, sizeof(VbSignature),
					sig->sig_offset, sig->sig_size);
}

void PublicKeyInit(struct vb2_packed_key *key,
		   uint8_t *key_data, uint64_t key_size)
{
	key->key_offset = vb2_offset_of(key, key_data);
	key->key_size = key_size;
	key->algorithm = VB2_ALG_COUNT; /* Key not present yet */
	key->key_version = 0;
}

int PublicKeyCopy(struct vb2_packed_key *dest, const struct vb2_packed_key *src)
{
	if (dest->key_size < src->key_size)
		return 1;

	dest->key_size = src->key_size;
	dest->algorithm = src->algorithm;
	dest->key_version = src->key_version;
	memcpy(GetPublicKeyData(dest), GetPublicKeyDataC(src), src->key_size);
	return 0;
}

vb2_error_t VbGetKernelVmlinuzHeader(const VbKernelPreambleHeader *preamble,
				     uint64_t *vmlinuz_header_address,
				     uint64_t *vmlinuz_header_size)
{
	*vmlinuz_header_address = 0;
	*vmlinuz_header_size = 0;
	if (preamble->header_version_minor > 0) {
		/*
		 * Set header and size only if the preamble header version is >
		 * 2.1 as they don't exist in version 2.0 (Note that we don't
		 * need to check header_version_major; if that's not 2 then
		 * VerifyKernelPreamble() would have already failed.
		 */
		*vmlinuz_header_address = preamble->vmlinuz_header_address;
		*vmlinuz_header_size = preamble->vmlinuz_header_size;
	}
	return VBOOT_SUCCESS;
}

vb2_error_t VbKernelHasFlags(const VbKernelPreambleHeader *preamble)
{
	if (preamble->header_version_minor > 1)
		return VBOOT_SUCCESS;

	return VBOOT_KERNEL_PREAMBLE_NO_FLAGS;
}

vb2_error_t VerifyVmlinuzInsideKBlob(uint64_t kblob, uint64_t kblob_size,
				     uint64_t header, uint64_t header_size)
{
	uint64_t end = header-kblob;
	if (end > kblob_size)
		return VBOOT_PREAMBLE_INVALID;
	if (UINT64_MAX - end < header_size)
		return VBOOT_PREAMBLE_INVALID;
	if (end + header_size > kblob_size)
		return VBOOT_PREAMBLE_INVALID;

	return VBOOT_SUCCESS;
}

uint64_t VbSharedDataReserve(VbSharedDataHeader *header, uint64_t size)
{
	if (!header || size > header->data_size - header->data_used) {
		VB2_DEBUG("VbSharedData buffer out of space.\n");
		return 0;  /* Not initialized, or not enough space left. */
	}

	uint64_t offs = header->data_used;
	VB2_DEBUG("VbSharedDataReserve %d bytes at %d\n", (int)size, (int)offs);

	header->data_used += size;
	return offs;
}

vb2_error_t VbSharedDataSetKernelKey(VbSharedDataHeader *header,
				     const struct vb2_packed_key *src)
{
	struct vb2_packed_key *kdest;

	if (!header)
		return VBOOT_SHARED_DATA_INVALID;
	if (!src)
		return VBOOT_PUBLIC_KEY_INVALID;

	kdest = &header->kernel_subkey;

	VB2_DEBUG("Saving kernel subkey to shared data: size %d, algo %d\n",
		  vb2_rsa_sig_size(vb2_crypto_to_signature(src->algorithm)),
		  (int)src->algorithm);

	/* Attempt to allocate space for key, if it hasn't been allocated yet */
	if (!header->kernel_subkey_data_offset) {
		header->kernel_subkey_data_offset =
			VbSharedDataReserve(header, src->key_size);
		if (!header->kernel_subkey_data_offset)
			return VBOOT_SHARED_DATA_INVALID;
		header->kernel_subkey_data_size = src->key_size;
	}

	/* Copy the kernel sign key blob into the destination buffer */
	PublicKeyInit(kdest,
		      (uint8_t *)header + header->kernel_subkey_data_offset,
		      header->kernel_subkey_data_size);

	return PublicKeyCopy(kdest, src);
}

int vb2_allow_recovery(struct vb2_context *ctx)
{
	/* VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY forces this to always return
	   true. */
	if (vb2_get_gbb(ctx)->flags & VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY)
		return 1;

	/*
	 * If EC is in RW, it implies recovery wasn't manually requested.
	 * On some platforms, EC_IN_RW can't be reset by the EC, thus, this may
	 * return false (=RW). That's ok because if recovery is manual, we will
	 * get the right signal and that's the case we care about.
	 */
	if (!VbExTrustEC(0))
		return 0;

	/* Now we confidently check the recovery switch state at boot */
	return !!(vb2_get_sd(ctx)->vbsd->flags & VBSD_BOOT_REC_SWITCH_ON);
}