1 #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 10 extern "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 27 struct 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` 43 static 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` 94 static 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` 192 static 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 239 static 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` 268 static 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` 353 failure_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` 410 static 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` 450 failure_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 575