blob: 996260b1b8aab65e5ea46421ef308e8c18ae1c9a [file] [log] [blame]
Willy Tudca92e42023-11-16 23:22:07 -08001#include "stddef.h"
2
3#include <libcr51sign/cr51_image_descriptor.h>
4#include <libcr51sign/libcr51sign.h>
5#include <libcr51sign/libcr51sign_internal.h>
6#include <libcr51sign/libcr51sign_mauv.h>
7#include <stdint.h>
8
9#ifdef __cplusplus
10extern "C"
11{
12#endif
13
14#define IMAGE_MAUV_MAX_DENYLIST_ENTRIES \
15 ((IMAGE_MAUV_DATA_MAX_SIZE - sizeof(struct image_mauv)) / sizeof(uint64_t))
16
17_Static_assert(
18 (sizeof(struct image_mauv) +
19 IMAGE_MAUV_MAX_DENYLIST_ENTRIES *
20 MEMBER_SIZE(struct image_mauv, version_denylist[0])) ==
21 IMAGE_MAUV_DATA_MAX_SIZE,
22 "IMAGE_MAUV_MAX_DENYLIST_ENTRIES number of denylist entries do not "
23 "completely fill IMAGE_MAUV_MAX_SIZE bytes assumed for data in struct "
24 "image_mauv");
25
26// Use wrapper struct to preserve alignment of image_mauv
27struct full_mauv
28{
29 struct image_mauv mauv;
30 uint8_t extra[IMAGE_MAUV_DATA_MAX_SIZE - sizeof(struct image_mauv)];
31};
32
33// Verify BLOB magic bytes in payload's image descriptor at the expected offset
34//
35// @param[in] ctx context which describes the image and holds opaque private
36// data for the user of the library
37// @param[in] intf function pointers which interface to the current system
38// and environment
39// @param[in] payload_blob_offset Absolute offset of payload BLOB data in
40// payload's image descriptor
41//
42// @return `failure_reason`
43static failure_reason
44 verify_payload_blob_magic(const struct libcr51sign_ctx* const ctx,
45 const struct libcr51sign_intf* const intf,
46 const uint32_t payload_blob_offset)
47{
48 int irv = 0;
49 struct blob payload_blob = {0};
50
51 if (!intf->read)
52 {
53 CPRINTS(ctx, "%s: Missing interface intf->read\n", __FUNCTION__);
54 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
55 }
56
57 irv = intf->read(ctx, payload_blob_offset, sizeof(struct blob),
58 (uint8_t*)&payload_blob);
59 if (irv != LIBCR51SIGN_SUCCESS)
60 {
61 CPRINTS(ctx, "%s: Could not read BLOB magic bytes from payload\n",
62 __FUNCTION__);
63 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
64 }
65
66 if (payload_blob.blob_magic != BLOB_MAGIC)
67 {
68 CPRINTS(ctx, "%s: BLOB magic bytes read from payload(%x) are invalid\n",
69 __FUNCTION__, payload_blob.blob_magic);
70 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
71 }
72
73 return LIBCR51SIGN_SUCCESS;
74}
75
76// Find offset of Image MAUV data in payload BLOB inside the image descriptor
77//
78// @param[in] ctx context which describes the image and holds opaque private
79// data for the user of the library
80// @param[in] intf function pointers which interface to the current system
81// and environment
82// @param[in] offset_after_payload_blob_magic Absolute offset of data after
83// payload BLOB magic bytes in image
84// descriptor
85// @param[in] payload_blob_size Size of payload BLOB as per its image
86// descriptor
87// @param[out] payload_image_mauv_data_offset Absolute offset of Image MAUV
88// data in payload's image
89// descriptor
90// @param[out] payload_image_mauv_data_size Size of Image MAUV data embedded in
91// payload's image descriptor
92//
93// @return `failure_reason`
94static failure_reason find_image_mauv_data_offset_in_payload(
95 const struct libcr51sign_ctx* const ctx,
96 const struct libcr51sign_intf* const intf,
97 const uint32_t offset_after_payload_blob_magic,
98 const uint32_t payload_blob_size,
99 uint32_t* const restrict payload_image_mauv_data_offset,
100 uint32_t* const restrict payload_image_mauv_data_size)
101{
102 struct blob_data payload_blob_data = {0};
103 int irv = 0;
104 bool found_image_mauv_data = false;
105
106 if (!intf->read)
107 {
108 CPRINTS(ctx, "%s: Missing interface intf->read\n", __FUNCTION__);
109 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
110 }
111 for (uint32_t current_offset = offset_after_payload_blob_magic;
112 current_offset <= offset_after_payload_blob_magic + payload_blob_size -
113 sizeof(struct blob_data);
114 /* increment based on each blob_data's size in loop */)
115 {
116 irv = intf->read(ctx, current_offset, sizeof(struct blob_data),
117 (uint8_t*)&payload_blob_data);
118 if (irv != LIBCR51SIGN_SUCCESS)
119 {
120 CPRINTS(ctx, "%s: Could not read BLOB data at offset %x\n",
121 __FUNCTION__, current_offset);
122 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
123 }
124
125 if ((current_offset - offset_after_payload_blob_magic) +
126 sizeof(struct blob_data) + payload_blob_data.blob_payload_size >
127 payload_blob_size)
128 {
129 CPRINTS(
130 ctx,
131 "%s: BLOB payload size crosses threshold expected by blob_size "
132 "in image descriptor",
133 __FUNCTION__);
134 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
135 }
136
137 switch (payload_blob_data.blob_type_magic)
138 {
139 case BLOB_TYPE_MAGIC_MAUV:
140 if (!found_image_mauv_data)
141 {
142 *payload_image_mauv_data_offset = current_offset +
143 sizeof(struct blob_data);
144 *payload_image_mauv_data_size =
145 payload_blob_data.blob_payload_size;
146 found_image_mauv_data = true;
147 /* intentional fall-through to increment current offset */
148 }
149 else
150 {
151 /* There should be only one Image MAUV in a valid image
152 * descriptor */
153 CPRINTS(
154 ctx,
155 "%s: Found multiple Image MAUV BLOB instances in payload\n",
156 __FUNCTION__);
157 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
158 }
159 default:
160 current_offset += sizeof(struct blob_data) +
161 payload_blob_data.blob_payload_size;
162 /* Increment offset to keep the expected alignment */
163 current_offset =
164 ((current_offset - 1) & ~(BLOB_DATA_ALIGNMENT - 1)) +
165 BLOB_DATA_ALIGNMENT;
166 break;
167 }
168 }
169 if (!found_image_mauv_data)
170 {
171 CPRINTS(ctx, "%s: Did not find Image MAUV BLOB inside payload\n",
172 __FUNCTION__);
173 }
174 return LIBCR51SIGN_SUCCESS;
175}
176
177// Read Image MAUV data from BLOB inside payload's image descriptor
178//
179// @param[in] ctx context which describes the image and holds opaque private
180// data for the user of the library
181// @param[in] intf function pointers which interface to the current system
182// and environment
183// @param[in] payload_image_mauv_data_offset Absolute offset of Image MAUV data
184// in payload's image descriptor
185// @param[in] payload_image_mauv_data_size Size of Image MAUV data embedded in
186// payload's image descriptor
187// @param[out] payload_image_mauv_data_buffer Buffer to store Image MAUV data
188// read from payload's image
189// descriptor
190//
191// @return `failure_reason`
192static failure_reason read_image_mauv_data_from_payload(
193 const struct libcr51sign_ctx* const ctx,
194 const struct libcr51sign_intf* const intf,
195 const uint32_t payload_image_mauv_data_offset,
196 const uint32_t payload_image_mauv_data_size,
197 struct image_mauv* const restrict payload_image_mauv_data_buffer)
198{
199 int irv = 0;
200
201 if (!intf->read)
202 {
203 CPRINTS(ctx, "%s: Missing interface intf->read\n", __FUNCTION__);
204 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
205 }
206
207 if (payload_image_mauv_data_size > IMAGE_MAUV_DATA_MAX_SIZE)
208 {
209 CPRINTS(
210 ctx,
211 "%s: Payload Image MAUV data size (0x%x) is more than supported "
212 "maximum size\n",
213 __FUNCTION__, payload_image_mauv_data_size);
214 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
215 }
216
217 irv = intf->read(ctx, payload_image_mauv_data_offset,
218 payload_image_mauv_data_size,
219 (uint8_t*)payload_image_mauv_data_buffer);
220 if (irv != LIBCR51SIGN_SUCCESS)
221 {
222 CPRINTS(ctx,
223 "%s: Could not read Image MAUV data from payload @ offset 0x%x "
224 "size 0x%x\n",
225 __FUNCTION__, payload_image_mauv_data_offset,
226 payload_image_mauv_data_size);
227 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
228 }
229
230 return LIBCR51SIGN_SUCCESS;
231}
232
233// Check if Image MAUV allows update to a target payload version
234//
235// @param[in] stored_image_mauv_data Image MAUV data stored in system
236// @param[in] new_payload_security_version Payload version
237//
238// @return `bool` `True` if update to target version is allowed by MAUV data
239static bool does_stored_image_mauv_allow_update(
240 const struct image_mauv* const stored_image_mauv_data,
241 const uint64_t new_payload_security_version)
242{
243 if (new_payload_security_version <
244 stored_image_mauv_data->minimum_acceptable_update_version)
245 {
246 return false;
247 }
248
249 for (uint32_t i = 0;
250 i < stored_image_mauv_data->version_denylist_num_entries; i++)
251 {
252 if (stored_image_mauv_data->version_denylist[i] ==
253 new_payload_security_version)
254 {
255 return false;
256 }
257 }
258
259 return true;
260}
261
262// Do a sanity check for values stored in Image MAUV data
263//
264// @param[in] image_mauv_data Image MAUV data
265// @param[in] image_mauv_data_size Size of Image MAUV data in bytes
266//
267// @return `failure_reason`
268static failure_reason sanity_check_image_mauv_data(
269 const struct image_mauv* const restrict image_mauv_data,
270 const uint32_t image_mauv_data_size)
271{
272 uint32_t expected_image_mauv_data_size = 0;
273
274 if (image_mauv_data_size < sizeof(struct image_mauv))
275 {
276 CPRINTS(ctx, "%s: Image MAUV data size is smaller than expected\n",
277 __FUNCTION__);
278 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
279 }
280
281 if (image_mauv_data->mauv_struct_version != IMAGE_MAUV_STRUCT_VERSION)
282 {
283 CPRINTS(ctx, "%s: Unexpected Image MAUV version\n", __FUNCTION__);
284 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
285 }
286
287 if (image_mauv_data->payload_security_version == 0)
288 {
289 // Handle trivial case of someone not initializing MAUV properly
290 CPRINTS(ctx, "%s: Payload security version should be greater than 0\n",
291 __FUNCTION__);
292 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
293 }
294
295 if (image_mauv_data->version_denylist_num_entries >
296 IMAGE_MAUV_MAX_DENYLIST_ENTRIES)
297 {
298 CPRINTS(
299 ctx,
300 "%s: Version denylist entries in Image MAUV exceed maximum count\n",
301 __FUNCTION__);
302 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
303 }
304
305 expected_image_mauv_data_size =
306 sizeof(struct image_mauv) +
307 image_mauv_data->version_denylist_num_entries *
308 MEMBER_SIZE(struct image_mauv, version_denylist[0]);
309
310 if (image_mauv_data_size != expected_image_mauv_data_size)
311 {
312 CPRINTS(ctx,
313 "%s: Size of Image MAUV data (0x%x) is different than expected "
314 "size (0x%x)\n",
315 __FUNCTION__, image_mauv_data_size,
316 expected_image_mauv_data_size);
317 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
318 }
319
320 if (!does_stored_image_mauv_allow_update(
321 image_mauv_data, image_mauv_data->payload_security_version))
322 {
323 CPRINTS(ctx,
324 "%s: Image MAUV does not allow update to the payload it was "
325 "contained in\n",
326 __FUNCTION__);
327 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
328 }
329 return LIBCR51SIGN_SUCCESS;
330}
331
332// Find and read (if found) Image MAUV from payload's image descriptor
333//
334// @param[in] ctx context which describes the image and holds opaque private
335// data for the user of the library
336// @param[in] intf function pointers which interface to the current system
337// and environment
338// @param[in] payload_blob_offset Absolute offset of payload BLOB data in
339// payload's image descriptor
340// @param[in] payload_blob_size Size of payload BLOB data as per payload's
341// image descriptor
342// @param[out] payload_image_mauv_data_buffer Buffer to store Image MAUV data
343// read from payload's image
344// descriptor
345// @param[out] payload_image_mauv_data_size Size of Image MAUV data (in bytes)
346// read from payload's image
347// descriptor
348// @param[out] payload_contains_image_mauv_data Flag to indicate whether Image
349// MAUV data is present in
350// payload's image descriptor
351//
352// @return `failure_reason`
353failure_reason find_and_read_image_mauv_data_from_payload(
354 const struct libcr51sign_ctx* const ctx,
355 const struct libcr51sign_intf* const intf,
356 const uint32_t payload_blob_offset, const uint32_t payload_blob_size,
357 uint8_t payload_image_mauv_data_buffer[],
358 uint32_t* const restrict payload_image_mauv_data_size,
359 bool* const restrict payload_contains_image_mauv_data)
360{
361 failure_reason rv = LIBCR51SIGN_SUCCESS;
362 uint32_t payload_image_mauv_data_offset = 0;
363
364 rv = verify_payload_blob_magic(ctx, intf, payload_blob_offset);
365 if (rv != LIBCR51SIGN_SUCCESS)
366 {
367 return rv;
368 }
369
370 rv = find_image_mauv_data_offset_in_payload(
371 ctx, intf, payload_blob_offset + offsetof(struct blob, blobs),
372 payload_blob_size, &payload_image_mauv_data_offset,
373 payload_image_mauv_data_size);
374 if (rv != LIBCR51SIGN_SUCCESS)
375 {
376 return rv;
377 }
378
379 *payload_contains_image_mauv_data = (payload_image_mauv_data_offset != 0);
380
381 if (*payload_contains_image_mauv_data)
382 {
383 rv = read_image_mauv_data_from_payload(
384 ctx, intf, payload_image_mauv_data_offset,
385 *payload_image_mauv_data_size,
386 (struct image_mauv*)payload_image_mauv_data_buffer);
387 if (rv != LIBCR51SIGN_SUCCESS)
388 {
389 return rv;
390 }
391
392 return sanity_check_image_mauv_data(
393 (struct image_mauv*)payload_image_mauv_data_buffer,
394 *payload_image_mauv_data_size);
395 }
396 return LIBCR51SIGN_SUCCESS;
397}
398
399// Replace stored Image MAUV data with payload Image MAUV data
400//
401// @param[in] ctx context which describes the image and holds opaque private
402// data for the user of the library
403// @param[in] intf function pointers which interface to the current system
404// and environment
405// @param[in] payload_image_mauv_data Image MAUV data from payload
406// @param[in] payload_image_mauv_data_size Size of Image MAUV data (in bytes)
407// embedded inside payload
408//
409// @return `failure_reason`
410static failure_reason update_stored_image_mauv_data(
411 const struct libcr51sign_ctx* const ctx,
412 const struct libcr51sign_intf* const intf,
413 const struct image_mauv* const restrict payload_image_mauv_data,
414 const uint32_t payload_image_mauv_data_size)
415{
416 int irv = 0;
417
418 if (!intf->store_new_image_mauv_data)
419 {
420 CPRINTS(ctx, "%s: Missing interface intf->store_new_image_mauv_data\n",
421 __FUNCTION__);
422 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
423 }
424
425 irv = intf->store_new_image_mauv_data(
426 ctx, (uint8_t*)payload_image_mauv_data, payload_image_mauv_data_size);
427 if (irv != LIBCR51SIGN_SUCCESS)
428 {
429 CPRINTS(ctx,
430 "%s: Could not store new Image MAUV data from the payload\n",
431 __FUNCTION__);
432 return LIBCR51SIGN_ERROR_STORING_NEW_IMAGE_MAUV_DATA;
433 }
434 return LIBCR51SIGN_SUCCESS;
435}
436
437// Validates Image MAUV from payload against stored Image MAUV (if present)
438//
439// @param[in] ctx context which describes the image and holds opaque private
440// data for the user of the library
441// @param[in] intf function pointers which interface to the current system
442// and environment
443// @param[in] payload_blob_offset Absolute offset of BLOB data embedded in
444// image descriptor. `0` if BLOB data is not
445// present in image descriptor
446// @param[in] payload_blob_size Size of BLOB data from `blob_size` field in
447// image descriptor
448//
449// @return `failure_reason`
450failure_reason
451 validate_payload_image_mauv(const struct libcr51sign_ctx* const ctx,
452 const struct libcr51sign_intf* const intf,
453 const uint32_t payload_blob_offset,
454 const uint32_t payload_blob_size)
455{
456 uint32_t payload_image_mauv_data_size = 0;
457 struct full_mauv payload_image_mauv_data_buffer = {0};
458
459 uint32_t stored_image_mauv_data_size = 0;
460 struct full_mauv stored_image_mauv_data_buffer = {0};
461
462 bool payload_contains_image_mauv_data = false;
463
464 failure_reason rv = LIBCR51SIGN_SUCCESS;
465 int irv = 0;
466
467 bool payload_blob_present = (payload_blob_offset != 0);
468 if (payload_blob_present)
469 {
470 rv = find_and_read_image_mauv_data_from_payload(
471 ctx, intf, payload_blob_offset, payload_blob_size,
472 (uint8_t*)&payload_image_mauv_data_buffer,
473 &payload_image_mauv_data_size, &payload_contains_image_mauv_data);
474 if (rv != LIBCR51SIGN_SUCCESS)
475 {
476 return rv;
477 }
478 }
479
480 if (!intf->retrieve_stored_image_mauv_data)
481 {
482 if (payload_contains_image_mauv_data)
483 {
484 CPRINTS(
485 ctx,
486 "%s: Payload contains Image MAUV data but required interface "
487 "intf->retrieve_stored_image_mauv_data is missing\n",
488 __FUNCTION__);
489 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
490 }
491 CPRINTS(
492 ctx,
493 "%s: Payload does not contain Image MAUV data and interface "
494 "intf->retrieve_stored_image_mauv_data is missing. Skipping MAUV "
495 "check for backward compatibility.\n",
496 __FUNCTION__);
497 return LIBCR51SIGN_SUCCESS;
498 }
499
500 irv = intf->retrieve_stored_image_mauv_data(
501 ctx, (uint8_t*)&stored_image_mauv_data_buffer,
502 &stored_image_mauv_data_size, IMAGE_MAUV_DATA_MAX_SIZE);
503 if (irv == LIBCR51SIGN_NO_STORED_MAUV_FOUND)
504 {
505 CPRINTS(
506 ctx,
507 "%s: Stored Image MAUV data not present in the system. Skipping Image "
508 "MAUV check\n",
509 __FUNCTION__);
510 if (payload_contains_image_mauv_data)
511 {
512 return update_stored_image_mauv_data(
513 ctx, intf, (struct image_mauv*)&payload_image_mauv_data_buffer,
514 payload_image_mauv_data_size);
515 }
516 return LIBCR51SIGN_SUCCESS;
517 }
518 if (irv != LIBCR51SIGN_SUCCESS)
519 {
520 CPRINTS(ctx, "%s: Could not retrieve Image MAUV stored in system\n",
521 __FUNCTION__);
522 return LIBCR51SIGN_ERROR_RETRIEVING_STORED_IMAGE_MAUV_DATA;
523 }
524 if (stored_image_mauv_data_size > IMAGE_MAUV_DATA_MAX_SIZE)
525 {
526 CPRINTS(ctx,
527 "%s: Stored Image MAUV data size (0x%x) is more than supported "
528 "maximum size\n",
529 __FUNCTION__, stored_image_mauv_data_size);
530 return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA;
531 }
532
533 rv = sanity_check_image_mauv_data(
534 (struct image_mauv*)&stored_image_mauv_data_buffer,
535 stored_image_mauv_data_size);
536 if (rv != LIBCR51SIGN_SUCCESS)
537 {
538 return rv;
539 }
540
541 if (!payload_contains_image_mauv_data)
542 {
543 CPRINTS(ctx, "%s: Image MAUV expected to be present in payload",
544 __FUNCTION__);
545 return LIBCR51SIGN_ERROR_STORED_IMAGE_MAUV_EXPECTS_PAYLOAD_IMAGE_MAUV;
546 }
547
548 if (!does_stored_image_mauv_allow_update(
549 (struct image_mauv*)&stored_image_mauv_data_buffer,
550 ((struct image_mauv*)&payload_image_mauv_data_buffer)
551 ->payload_security_version))
552 {
553 CPRINTS(ctx,
554 "%s: Stored Image MAUV data does not allow update to payload "
555 "version\n",
556 __FUNCTION__);
557 return LIBCR51SIGN_ERROR_STORED_IMAGE_MAUV_DOES_NOT_ALLOW_UPDATE_TO_PAYLOAD;
558 }
559
560 if (((struct image_mauv*)&payload_image_mauv_data_buffer)
561 ->mauv_update_timestamp >
562 ((struct image_mauv*)&stored_image_mauv_data_buffer)
563 ->mauv_update_timestamp)
564 {
565 return update_stored_image_mauv_data(
566 ctx, intf, (struct image_mauv*)&payload_image_mauv_data_buffer,
567 payload_image_mauv_data_size);
568 }
569 return LIBCR51SIGN_SUCCESS;
570}
571
572#ifdef __cplusplus
573} // extern "C"
574#endif