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