| #include "stddef.h" |
| |
| #include <libcr51sign/cr51_image_descriptor.h> |
| #include <libcr51sign/libcr51sign.h> |
| #include <libcr51sign/libcr51sign_internal.h> |
| #include <libcr51sign/libcr51sign_mauv.h> |
| #include <stdint.h> |
| |
| #ifdef __cplusplus |
| extern "C" |
| { |
| #endif |
| |
| #define IMAGE_MAUV_MAX_DENYLIST_ENTRIES \ |
| ((IMAGE_MAUV_DATA_MAX_SIZE - sizeof(struct image_mauv)) / sizeof(uint64_t)) |
| |
| _Static_assert( |
| (sizeof(struct image_mauv) + |
| IMAGE_MAUV_MAX_DENYLIST_ENTRIES * |
| MEMBER_SIZE(struct image_mauv, version_denylist[0])) == |
| IMAGE_MAUV_DATA_MAX_SIZE, |
| "IMAGE_MAUV_MAX_DENYLIST_ENTRIES number of denylist entries do not " |
| "completely fill IMAGE_MAUV_MAX_SIZE bytes assumed for data in struct " |
| "image_mauv"); |
| |
| // Use wrapper struct to preserve alignment of image_mauv |
| struct full_mauv |
| { |
| struct image_mauv mauv; |
| uint8_t extra[IMAGE_MAUV_DATA_MAX_SIZE - sizeof(struct image_mauv)]; |
| }; |
| |
| // Verify BLOB magic bytes in payload's image descriptor at the expected offset |
| // |
| // @param[in] ctx context which describes the image and holds opaque private |
| // data for the user of the library |
| // @param[in] intf function pointers which interface to the current system |
| // and environment |
| // @param[in] payload_blob_offset Absolute offset of payload BLOB data in |
| // payload's image descriptor |
| // |
| // @return `failure_reason` |
| static failure_reason |
| verify_payload_blob_magic(const struct libcr51sign_ctx* const ctx, |
| const struct libcr51sign_intf* const intf, |
| const uint32_t payload_blob_offset) |
| { |
| int irv = 0; |
| struct blob payload_blob = {0}; |
| |
| if (!intf->read) |
| { |
| CPRINTS(ctx, "%s: Missing interface intf->read\n", __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_INTERFACE; |
| } |
| |
| irv = intf->read(ctx, payload_blob_offset, sizeof(struct blob), |
| (uint8_t*)&payload_blob); |
| if (irv != LIBCR51SIGN_SUCCESS) |
| { |
| CPRINTS(ctx, "%s: Could not read BLOB magic bytes from payload\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_RUNTIME_FAILURE; |
| } |
| |
| if (payload_blob.blob_magic != BLOB_MAGIC) |
| { |
| CPRINTS(ctx, "%s: BLOB magic bytes read from payload(%x) are invalid\n", |
| __FUNCTION__, payload_blob.blob_magic); |
| return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR; |
| } |
| |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| // Find offset of Image MAUV data in payload BLOB inside the image descriptor |
| // |
| // @param[in] ctx context which describes the image and holds opaque private |
| // data for the user of the library |
| // @param[in] intf function pointers which interface to the current system |
| // and environment |
| // @param[in] offset_after_payload_blob_magic Absolute offset of data after |
| // payload BLOB magic bytes in image |
| // descriptor |
| // @param[in] payload_blob_size Size of payload BLOB as per its image |
| // descriptor |
| // @param[out] payload_image_mauv_data_offset Absolute offset of Image MAUV |
| // data in payload's image |
| // descriptor |
| // @param[out] payload_image_mauv_data_size Size of Image MAUV data embedded in |
| // payload's image descriptor |
| // |
| // @return `failure_reason` |
| static failure_reason find_image_mauv_data_offset_in_payload( |
| const struct libcr51sign_ctx* const ctx, |
| const struct libcr51sign_intf* const intf, |
| const uint32_t offset_after_payload_blob_magic, |
| const uint32_t payload_blob_size, |
| uint32_t* const restrict payload_image_mauv_data_offset, |
| uint32_t* const restrict payload_image_mauv_data_size) |
| { |
| struct blob_data payload_blob_data = {0}; |
| int irv = 0; |
| bool found_image_mauv_data = false; |
| |
| if (!intf->read) |
| { |
| CPRINTS(ctx, "%s: Missing interface intf->read\n", __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_INTERFACE; |
| } |
| for (uint32_t current_offset = offset_after_payload_blob_magic; |
| current_offset <= offset_after_payload_blob_magic + payload_blob_size - |
| sizeof(struct blob_data); |
| /* increment based on each blob_data's size in loop */) |
| { |
| irv = intf->read(ctx, current_offset, sizeof(struct blob_data), |
| (uint8_t*)&payload_blob_data); |
| if (irv != LIBCR51SIGN_SUCCESS) |
| { |
| CPRINTS(ctx, "%s: Could not read BLOB data at offset %x\n", |
| __FUNCTION__, current_offset); |
| return LIBCR51SIGN_ERROR_RUNTIME_FAILURE; |
| } |
| |
| if ((current_offset - offset_after_payload_blob_magic) + |
| sizeof(struct blob_data) + payload_blob_data.blob_payload_size > |
| payload_blob_size) |
| { |
| CPRINTS( |
| ctx, |
| "%s: BLOB payload size crosses threshold expected by blob_size " |
| "in image descriptor", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR; |
| } |
| |
| switch (payload_blob_data.blob_type_magic) |
| { |
| case BLOB_TYPE_MAGIC_MAUV: |
| if (!found_image_mauv_data) |
| { |
| *payload_image_mauv_data_offset = current_offset + |
| sizeof(struct blob_data); |
| *payload_image_mauv_data_size = |
| payload_blob_data.blob_payload_size; |
| found_image_mauv_data = true; |
| /* intentional fall-through to increment current offset */ |
| } |
| else |
| { |
| /* There should be only one Image MAUV in a valid image |
| * descriptor */ |
| CPRINTS( |
| ctx, |
| "%s: Found multiple Image MAUV BLOB instances in payload\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR; |
| } |
| default: |
| current_offset += sizeof(struct blob_data) + |
| payload_blob_data.blob_payload_size; |
| /* Increment offset to keep the expected alignment */ |
| current_offset = |
| ((current_offset - 1) & ~(BLOB_DATA_ALIGNMENT - 1)) + |
| BLOB_DATA_ALIGNMENT; |
| break; |
| } |
| } |
| if (!found_image_mauv_data) |
| { |
| CPRINTS(ctx, "%s: Did not find Image MAUV BLOB inside payload\n", |
| __FUNCTION__); |
| } |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| // Read Image MAUV data from BLOB inside payload's image descriptor |
| // |
| // @param[in] ctx context which describes the image and holds opaque private |
| // data for the user of the library |
| // @param[in] intf function pointers which interface to the current system |
| // and environment |
| // @param[in] payload_image_mauv_data_offset Absolute offset of Image MAUV data |
| // in payload's image descriptor |
| // @param[in] payload_image_mauv_data_size Size of Image MAUV data embedded in |
| // payload's image descriptor |
| // @param[out] payload_image_mauv_data_buffer Buffer to store Image MAUV data |
| // read from payload's image |
| // descriptor |
| // |
| // @return `failure_reason` |
| static failure_reason read_image_mauv_data_from_payload( |
| const struct libcr51sign_ctx* const ctx, |
| const struct libcr51sign_intf* const intf, |
| const uint32_t payload_image_mauv_data_offset, |
| const uint32_t payload_image_mauv_data_size, |
| struct image_mauv* const restrict payload_image_mauv_data_buffer) |
| { |
| int irv = 0; |
| |
| if (!intf->read) |
| { |
| CPRINTS(ctx, "%s: Missing interface intf->read\n", __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_INTERFACE; |
| } |
| |
| if (payload_image_mauv_data_size > IMAGE_MAUV_DATA_MAX_SIZE) |
| { |
| CPRINTS( |
| ctx, |
| "%s: Payload Image MAUV data size (0x%x) is more than supported " |
| "maximum size\n", |
| __FUNCTION__, payload_image_mauv_data_size); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| irv = intf->read(ctx, payload_image_mauv_data_offset, |
| payload_image_mauv_data_size, |
| (uint8_t*)payload_image_mauv_data_buffer); |
| if (irv != LIBCR51SIGN_SUCCESS) |
| { |
| CPRINTS(ctx, |
| "%s: Could not read Image MAUV data from payload @ offset 0x%x " |
| "size 0x%x\n", |
| __FUNCTION__, payload_image_mauv_data_offset, |
| payload_image_mauv_data_size); |
| return LIBCR51SIGN_ERROR_RUNTIME_FAILURE; |
| } |
| |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| // Check if Image MAUV allows update to a target payload version |
| // |
| // @param[in] stored_image_mauv_data Image MAUV data stored in system |
| // @param[in] new_payload_security_version Payload version |
| // |
| // @return `bool` `True` if update to target version is allowed by MAUV data |
| static bool does_stored_image_mauv_allow_update( |
| const struct image_mauv* const stored_image_mauv_data, |
| const uint64_t new_payload_security_version) |
| { |
| if (new_payload_security_version < |
| stored_image_mauv_data->minimum_acceptable_update_version) |
| { |
| return false; |
| } |
| |
| for (uint32_t i = 0; |
| i < stored_image_mauv_data->version_denylist_num_entries; i++) |
| { |
| if (stored_image_mauv_data->version_denylist[i] == |
| new_payload_security_version) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Do a sanity check for values stored in Image MAUV data |
| // |
| // @param[in] image_mauv_data Image MAUV data |
| // @param[in] image_mauv_data_size Size of Image MAUV data in bytes |
| // |
| // @return `failure_reason` |
| static failure_reason sanity_check_image_mauv_data( |
| const struct image_mauv* const restrict image_mauv_data, |
| const uint32_t image_mauv_data_size) |
| { |
| uint32_t expected_image_mauv_data_size = 0; |
| |
| if (image_mauv_data_size < sizeof(struct image_mauv)) |
| { |
| CPRINTS(ctx, "%s: Image MAUV data size is smaller than expected\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| if (image_mauv_data->mauv_struct_version != IMAGE_MAUV_STRUCT_VERSION) |
| { |
| CPRINTS(ctx, "%s: Unexpected Image MAUV version\n", __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| if (image_mauv_data->payload_security_version == 0) |
| { |
| // Handle trivial case of someone not initializing MAUV properly |
| CPRINTS(ctx, "%s: Payload security version should be greater than 0\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| if (image_mauv_data->version_denylist_num_entries > |
| IMAGE_MAUV_MAX_DENYLIST_ENTRIES) |
| { |
| CPRINTS( |
| ctx, |
| "%s: Version denylist entries in Image MAUV exceed maximum count\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| expected_image_mauv_data_size = |
| sizeof(struct image_mauv) + |
| image_mauv_data->version_denylist_num_entries * |
| MEMBER_SIZE(struct image_mauv, version_denylist[0]); |
| |
| if (image_mauv_data_size != expected_image_mauv_data_size) |
| { |
| CPRINTS(ctx, |
| "%s: Size of Image MAUV data (0x%x) is different than expected " |
| "size (0x%x)\n", |
| __FUNCTION__, image_mauv_data_size, |
| expected_image_mauv_data_size); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| if (!does_stored_image_mauv_allow_update( |
| image_mauv_data, image_mauv_data->payload_security_version)) |
| { |
| CPRINTS(ctx, |
| "%s: Image MAUV does not allow update to the payload it was " |
| "contained in\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| // Find and read (if found) Image MAUV from payload's image descriptor |
| // |
| // @param[in] ctx context which describes the image and holds opaque private |
| // data for the user of the library |
| // @param[in] intf function pointers which interface to the current system |
| // and environment |
| // @param[in] payload_blob_offset Absolute offset of payload BLOB data in |
| // payload's image descriptor |
| // @param[in] payload_blob_size Size of payload BLOB data as per payload's |
| // image descriptor |
| // @param[out] payload_image_mauv_data_buffer Buffer to store Image MAUV data |
| // read from payload's image |
| // descriptor |
| // @param[out] payload_image_mauv_data_size Size of Image MAUV data (in bytes) |
| // read from payload's image |
| // descriptor |
| // @param[out] payload_contains_image_mauv_data Flag to indicate whether Image |
| // MAUV data is present in |
| // payload's image descriptor |
| // |
| // @return `failure_reason` |
| failure_reason find_and_read_image_mauv_data_from_payload( |
| const struct libcr51sign_ctx* const ctx, |
| const struct libcr51sign_intf* const intf, |
| const uint32_t payload_blob_offset, const uint32_t payload_blob_size, |
| uint8_t payload_image_mauv_data_buffer[], |
| uint32_t* const restrict payload_image_mauv_data_size, |
| bool* const restrict payload_contains_image_mauv_data) |
| { |
| failure_reason rv = LIBCR51SIGN_SUCCESS; |
| uint32_t payload_image_mauv_data_offset = 0; |
| |
| rv = verify_payload_blob_magic(ctx, intf, payload_blob_offset); |
| if (rv != LIBCR51SIGN_SUCCESS) |
| { |
| return rv; |
| } |
| |
| rv = find_image_mauv_data_offset_in_payload( |
| ctx, intf, payload_blob_offset + offsetof(struct blob, blobs), |
| payload_blob_size, &payload_image_mauv_data_offset, |
| payload_image_mauv_data_size); |
| if (rv != LIBCR51SIGN_SUCCESS) |
| { |
| return rv; |
| } |
| |
| *payload_contains_image_mauv_data = (payload_image_mauv_data_offset != 0); |
| |
| if (*payload_contains_image_mauv_data) |
| { |
| rv = read_image_mauv_data_from_payload( |
| ctx, intf, payload_image_mauv_data_offset, |
| *payload_image_mauv_data_size, |
| (struct image_mauv*)payload_image_mauv_data_buffer); |
| if (rv != LIBCR51SIGN_SUCCESS) |
| { |
| return rv; |
| } |
| |
| return sanity_check_image_mauv_data( |
| (struct image_mauv*)payload_image_mauv_data_buffer, |
| *payload_image_mauv_data_size); |
| } |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| // Replace stored Image MAUV data with payload Image MAUV data |
| // |
| // @param[in] ctx context which describes the image and holds opaque private |
| // data for the user of the library |
| // @param[in] intf function pointers which interface to the current system |
| // and environment |
| // @param[in] payload_image_mauv_data Image MAUV data from payload |
| // @param[in] payload_image_mauv_data_size Size of Image MAUV data (in bytes) |
| // embedded inside payload |
| // |
| // @return `failure_reason` |
| static failure_reason update_stored_image_mauv_data( |
| const struct libcr51sign_ctx* const ctx, |
| const struct libcr51sign_intf* const intf, |
| const struct image_mauv* const restrict payload_image_mauv_data, |
| const uint32_t payload_image_mauv_data_size) |
| { |
| int irv = 0; |
| |
| if (!intf->store_new_image_mauv_data) |
| { |
| CPRINTS(ctx, "%s: Missing interface intf->store_new_image_mauv_data\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_INTERFACE; |
| } |
| |
| irv = intf->store_new_image_mauv_data( |
| ctx, (uint8_t*)payload_image_mauv_data, payload_image_mauv_data_size); |
| if (irv != LIBCR51SIGN_SUCCESS) |
| { |
| CPRINTS(ctx, |
| "%s: Could not store new Image MAUV data from the payload\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_STORING_NEW_IMAGE_MAUV_DATA; |
| } |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| // Validates Image MAUV from payload against stored Image MAUV (if present) |
| // |
| // @param[in] ctx context which describes the image and holds opaque private |
| // data for the user of the library |
| // @param[in] intf function pointers which interface to the current system |
| // and environment |
| // @param[in] payload_blob_offset Absolute offset of BLOB data embedded in |
| // image descriptor. `0` if BLOB data is not |
| // present in image descriptor |
| // @param[in] payload_blob_size Size of BLOB data from `blob_size` field in |
| // image descriptor |
| // |
| // @return `failure_reason` |
| failure_reason |
| validate_payload_image_mauv(const struct libcr51sign_ctx* const ctx, |
| const struct libcr51sign_intf* const intf, |
| const uint32_t payload_blob_offset, |
| const uint32_t payload_blob_size) |
| { |
| uint32_t payload_image_mauv_data_size = 0; |
| struct full_mauv payload_image_mauv_data_buffer = {0}; |
| |
| uint32_t stored_image_mauv_data_size = 0; |
| struct full_mauv stored_image_mauv_data_buffer = {0}; |
| |
| bool payload_contains_image_mauv_data = false; |
| |
| failure_reason rv = LIBCR51SIGN_SUCCESS; |
| int irv = 0; |
| |
| bool payload_blob_present = (payload_blob_offset != 0); |
| if (payload_blob_present) |
| { |
| rv = find_and_read_image_mauv_data_from_payload( |
| ctx, intf, payload_blob_offset, payload_blob_size, |
| (uint8_t*)&payload_image_mauv_data_buffer, |
| &payload_image_mauv_data_size, &payload_contains_image_mauv_data); |
| if (rv != LIBCR51SIGN_SUCCESS) |
| { |
| return rv; |
| } |
| } |
| |
| if (!intf->retrieve_stored_image_mauv_data) |
| { |
| if (payload_contains_image_mauv_data) |
| { |
| CPRINTS( |
| ctx, |
| "%s: Payload contains Image MAUV data but required interface " |
| "intf->retrieve_stored_image_mauv_data is missing\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_INVALID_INTERFACE; |
| } |
| CPRINTS( |
| ctx, |
| "%s: Payload does not contain Image MAUV data and interface " |
| "intf->retrieve_stored_image_mauv_data is missing. Skipping MAUV " |
| "check for backward compatibility.\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| irv = intf->retrieve_stored_image_mauv_data( |
| ctx, (uint8_t*)&stored_image_mauv_data_buffer, |
| &stored_image_mauv_data_size, IMAGE_MAUV_DATA_MAX_SIZE); |
| if (irv == LIBCR51SIGN_NO_STORED_MAUV_FOUND) |
| { |
| CPRINTS( |
| ctx, |
| "%s: Stored Image MAUV data not present in the system. Skipping Image " |
| "MAUV check\n", |
| __FUNCTION__); |
| if (payload_contains_image_mauv_data) |
| { |
| return update_stored_image_mauv_data( |
| ctx, intf, (struct image_mauv*)&payload_image_mauv_data_buffer, |
| payload_image_mauv_data_size); |
| } |
| return LIBCR51SIGN_SUCCESS; |
| } |
| if (irv != LIBCR51SIGN_SUCCESS) |
| { |
| CPRINTS(ctx, "%s: Could not retrieve Image MAUV stored in system\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_RETRIEVING_STORED_IMAGE_MAUV_DATA; |
| } |
| if (stored_image_mauv_data_size > IMAGE_MAUV_DATA_MAX_SIZE) |
| { |
| CPRINTS(ctx, |
| "%s: Stored Image MAUV data size (0x%x) is more than supported " |
| "maximum size\n", |
| __FUNCTION__, stored_image_mauv_data_size); |
| return LIBCR51SIGN_ERROR_INVALID_IMAGE_MAUV_DATA; |
| } |
| |
| rv = sanity_check_image_mauv_data( |
| (struct image_mauv*)&stored_image_mauv_data_buffer, |
| stored_image_mauv_data_size); |
| if (rv != LIBCR51SIGN_SUCCESS) |
| { |
| return rv; |
| } |
| |
| if (!payload_contains_image_mauv_data) |
| { |
| CPRINTS(ctx, "%s: Image MAUV expected to be present in payload", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_STORED_IMAGE_MAUV_EXPECTS_PAYLOAD_IMAGE_MAUV; |
| } |
| |
| if (!does_stored_image_mauv_allow_update( |
| (struct image_mauv*)&stored_image_mauv_data_buffer, |
| ((struct image_mauv*)&payload_image_mauv_data_buffer) |
| ->payload_security_version)) |
| { |
| CPRINTS(ctx, |
| "%s: Stored Image MAUV data does not allow update to payload " |
| "version\n", |
| __FUNCTION__); |
| return LIBCR51SIGN_ERROR_STORED_IMAGE_MAUV_DOES_NOT_ALLOW_UPDATE_TO_PAYLOAD; |
| } |
| |
| if (((struct image_mauv*)&payload_image_mauv_data_buffer) |
| ->mauv_update_timestamp > |
| ((struct image_mauv*)&stored_image_mauv_data_buffer) |
| ->mauv_update_timestamp) |
| { |
| return update_stored_image_mauv_data( |
| ctx, intf, (struct image_mauv*)&payload_image_mauv_data_buffer, |
| payload_image_mauv_data_size); |
| } |
| return LIBCR51SIGN_SUCCESS; |
| } |
| |
| #ifdef __cplusplus |
| } // extern "C" |
| #endif |