blob: 5ea417192f5ba5b85b95fc6dab7404fcc736fac4 [file] [log] [blame]
Lucian Paul-Trifu3519afe2022-03-08 15:02:31 +00001/*
2 * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <errno.h>
8#include <stdbool.h>
9#include <stdint.h>
10#include <string.h>
11
12#include <lib/tpm/tpm.h>
13#include <lib/tpm/tpm_log.h>
14
15#include "tpm_log_private.h"
16
17
18/*
19 * TODO: The struct serialisation handling in this library is prone to alignment
20 * faults. It happens to work because the TCG-defined structure fields
21 * generally maintain natural alignment. And it avoids undefined C-language
22 * behaviour thanks to #pragma pack(1).
23 * However, future extensions of this library could introduce structures whose
24 * serialisation would break the natural alignment. For example, serialising
25 * vendor-specific info structures.
26 * Therefore, it would be an improvement if a more standard way of serialising
27 * struct was used, such as preparing structs on the stack first, and then
28 * serialising them to the destination via memcpy().
29 */
30
31static const id_event_container_t id_event_templ = {
32 .container = {
33 .pcr_index = TPM_PCR_0,
34 .event_type = TPM_LOG_EV_NO_ACTION,
35 .digest = {0},
36 /*
37 * Must be set at run-time, when hash_alg_count and the number of
38 * vendor bytes is given:
39 * .event_size = ...;
40 */
41 },
42 .id_event_misc_data = {
43 .signature = TCG_ID_EVENT_SIGNATURE_03,
44 .platform_class = PLATFORM_CLASS_CLIENT,
45 .spec_version_minor = TCG_SPEC_VERSION_MINOR_TPM2,
46 .spec_version_major = TCG_SPEC_VERSION_MAJOR_TPM2,
47 .spec_errata = TCG_SPEC_ERRATA_TPM2,
48 .uintn_size = (uint8_t)(sizeof(unsigned int) /
49 sizeof(uint32_t)),
50 /*
51 * Must be set to hash_alg_count
52 * .number_of_algorithms = hash_alg_count
53 */
54 }
55};
56
57static const event2_header_t startup_event_container_templ = {
58 /*
59 * All EV_NO_ACTION events SHALL set
60 * TCG_PCR_EVENT2.pcrIndex = 0, unless otherwise specified
61 */
62 .pcr_index = TPM_PCR_0,
63
64 /*
65 * All EV_NO_ACTION events SHALL set
66 * TCG_PCR_EVENT2.eventType = 03h
67 */
68 .event_type = TPM_LOG_EV_NO_ACTION,
69
70 /*
71 * All EV_NO_ACTION events SHALL set
72 * TCG_PCR_EVENT2.digests to all
73 * 0x00's for each allocated Hash algorithm
74 *
75 * Must be set at runtime, when hash_alg_count is known:
76 * .digests = {
77 * .count = hash_alg_count,
78 * .digests = {alg_id1, {0}, alg_id2, {0}, ...}
79 * }
80 */
81};
82static const startup_locality_event_t startup_event_templ = {
83 .startup_event_header = {
84 .event_size = sizeof(startup_locality_event_data_t),
85 },
86 .startup_event_data = {
87 .signature = TCG_STARTUP_LOCALITY_SIGNATURE,
88 /*
89 * Must be set at run time, when startup_locality is provided:
90 * .startup_locality = startup_locality
91 */
92 }
93};
94
95
96int tpm_log_init(uint32_t *const buf, size_t buf_bytes,
97 enum tpm_hash_alg alg[], size_t num_algs,
98 struct tpm_log_info *log_out)
99{
100 const char *const buf_end = (char *)buf + buf_bytes;
101 char *cur , *cur_next;
102 char *id_event;
103
104 for (int i = 0; i < num_algs; i++) {
105 if (!tpm_alg_is_valid(alg[i])) {
106 return -EINVAL;
107 }
108 }
109
110 cur = (char *)buf;
111 cur_next = cur + sizeof(id_event_container_t);
112 if (cur_next > buf_end) {
113 return -ENOMEM;
114 }
115
116 /* Copy the TCG_EfiSpecIDEventStruct container template. */
117 (void)memcpy(cur, (const void *)&id_event_templ, sizeof(id_event_templ));
118 id_event = cur;
119
120 /* TCG_EfiSpecIDEventStruct.numberOfAlgorithms */
121 ((id_event_container_t *)cur)->
122 id_event_misc_data.number_of_algorithms = num_algs;
123
124 cur = cur_next;
125
126 /* TCG_EfiSpecIDEventStruct.digestSizes[] */
127 for (int i = 0; i < num_algs; i++) {
128 cur_next = cur + sizeof(id_event_alg_info_t);
129 if (cur_next > buf_end) {
130 return -ENOMEM;
131 }
132
133 ((id_event_alg_info_t *)cur)->algorithm_id = alg[i];
134 ((id_event_alg_info_t *)cur)->digest_size = tpm_alg_dsize(alg[i]);
135 cur = cur_next;
136 }
137
138 #define VENDOR_INFO_SIZE 3U
139 cur_next = cur + offsetof(id_event_vendor_data_t, vendor_info) +
140 VENDOR_INFO_SIZE;
141 if (cur_next > buf_end) {
142 return -ENOMEM;
143 }
144
145 /*
146 * TCG_EfiSpecIDEventStruct.vendorInfoSize -- vendor data is not supported
147 * currently.
148 * Note that when supporting vendor data, it is recommended that only
149 * 4-byte-aligned sizes are supported, because other sizes break the
150 * alignment assumptions relied upon when writing to the event log.
151 */
152 ((id_event_vendor_data_t *)cur)->vendor_info_size = VENDOR_INFO_SIZE;
153 for (int i = 0; i < VENDOR_INFO_SIZE; i++) {
154 ((id_event_vendor_data_t *)cur)->vendor_info[i] = 0;
155 }
156
157 cur = cur_next;
158
159 /* TCG_EfiSpecIDEventStruct container info. */
160 ((id_event_container_t *)id_event)->container.event_size =
161 cur - id_event - sizeof(((id_event_container_t *)NULL)->container);
162
163 log_out->buf = (char *)buf;
164 log_out->buf_bytes = buf_bytes;
165 log_out->cursor = cur;
166 log_out->startup_locality_event_data = NULL;
167 return 0;
168}
169
170static const id_event_misc_data_t *tpm_log_get_id_event(
171 const struct tpm_log_info *log)
172{
173 const char *const buf_end = log->buf + log->buf_bytes;
174
175 if (log->buf + sizeof(id_event_misc_data_t) > buf_end) {
176 return NULL;
177 }
178
179 return &((id_event_container_t *)log->buf)->id_event_misc_data;
180}
181
182static struct tpm_log_digest *digests_arg_get_digest(
183 struct tpm_log_digests *digests,
184 enum tpm_hash_alg required_h_alg)
185{
186 for (int i = 0; i < digests->count; i++) {
187 struct tpm_log_digest *d = digests->d + i;
188
189 if (d->h_alg == required_h_alg) {
190 return d;
191 }
192 }
193
194 return NULL;
195}
196
197static int add_tpml_digest_values(const struct tpm_log_info *log, char *cur,
198 struct tpm_log_digests *digests,
199 char **cur_out)
200{
201 const id_event_misc_data_t *id_event;
202 const char *const buf_end = log->buf + log->buf_bytes;
203 char *cur_next;
204
205 if (!(id_event = tpm_log_get_id_event(log))) {
206 return -EINVAL;
207 }
208
209 cur_next = cur + offsetof(tpml_digest_values_t, digests);
210 if (cur_next > buf_end) {
211 return -ENOMEM;
212 }
213
214 /* TCG_PCR_EVENT2.Digests.Count */
215 ((tpml_digest_values_t *)cur)->count = id_event->number_of_algorithms;
216 cur = cur_next;
217
218 /* TCG_PCR_EVENT2.Digests.Digests[] */
219 for (int i = 0; i < id_event->number_of_algorithms; i++) {
220 const id_event_alg_info_t *required_d = id_event->digest_sizes + i;
221 struct tpm_log_digest *d;
222
223 cur_next = cur + offsetof(tpmt_ha_t, digest);
224 if (cur_next > buf_end) {
225 return -ENOMEM;
226 }
227
228 /* TCG_PCR_EVENT2.Digests.Digests.Algorithm_Id */
229 ((tpmt_ha_t *)cur)->algorithm_id = required_d->algorithm_id;
230 cur = cur_next;
231
232 cur_next = cur + required_d->digest_size;
233 if (cur_next > buf_end) {
234 return -ENOMEM;
235 }
236
237 /* TCG_PCR_EVENT2.Digests.Digests.Digest */
238 if (digests) {
239 d = digests_arg_get_digest(digests, required_d->algorithm_id);
240 (void)memcpy(cur, d->buf, required_d->digest_size);
241 } else {
242 (void)memset(cur, 0, required_d->digest_size);
243 }
244
245 cur = cur_next;
246 }
247
248 *cur_out = cur;
249 return 0;
250}
251
252static int add_startup_locality_event2(const struct tpm_log_info *log, char *cur,
253 uint8_t startup_locality,
254 char **cur_out)
255{
256 const char *const buf_end = log->buf + log->buf_bytes;
257 char *cur_next;
258 int rc;
259
260 cur_next = cur + offsetof(event2_header_t, digests);
261 if (cur_next > buf_end) {
262 return -ENOMEM;
263 }
264
265 /* Copy Startup Locality event container */
266 (void)memcpy(cur, &startup_event_container_templ, cur_next - cur);
267 cur = cur_next;
268
269 if ((rc = add_tpml_digest_values(log, cur, NULL, &cur))) {
270 return rc;
271 }
272
273 cur_next = cur + sizeof(startup_locality_event_t);
274 if (cur_next > buf_end) {
275 return -ENOMEM;
276 }
277
278 /* Copy TCG_EfiStartupLocalityEvent event */
279 (void)memcpy(cur, &startup_event_templ, sizeof(startup_locality_event_t));
280
281 /* Adjust TCG_EfiStartupLocalityEvent.StartupLocality */
282 ((startup_locality_event_t *)cur)->
283 startup_event_data.startup_locality = startup_locality;
284
285 cur = cur_next;
286
287 *cur_out = cur;
288 return 0;
289}
290
291static int check_arg_event_type(uint32_t event_type, enum tpm_pcr_idx pcr,
292 struct tpm_log_digests *digests,
293 const unsigned char *event_data, size_t event_data_bytes)
294{
295 /*
296 * As per TCG specifications, firmware components that are measured
297 * into PCR[0] must be logged in the event log using the event type
298 * EV_POST_CODE.
299 */
300 if (pcr == TPM_PCR_0 && event_type != TPM_LOG_EV_POST_CODE) {
301 return -EINVAL;
302 }
303 /*
304 * EV_NO_ACTION have digest byte values 0s for each allocated hash alg.
305 *
306 * Ref. Section 9.4.5 "EV_NO_ACTION Event Types", requirement #3.
307 */
308 if (event_type == TPM_LOG_EV_NO_ACTION && digests) {
309 return -EINVAL;
310 }
311 if (event_type != TPM_LOG_EV_NO_ACTION && !digests) {
312 return -EINVAL;
313 }
314 /*
315 * TODO: Further event-specific validation or exceptions, e.g. as per
316 * Section 9.4 "Event Descriptions":
317 * - EV_ACTION
318 * - EV_EFI_ACTION
319 */
320
321 return 0;
322}
323
324static int check_arg_digests(const id_event_misc_data_t *id_event,
325 struct tpm_log_digests *digests)
326{
327 /* Check that the digests being added fit the event log's structure. */
328
329 if (digests->count != id_event->number_of_algorithms) {
330 return -EINVAL;
331 }
332
333 for (int i = 0; i < digests->count; i++) {
334 struct tpm_log_digest *d = digests->d + i;
335
336 if (!tpm_alg_is_valid(d->h_alg)) {
337 return -EINVAL;
338 } else if (d->buf_bytes < tpm_alg_dsize(d->h_alg)) {
339 return -EINVAL;
340 }
341 }
342
343 for (int i = 0; i < id_event->number_of_algorithms; i++) {
344 const id_event_alg_info_t *required_d = id_event->digest_sizes + i;
345
346 if (!digests_arg_get_digest(digests, required_d->algorithm_id)) {
347 return -EINVAL;
348 }
349 }
350
351 return 0;
352}
353
354int tpm_log_add_event(struct tpm_log_info *log,
355 uint32_t event_type, enum tpm_pcr_idx pcr,
356 struct tpm_log_digests *digests,
357 const unsigned char *event_data, size_t event_data_bytes)
358{
359 const id_event_misc_data_t *id_event;
360 const char *const buf_end = log->buf + log->buf_bytes;
361 char *cur = log->cursor, *cur_next;
362 int rc;
363
364 if ((rc = check_arg_event_type(event_type, pcr, digests,
365 event_data, event_data_bytes))) {
366 return rc;
367 }
368
369 if (!(id_event = tpm_log_get_id_event(log))) {
370 return -EINVAL;
371 }
372
373 if (digests && (rc = check_arg_digests(id_event, digests))) {
374 return rc;
375 }
376
377 /*
378 * The Startup Locality event should be placed in the log before
379 * any event that extends PCR[0].
380 *
381 * Ref. TCG PC Client Platform Firmware Profile 9.4.5.3
382 */
383 if (pcr == TPM_PCR_0 && log->startup_locality_event_data == NULL) {
384 if ((rc = add_startup_locality_event2(log, cur, 3, &cur))) {
385 return rc;
386 }
387 }
388
389 cur_next = cur + offsetof(event2_header_t, digests);
390 if (cur_next > buf_end) {
391 return -ENOMEM;
392 }
393
394 /* TCG_PCR_EVENT2.PCRIndex */
395 ((event2_header_t *)cur)->pcr_index = pcr;
396
397 /* TCG_PCR_EVENT2.EventType */
398 ((event2_header_t *)cur)->event_type = event_type;
399
400 cur = cur_next;
401
402 /*
403 * TODO: Further event-specific handling, e.g. as per Section 9.4 "Event
404 * Descriptions":
405 * - EV_ACTION
406 * - EV_EFI_ACTION
407 */
408
409 /* TCG_PCR_EVENT2.Digests */
410 if ((rc = add_tpml_digest_values(log, cur, digests, &cur))) {
411 return rc;
412 }
413
414 cur_next = cur + offsetof(event2_data_t, event);
415 if (cur_next > buf_end) {
416 return -ENOMEM;
417 }
418
419 /* TCG_PCR_EVENT2.EventSize */
420 ((event2_data_t *)cur)->event_size = event_data_bytes;
421 cur = cur_next;
422
423 /* End of event data */
424 cur_next = cur + event_data_bytes;
425 if (cur_next > buf_end) {
426 return -ENOMEM;
427 }
428
429 /* TCG_PCR_EVENT2.Event */
430 (void)memcpy(cur, event_data, event_data_bytes);
431 cur = cur_next;
432
433 log->cursor = cur;
434
435 return 0;
436}
437
438void tpm_log_serialise(char *dst, const struct tpm_log_info *log,
439 size_t *log_size_out)
440{
441 size_t log_size = log->cursor - log->buf;
442
443 if (dst) {
444 (void)memcpy(dst, log->buf, log_size);
445 }
446 if (log_size_out) {
447 *log_size_out = log_size;
448 }
449}