blob: 6f4daf9f3303ee4e36b6025ebe076cddf9cfbe85 [file] [log] [blame]
Maulik Pateld3142702022-06-22 10:09:13 +01001/*
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +00002 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
Maulik Pateld3142702022-06-22 10:09:13 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include "measured_boot.h"
9#include "measured_boot_utils.h"
10#include "measured_boot_api.h"
11#include "psa/crypto.h"
Maulik Pateld3142702022-06-22 10:09:13 +010012#include "tfm_boot_status.h"
13#include "boot_hal.h"
14#include "service_api.h"
15#include "tfm_strnlen.h"
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +000016#include "tfm_log_unpriv.h"
Maulik Pateld3142702022-06-22 10:09:13 +010017#include <stdint.h>
18#include <string.h>
19#include <stdbool.h>
20
21#define TEMP_BUFFER_SIZE (MEASUREMENT_VALUE_SIZE + MEASUREMENT_VALUE_MAX_SIZE)
22
Jamie Fox5fb7a132022-08-01 17:26:03 +010023#ifdef CONFIG_TFM_BOOT_STORE_MEASUREMENTS
Maulik Pateld3142702022-06-22 10:09:13 +010024/* Size of 1 complete measurement (value + metadata) in TLV format. */
25#define SHARED_BOOT_MEASUREMENT_SIZE \
26 ((2 * SHARED_DATA_ENTRY_HEADER_SIZE) \
27 + sizeof(struct boot_measurement_metadata) \
28 + MEASUREMENT_VALUE_MAX_SIZE)
29
30/* 2 measurements from the BL1 stages and 1 measurement per image from BL2. */
31#define MAX_SHARED_BOOT_DATA_LENGTH ((2 + MCUBOOT_IMAGE_NUMBER) \
32 * SHARED_BOOT_MEASUREMENT_SIZE)
33
34/*!
35 * \struct boot_measurement_data
36 *
37 * \brief Contains all the measurement and related metadata (from BL1 and BL2).
38 *
39 * \details This is a redefinition of \ref tfm_boot_data to allocate the
40 * appropriate, service dependent size of \ref boot_data.
41 */
42struct boot_measurement_data {
43 struct shared_data_tlv_header header;
44 uint8_t data[MAX_SHARED_BOOT_DATA_LENGTH];
45};
46
47/*!
48 * \var boot_measurements
49 *
50 * \brief Store the boot measurements in the service's memory.
51 *
52 * \details Boot measurements come from the BL1 and BL2 boot stages and stored
53 * on a memory area which is shared between the bootloaders and SPM.
54 * SPM provides the \ref tfm_core_get_boot_data() API to retrieve
55 * the service related data from shared area.
56 */
57__attribute__ ((aligned(4)))
58static struct boot_measurement_data boot_measurements;
Jamie Fox5fb7a132022-08-01 17:26:03 +010059#endif /* CONFIG_TFM_BOOT_STORE_MEASUREMENTS */
Maulik Pateld3142702022-06-22 10:09:13 +010060
61struct measurement_metadata_t {
62 uint8_t signer_id[SIGNER_ID_MAX_SIZE];
63 size_t signer_id_size;
64 uint8_t version[VERSION_MAX_SIZE];
65 uint8_t version_size;
66 uint32_t measurement_algo;
67 uint8_t sw_type[SW_TYPE_MAX_SIZE];
68 uint8_t sw_type_size;
69};
70
71struct measurement_value_t {
72 uint8_t hash_buf[MEASUREMENT_VALUE_MAX_SIZE];
73 uint8_t hash_buf_size;
74};
75
76struct measurement_t {
77 struct measurement_value_t value; /* measurement value */
78 struct measurement_metadata_t metadata; /* metadata */
79};
80
81struct measured_boot_slot_t {
82 bool is_locked;
83 bool is_populated;
84 bool is_common;
85 struct measurement_t measurement;
86};
87
88static struct measured_boot_slot_t measurement_slot[NUM_OF_MEASUREMENT_SLOTS];
89
90static bool is_signer_id_different(uint8_t index, const uint8_t *signer_id)
91{
92 uint8_t *stored_signer_id =
93 &measurement_slot[index].measurement.metadata.signer_id[0];
94 uint8_t stored_signer_id_size =
95 measurement_slot[index].measurement.metadata.signer_id_size;
96
97 if (memcmp(signer_id, stored_signer_id, stored_signer_id_size) != 0) {
98 return true;
99 }
100 return false;
101}
102
103/* TODO: Access control strategy to be updated */
104static bool is_slot_access_prohibited(uint8_t slot_index,
Maulik Patel537ddc82023-01-27 11:56:36 +0000105 const uint8_t *signer_id,
106 uint32_t measurement_algo)
Maulik Pateld3142702022-06-22 10:09:13 +0100107{
Maulik Patel537ddc82023-01-27 11:56:36 +0000108 if (measurement_algo != measurement_slot[slot_index].measurement.metadata.measurement_algo) {
109 /* The client hash algorithm is different from the current slot hash algorithm */
110 return true;
111 }
112
Maulik Pateld3142702022-06-22 10:09:13 +0100113 if (is_signer_id_different(slot_index, signer_id)) {
114 /* The client signer id is different from the current slot signer id */
Maulik Patel537ddc82023-01-27 11:56:36 +0000115 if (!measurement_slot[slot_index].is_common) {
116 /* This slot does NOT hold common measurements */
Maulik Pateld3142702022-06-22 10:09:13 +0100117 return true;
118 }
119 }
120
Maulik Patel537ddc82023-01-27 11:56:36 +0000121 /* The client signer id and hash algo is same as the current slot signer id
122 * and hash algo respectively; hence it must be allowed full access
Maulik Pateld3142702022-06-22 10:09:13 +0100123 */
124 return false;
125}
126
127/* TODO: Implement updates for access control strategy here. */
128static bool is_read_access_prohibited(uint8_t slot_index)
129{
130 return false;
131}
132
133static inline bool is_measurement_slot_populated(const uint8_t index)
134{
135 /* Extension is required if any previous measurement value already exists
136 * in this slot
137 */
138 return (measurement_slot[index].is_populated);
139}
140
141psa_status_t measured_boot_read_measurement(uint8_t index,
142 uint8_t *signer_id,
143 size_t signer_id_size,
144 size_t *signer_id_len,
145 uint8_t *version,
146 size_t version_size,
147 uint8_t *version_len,
148 uint32_t *measurement_algo,
149 uint8_t *sw_type,
150 size_t sw_type_size,
151 uint8_t *sw_type_len,
152 uint8_t *measurement_value,
153 size_t measurement_value_size,
154 size_t *measurement_value_len,
155 uint8_t *is_locked)
156{
157 struct measurement_t *src_measurement =
158 &measurement_slot[index].measurement;
159
160 if (is_read_access_prohibited(index)) {
161 return PSA_ERROR_NOT_PERMITTED;
162 }
163
164 if (is_measurement_slot_populated(index)) {
165 if ((version_size < src_measurement->metadata.version_size) ||
166 (sw_type_size < src_measurement->metadata.sw_type_size) ||
167 (signer_id_size < src_measurement->metadata.signer_id_size) ||
168 (measurement_value_size < src_measurement->value.hash_buf_size)) {
169 /* The size of one of the arguments is incorrect */
170 return PSA_ERROR_INVALID_ARGUMENT;
171 }
172
173 *signer_id_len = src_measurement->metadata.signer_id_size;
174 memcpy(signer_id, src_measurement->metadata.signer_id, *signer_id_len);
175
176 *version_len = src_measurement->metadata.version_size;
177 memcpy(version, src_measurement->metadata.version, *version_len);
178
179 *sw_type_len = src_measurement->metadata.sw_type_size;
180 memcpy(sw_type, src_measurement->metadata.sw_type, *sw_type_len);
181
182 *measurement_algo = src_measurement->metadata.measurement_algo;
183
184 *measurement_value_len = src_measurement->value.hash_buf_size;
185 memcpy(measurement_value, src_measurement->value.hash_buf,
186 *measurement_value_len);
187
188 *is_locked = measurement_slot[index].is_locked;
189 } else {
190 /* Measurement slot is not populated. */
191 return PSA_ERROR_DOES_NOT_EXIST;
192 }
193
194 return PSA_SUCCESS;
195}
196
197static void update_metadata(const uint8_t index,
198 const uint8_t *signer_id,
199 const size_t signer_id_size,
200 const uint8_t *version,
201 const size_t version_size,
202 const uint32_t measurement_algo,
203 const uint8_t *sw_type,
204 const size_t sw_type_size)
205{
206 struct measurement_metadata_t *dest_metadata =
207 &measurement_slot[index].measurement.metadata;
208
209 /* Copy metadata for corresponding measurement slot */
210 dest_metadata->signer_id_size = signer_id_size;
211 dest_metadata->version_size = version_size;
212 dest_metadata->sw_type_size = sw_type_size;
213
214 memcpy(dest_metadata->signer_id, signer_id, signer_id_size);
215 memcpy(dest_metadata->version, version, version_size);
216 dest_metadata->measurement_algo = measurement_algo;
217 memcpy(dest_metadata->sw_type, sw_type, sw_type_size);
218}
219
220static void extend_metadata(const uint8_t index)
221{
222 struct measurement_metadata_t *dest_metadata =
223 &measurement_slot[index].measurement.metadata;
224
225 /* Do not update Signer ID as it should be the same */
226 /* Do not update Measurement algo as it should be the same */
227 /* Clear Version info and Software component description */
228 dest_metadata->version_size = 0;
229 dest_metadata->sw_type_size = 0;
230 (void)memset(dest_metadata->version, 0, VERSION_MAX_SIZE);
231 (void)memset(dest_metadata->sw_type, 0, SW_TYPE_MAX_SIZE);
232}
233
234static void store_measurement_value(const uint8_t index,
235 const uint8_t *src_value,
236 const size_t measurement_size)
237{
238 memcpy(&measurement_slot[index].measurement.value.hash_buf[0], src_value,
239 measurement_size);
240 measurement_slot[index].measurement.value.hash_buf_size = measurement_size;
241}
242
243static inline void lock_measurement_slot(const uint8_t index)
244{
245 measurement_slot[index].is_locked = true;
246}
247
248static inline void get_stored_measurement_value(const uint8_t index,
249 uint8_t *output_buffer,
250 const size_t measurement_size)
251{
252 memcpy(output_buffer,
253 &measurement_slot[index].measurement.value.hash_buf[0],
254 MEASUREMENT_VALUE_SIZE);
255}
256
257static psa_status_t extend_measurement_value(const uint8_t index,
258 const uint8_t *measurement,
259 const size_t measurement_size,
260 uint8_t *hash_result,
261 size_t *hash_len)
262{
263 uint8_t temp_buffer[TEMP_BUFFER_SIZE];
264 size_t total_size;
265
266 /* Read previous measurement */
267 get_stored_measurement_value(index, temp_buffer, MEASUREMENT_VALUE_SIZE);
268
269 /* concatenate new measurement */
270 memcpy(&temp_buffer[MEASUREMENT_VALUE_SIZE], measurement, measurement_size);
271 total_size = measurement_size + MEASUREMENT_VALUE_SIZE;
272
273 /* Perform hash calculation */
274 return psa_hash_compute(TFM_MEASURED_BOOT_HASH_ALG, temp_buffer,
275 total_size, hash_result, MEASUREMENT_VALUE_SIZE,
276 hash_len);
277}
278
279static inline bool is_measurement_slot_locked(const uint8_t index)
280{
281 return measurement_slot[index].is_locked;
282}
283
284static inline void mark_slot_as_occupied(const uint8_t index)
285{
286 measurement_slot[index].is_populated = true;
287}
288
289/* This API is used to extend and store measurement and the corresponding
290 * metadata / boot-record (if provided).
291 */
292psa_status_t measured_boot_extend_measurement(uint8_t index,
293 const uint8_t *signer_id,
294 size_t signer_id_size,
295 const uint8_t *version,
296 uint8_t version_size,
297 uint32_t measurement_algo,
298 const uint8_t *sw_type,
299 uint8_t sw_type_size,
300 const uint8_t *measurement_value,
301 size_t measurement_value_size,
302 uint8_t lock_measurement)
303{
304 psa_status_t status = PSA_SUCCESS;
305 uint8_t hash_result[MEASUREMENT_VALUE_SIZE] = {0};
306 size_t hash_len;
307
308 log_extend_measurement(index,
309 signer_id, signer_id_size,
310 version, version_size,
311 measurement_algo,
312 sw_type, sw_type_size,
313 measurement_value, measurement_value_size,
314 lock_measurement);
315
Maulik Pateld3142702022-06-22 10:09:13 +0100316 if (is_measurement_slot_locked(index)) {
317 /* Cannot write to measurement slot once locked */
318 status = PSA_ERROR_BAD_STATE;
319 goto error;
320 }
321
322 /* Check how metadata needs updating for the requested slot */
323 if (is_measurement_slot_populated(index)) {
Maulik Patel537ddc82023-01-27 11:56:36 +0000324 if (is_slot_access_prohibited(index, signer_id, measurement_algo)) {
325 status = PSA_ERROR_NOT_PERMITTED;
326 goto error;
327 }
328
Maulik Pateld3142702022-06-22 10:09:13 +0100329 /* Extend metadata */
330 extend_metadata(index);
331 } else {
332 /* Store the corresponding metadata */
333 update_metadata(index,
334 signer_id, signer_id_size,
335 version, version_size,
336 measurement_algo,
337 sw_type, sw_type_size);
338 /* Indicate that the slot is not empty anymore */
339 mark_slot_as_occupied(index);
340 }
341
342 /* Extend current measurement with new measured value */
343 status = extend_measurement_value(index,
344 measurement_value,
345 measurement_value_size,
346 hash_result, &hash_len);
347 if (status != PSA_SUCCESS) {
348 goto error;
349 }
350
351 /* Store calculated extended value */
352 store_measurement_value(index, hash_result, hash_len);
353
354 if (lock_measurement) {
355 /* lock measurement slot if requested */
356 lock_measurement_slot(index);
357 }
358
359error:
360 if (status != PSA_SUCCESS) {
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +0000361 VERBOSE_UNPRIV_RAW("Measured Boot : measurement extension failed.\n");
Maulik Pateld3142702022-06-22 10:09:13 +0100362 } else {
Jackson Cooper-Driver90d89a02025-03-03 16:41:37 +0000363 VERBOSE_UNPRIV_RAW("Measured Boot : measurement extended successfully.\n");
Maulik Pateld3142702022-06-22 10:09:13 +0100364 }
365
366 return status;
367}
368
369void initialise_all_measurements(void)
370{
371 uint32_t i;
372
373 for (i = 0; i < NUM_OF_MEASUREMENT_SLOTS; i++) {
374 measurement_slot[i].is_locked = false;
375 measurement_slot[i].is_populated = false;
376 /* By default, mark all slots as "not common" to avoid accidental extend
377 * and write by a different signer id
378 */
379 measurement_slot[i].is_common = false;
380
381 /* Clear all metadata for corresponding measurement slot */
382 (void)memset(&measurement_slot[i].measurement.metadata, 0,
383 sizeof(struct measurement_metadata_t));
384 /* Initialise measurement values to default pattern */
385 (void)memset(&measurement_slot[i].measurement.value.hash_buf[0],
386 MEASUREMENT_VALUE_INIT_PATTERN,
387 sizeof(measurement_slot[i].measurement.value.hash_buf));
388 }
389}
390
Jamie Fox5fb7a132022-08-01 17:26:03 +0100391#ifdef CONFIG_TFM_BOOT_STORE_MEASUREMENTS
Jamie Foxf683acf2023-08-07 18:40:58 +0100392/**
393 * \brief Format an unsigned integer value into the pointed-to string.
394 *
395 * \param[out] str Pointer to string
396 * \param[in] end Pointer to end of string
397 * \param[in] val Unsigned integer value
398 *
399 * \return Number of characters written (or would be written).
400 */
401static size_t format_uint(uint8_t **str, const uint8_t *const end, uint32_t val)
402{
403 size_t num = 0;
404
405 do {
406 if (*str < end) {
407 *(*str)++ = '0' + val % 10;
408 }
409 val /= 10;
410 num++;
411 } while (val > 0);
412
413 return num;
414}
415
416/**
417 * \brief Format the version into a string "major.minor.revision+build".
418 *
419 * \param[out] str Version string
420 * \param[in] size Size of version string
421 * \param[in] major Major version
422 * \param[in] minor Minor version
423 * \param[in] revision Revison version
424 * \param[in] build Build number
425 *
426 * \return Number of characters written (or would be written, if size is too
427 * small), excluding the NULL terminator.
428 */
429static size_t format_version_string(uint8_t *str, size_t size,
430 uint8_t major, uint8_t minor,
431 uint16_t revision, uint32_t build)
432{
433 const uint8_t *const end = str + size;
434 size_t num = 0;
435
436 num += format_uint(&str, end, major);
437
438 if (str < end) {
439 *str++ = '.';
440 }
441 num++;
442
443 num += format_uint(&str, end, minor);
444
445 if (str < end) {
446 *str++ = '.';
447 }
448 num++;
449
450 num += format_uint(&str, end, revision);
451
452 if (str < end) {
453 *str++ = '+';
454 }
455 num++;
456
457 num += format_uint(&str, end, build);
458
459 if (str < end) {
460 *str = '\0';
461 } else if (size > 0) {
462 str[size - 1] = '\0';
463 }
464
465 return num;
466}
467
Maulik Pateld3142702022-06-22 10:09:13 +0100468psa_status_t collect_shared_measurements(void)
469{
470 struct shared_data_tlv_entry tlv_entry;
471 struct boot_measurement_metadata *metadata_ptr;
472 uint8_t *tlv_end;
473 uint8_t *tlv_curr;
474 uint8_t claim;
475 psa_status_t status = PSA_ERROR_GENERIC_ERROR;
Jamie Fox884371e2023-07-24 15:33:08 +0100476 uint8_t version[VERSION_MAX_SIZE];
Jamie Foxf683acf2023-08-07 18:40:58 +0100477 size_t version_size;
Maulik Pateld3142702022-06-22 10:09:13 +0100478
479 /* Collect the measurements from the shared data area and store them. */
Kevin Peng3d407cf2023-08-16 10:36:39 +0800480 status = tfm_core_get_boot_data(TLV_MAJOR_MBS,
481 (struct tfm_boot_data *)&boot_measurements,
482 sizeof(boot_measurements));
483 if (status != PSA_SUCCESS) {
Maulik Pateld3142702022-06-22 10:09:13 +0100484 return PSA_ERROR_GENERIC_ERROR;
485 }
486
487 if (boot_measurements.header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
488 /* Boot measurement information is malformed. */
489 return PSA_ERROR_GENERIC_ERROR;
490 }
491
492 /* Get the boundaries of TLV section where to lookup. */
493 tlv_curr = boot_measurements.data;
494 tlv_end = (uint8_t *)&boot_measurements
495 + boot_measurements.header.tlv_tot_len;
496
497 while (tlv_curr < tlv_end) {
498 /* Copy TLV entry header - the measurement metadata must come first. */
499 (void)memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
500 if ((GET_MBS_CLAIM(tlv_entry.tlv_type) != SW_MEASURE_METADATA) ||
501 (tlv_entry.tlv_len != sizeof(struct boot_measurement_metadata))) {
502 /* Boot measurement information is malformed. */
503 status = PSA_ERROR_GENERIC_ERROR;
504 break;
505 }
506
507 metadata_ptr = (struct boot_measurement_metadata *)
508 (tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE);
509
510 /* Copy next TLV entry header - it must belong to the measurement. */
511 tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
512 (void)memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
513 claim = GET_MBS_CLAIM(tlv_entry.tlv_type);
514
515 if ((claim != SW_MEASURE_VALUE) &&
516 (claim != SW_MEASURE_VALUE_NON_EXTENDABLE)) {
517 /* Boot measurement information is malformed. */
518 status = PSA_ERROR_GENERIC_ERROR;
519 break;
520 } else {
521 /* Validate size limits of metadata items (if applicable) and
522 * the size limits of measurement value before storing it.
523 */
524 if ((metadata_ptr->signer_id_size < SIGNER_ID_MIN_SIZE) ||
525 (metadata_ptr->signer_id_size > SIGNER_ID_MAX_SIZE) ||
526 (tlv_entry.tlv_len < MEASUREMENT_VALUE_MIN_SIZE) ||
527 (tlv_entry.tlv_len > MEASUREMENT_VALUE_MAX_SIZE)) {
528 status = PSA_ERROR_GENERIC_ERROR;
529 break;
530 }
531
Jamie Foxf683acf2023-08-07 18:40:58 +0100532 version_size = format_version_string(version, sizeof(version),
533 metadata_ptr->sw_version.major,
534 metadata_ptr->sw_version.minor,
535 metadata_ptr->sw_version.revision,
536 metadata_ptr->sw_version.build_num);
537 if (version_size >= sizeof(version)) {
Jamie Fox884371e2023-07-24 15:33:08 +0100538 status = PSA_ERROR_GENERIC_ERROR;
539 break;
540 }
541
Maulik Pateld3142702022-06-22 10:09:13 +0100542 /* Store the measurement and associated metadata. */
543 status = measured_boot_extend_measurement(
544 (uint8_t)GET_MBS_SLOT(tlv_entry.tlv_type),
545 metadata_ptr->signer_id,
546 metadata_ptr->signer_id_size,
Jamie Fox884371e2023-07-24 15:33:08 +0100547 version,
548 version_size,
Maulik Pateld3142702022-06-22 10:09:13 +0100549 metadata_ptr->measurement_type,
550 (const uint8_t*)metadata_ptr->sw_type,
551 tfm_strnlen(metadata_ptr->sw_type,
552 sizeof(metadata_ptr->sw_type)),
553 tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE,
554 tlv_entry.tlv_len,
555 (claim == SW_MEASURE_VALUE_NON_EXTENDABLE) ? true : false);
556 if (status != PSA_SUCCESS) {
557 /* Failed to store measurement. */
558 break;
559 }
560 }
561 /* Move to the next TLV entry. */
562 tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
563 }
564
565 return status;
566}
Jamie Fox5fb7a132022-08-01 17:26:03 +0100567#endif /* CONFIG_TFM_BOOT_STORE_MEASUREMENTS */