Skip to content

Max pBuffer size for stse_data_storage_update_data_zone() unclear due to hidden overhead bytes #84

@parmi93

Description

@parmi93

I need to write a 639-byte DER certificate into the data partition (NVM zone index 1) of the STSAFE-A110, which has 700 bytes available. I cannot write all 639 bytes in a single call because we configured our Platform Abstraction Layer to use a buffer of STSAFEA_MAX_FRAME_LENGTH_A110 + 3 (507 + 3 = 510) bytes for I2C communication. So I considered implementing a function that writes the certificate by sending it in chunks of up to STSAFEA_MAX_FRAME_LENGTH_A110 (507) bytes via multiple calls to stse_data_storage_update_data_zone().

The problem is that if I try to write a chunk of 507 bytes, STSE_SERVICE_FRAME_SIZE_ERROR is immediately returned because the following condition evaluates to true (507 >= 507). I’m wondering why >= is used instead of simply >, is that a BUG?:

if (data_length >= stsafea_maximum_frame_length[pSTSE->device_type]) {
return STSE_SERVICE_FRAME_SIZE_ERROR;
}

In any case, I reduced the maximum chunk size to STSAFEA_MAX_FRAME_LENGTH_A110 - 1 (506), but then the failure occurs later due to stse_platform_i2c_send_start(), which returns STSE_PLATFORM_BUFFER_ERR, because frame_length is 513 bytes:

stse_ReturnCode_t stse_platform_i2c_send_start(PLAT_UI8 bus_id, PLAT_UI8 dev_addr, PLAT_UI16 speed,
                                               PLAT_UI16 frame_length)
{
    /* Check buffer overflow */
    if (esc_stse_i2c[bus_id].tx_rx_buffer_length < frame_length) // 510 < 513
    {
        return STSE_PLATFORM_BUFFER_ERR;
    }

    esc_stse_i2c[bus_id].tx_rx_frame_length = frame_length;
    esc_stse_i2c[bus_id].tx_rx_frame_offset = 0;

    return STSE_OK;
}

Note that esc_stse_i2c[bus_id].tx_rx_buffer_length is set by us to STSAFEA_MAX_FRAME_LENGTH_A110 + 3 (507 + 3 = 510).

I would have expected the frame_length argument to be 509 (506 + 3), since here (#53 (comment)) @TofMassilia13320 mentioned that setting the I2C platform buffer to the maximum value STSAFEA_MAX_FRAME_LENGTH_Axxx + 3 bytes ensures full command coverage of the selected STSAFE-A without overflow at the I2C platform layer.
This led me to assume that STSELib would invoke stse_platform_i2c_send_start() with a frame_length argument that could reach at most STSAFEA_MAX_FRAME_LENGTH_Axxx + 3 (510 in my case).
In other words, I understood that STSELib adds at most 3 bytes to the user data (2 bytes for the response length + 1 byte for the command or response header), as mentioned by @nils-cercariolo-st here: #53 (comment). However, this does not seem to hold true, as in my case frame_length is actually 513.

Digging into STSELib, I concluded that the extra 7 bytes are added by stsafea_update_data_zone() and stsafea_frame_transmit(). Specifically:

  • stsafea_update_data_zone() adds 5 bytes at the beginning of the I2C frame: 1 byte header, 1 byte zone access option, 1 byte zone index, and 2 bytes offset:

    stse_frame_allocate(CmdFrame);
    stse_frame_element_allocate_push(&CmdFrame, eCmdHeader, STSAFEA_HEADER_SIZE, &cmd_header);
    stse_frame_element_allocate_push(&CmdFrame, eOption, STSAFEA_ZONE_ACCESS_OPTION_SIZE, (PLAT_UI8 *)&option);
    stse_frame_element_allocate_push(&CmdFrame, eZoneIndex, STSAFEA_ZONE_INDEX_SIZE, (PLAT_UI8 *)&zone_index);
    stse_frame_element_allocate_push(&CmdFrame, eOffset, STSAFEA_ZONE_OFFSET_SIZE, (PLAT_UI8 *)&offset);
    stse_frame_element_allocate_push(&CmdFrame, eData, data_length, pData);

  • stsafea_frame_transmit() adds 2 bytes of CRC at the end of the I2C frame:

    stse_frame_element_allocate(crc_element, STSE_FRAME_CRC_SIZE, crc);
    stse_frame_push_element(pFrame, &crc_element);


So the problem is: how can I determine the maximum length of the buffer pBuffer that I am allowed to pass to stse_data_storage_update_data_zone()?

/*!
* \brief Update one memory zone of the STSE device
* \param[in] pSTSE Pointer to target STSE handler
* \param[in] zone Target STSE zone index
* \param[in] offset Update offset
* \param[in] pBuffer Pointer to applicative read buffer
* \param[in] length Update length in byte
* \param[in] atomicity \ref stse_zone_update_atomicity_t atomicity of the update access
* \param[in] protection \ref stse_cmd_protection_t command response protection indicator
* \return \ref STSE_OK on success ; \ref stse_ReturnCode_t error code otherwise
* \note - A target STSE handler must be initialized using the \ref stse_init routine prior to execute this API function
* \note - If command response protection is required an active session between Host/Companion and STSE must be open
* \note - The device may enforce a monotonic policy on zone's access condition (stse_zone_ac_t enum):
* Once set to a more restrictive condition (e.g. STSE_AC_ALWAYS -> STSE_AC_HOST -> STSE_AC_AUTH_AND_HOST),
* it's not possible to revert to a less restrictive one (e.g. STSE_AC_HOST -> STSE_AC_ALWAYS).
* \details \include{doc} stse_data_storage_update_zone.dox
*/
stse_ReturnCode_t stse_data_storage_update_data_zone(
stse_Handler_t *pSTSE,
PLAT_UI32 zone,
PLAT_UI16 offset,
PLAT_UI8 *pBuffer,
PLAT_UI16 length,
stse_zone_update_atomicity_t atomicity,
stse_cmd_protection_t protection);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions