STMicroelectronics / STM32CubeU5

Full Firmware Package for the STM32U5 series: HAL+LL drivers, CMSIS, BSP, MW, plus a set of Projects (examples and demos) running on all boards provided by ST (Nucleo, Evaluation and Discovery Kits).
Other
116 stars 61 forks source link

CRYP Key & IV configuration does not respect configured DataType #42

Closed fermentedfly closed 3 months ago

fermentedfly commented 4 months ago

stm32u5xx_hal_cryp

We use AES in byte mode (CRYP_DATATYPE_8B).

CRYP_ConfigTypeDef provides the option to set the DataType. This works fine for the data fed into AES but KEY & IV are passed in in mode CRYP_DATATYPE_32B regardless of the setup of DataType.

see static void CRYP_SetIV(CRYP_HandleTypeDef *hcryp) & static void CRYP_SetKey(CRYP_HandleTypeDef *hcryp, uint32_t KeySize).

This is especially problematic since key & iv are not passed to the hardware registers during HAL_CRYP_Init or HAL_CRYP_SetConfig but when calling HAL_CRYP_Encrypt which means we can't use local (stack) variables to correct the data alignment ourselves.

Please update CRYP_SetIV & CRYP_SetKey so they can deal with all modes of DataType.

Thanks, Manuel

RJMSTM commented 4 months ago

Hello @fermentedfly,

Thank you for reporting. Would you please clarify your problem do you want to have the ability to changes DataType into the Key and initialization vector.

Otherwise, you should first call HAL_CRYP_SetConfigthen HAL_CRYP_Initand then HAL_CRYP_Encrypt

Regards,

fermentedfly commented 4 months ago

I've attached a demo. testCBCNoSwap() passes key, IV, and data in big-endian, with DataType set to CRYP_NO_SWAP. This is similar to some examples in STM32Cube and works.

testCBCByteSwap() passes key, IV, and data in little-endian, with DataType set to CRYP_BYTE_SWAP. This fails. I've had a look into stm32u5xx_hal_cryp.c, the cause for the failure is that key and IV are always interpreted as being in big-endian, regardless of DataType.

testCBCByteSwapKeyIVBigEndian() passes key and IV in big-endian and data in little-endian, with DataType set to CRYP_BYTE_SWAP. This works.

Herein lies the problem: There is no way to pass key and IV in little-endian, which is an issue for my application.

void testCBCNoSwap()
{
    __HAL_RCC_AES_CLK_ENABLE();

    // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip
    // CBCMMT128.rsp Encrypt Count 0
    // CRYP_NO_SWAP

    uint32_t key[] = {0x1f8e4973, 0x953f3fb0, 0xbd6b1666, 0x2e9a3c17};
    static_assert(sizeof(key) == 16);
    uint32_t iv[] = {0x2fe2b333, 0xceda8f98, 0xf4a99b40, 0xd2cd34a8};
    static_assert(sizeof(iv) == 16);
    uint32_t pt[] = {0x45cf1296, 0x4fc824ab, 0x76616ae2, 0xf4bf0822};
    static_assert(sizeof(pt) == 16);
    uint32_t ctRef[] = {0x0f61c4d4, 0x4c5147c0, 0x3c195ad7, 0xe2cc12b2};
    static_assert(sizeof(ctRef) == 16);
    decltype(ctRef) ct{};

    CRYP_HandleTypeDef handle{.Instance = AES,
                              .Init     = {
                                      .DataType = CRYP_NO_SWAP,
                                      .KeySize  = CRYP_KEYSIZE_128B,

                                      .pKey          = (uint32_t *)key,
                                      .pInitVect     = (uint32_t *)iv,
                                      .Algorithm     = CRYP_AES_CBC,
                                      .DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD,
                              }};

    configASSERT(HAL_CRYP_Init(&handle) == HAL_OK);
    configASSERT(HAL_CRYP_Encrypt(&handle, (uint32_t *)pt, std::extent_v<decltype(pt)>, (uint32_t *)ct, 1000)
                 == HAL_OK);
    configASSERT(std::equal(ct, ct + std::extent_v<decltype(ct)>, ctRef) == true);

    __HAL_RCC_AES_CLK_DISABLE();
}
void testCBCByteSwapKeyIVBigEndian()
{
    __HAL_RCC_AES_CLK_ENABLE();

    // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip
    // CBCMMT128.rsp Encrypt Count 0
    // CRYP_BYTE_SWAP

    uint32_t key[] = {0x1f8e4973, 0x953f3fb0, 0xbd6b1666, 0x2e9a3c17};
    static_assert(sizeof(key) == 16);
    uint32_t iv[] = {0x2fe2b333, 0xceda8f98, 0xf4a99b40, 0xd2cd34a8};
    static_assert(sizeof(iv) == 16);
    uint8_t pt[] = {0x45, 0xcf, 0x12, 0x96, 0x4f, 0xc8, 0x24, 0xab, 0x76, 0x61, 0x6a, 0xe2, 0xf4, 0xbf, 0x08, 0x22};
    static_assert(sizeof(pt) == 16);
    uint8_t ctRef[] = {0x0f, 0x61, 0xc4, 0xd4, 0x4c, 0x51, 0x47, 0xc0, 0x3c, 0x19, 0x5a, 0xd7, 0xe2, 0xcc, 0x12, 0xb2};
    static_assert(sizeof(ctRef) == 16);
    decltype(ctRef) ct{};

    CRYP_HandleTypeDef handle{.Instance = AES,
                              .Init     = {
                                      .DataType = CRYP_BYTE_SWAP,
                                      .KeySize  = CRYP_KEYSIZE_128B,

                                      .pKey          = (uint32_t *)key,
                                      .pInitVect     = (uint32_t *)iv,
                                      .Algorithm     = CRYP_AES_CBC,
                                      .DataWidthUnit = CRYP_DATAWIDTHUNIT_BYTE,
                              }};

    configASSERT(HAL_CRYP_Init(&handle) == HAL_OK);
    configASSERT(HAL_CRYP_Encrypt(&handle, (uint32_t *)pt, std::extent_v<decltype(pt)>, (uint32_t *)ct, 1000)
                 == HAL_OK);
    configASSERT(std::equal(ct, ct + std::extent_v<decltype(ct)>, ctRef) == true);

    __HAL_RCC_AES_CLK_DISABLE();
}
void testCBCByteSwap()
{
    __HAL_RCC_AES_CLK_ENABLE();

    // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip
    // CBCMMT128.rsp Encrypt Count 0
    // CRYP_BYTE_SWAP

    uint8_t key[] = {0x1f, 0x8e, 0x49, 0x73, 0x95, 0x3f, 0x3f, 0xb0, 0xbd, 0x6b, 0x16, 0x66, 0x2e, 0x9a, 0x3c, 0x17};
    static_assert(sizeof(key) == 16);
    uint8_t iv[] = {0x2f, 0xe2, 0xb3, 0x33, 0xce, 0xda, 0x8f, 0x98, 0xf4, 0xa9, 0x9b, 0x40, 0xd2, 0xcd, 0x34, 0xa8};
    static_assert(sizeof(iv) == 16);
    uint8_t pt[] = {0x45, 0xcf, 0x12, 0x96, 0x4f, 0xc8, 0x24, 0xab, 0x76, 0x61, 0x6a, 0xe2, 0xf4, 0xbf, 0x08, 0x22};
    static_assert(sizeof(pt) == 16);
    uint8_t ctRef[] = {0x0f, 0x61, 0xc4, 0xd4, 0x4c, 0x51, 0x47, 0xc0, 0x3c, 0x19, 0x5a, 0xd7, 0xe2, 0xcc, 0x12, 0xb2};
    static_assert(sizeof(ctRef) == 16);
    decltype(ctRef) ct{};

    CRYP_HandleTypeDef handle{.Instance = AES,
                              .Init     = {
                                      .DataType = CRYP_BYTE_SWAP,
                                      .KeySize  = CRYP_KEYSIZE_128B,

                                      .pKey          = (uint32_t *)key,
                                      .pInitVect     = (uint32_t *)iv,
                                      .Algorithm     = CRYP_AES_CBC,
                                      .DataWidthUnit = CRYP_DATAWIDTHUNIT_BYTE,
                              }};

    configASSERT(HAL_CRYP_Init(&handle) == HAL_OK);
    configASSERT(HAL_CRYP_Encrypt(&handle, (uint32_t *)pt, std::extent_v<decltype(pt)>, (uint32_t *)ct, 1000)
                 == HAL_OK);
    configASSERT(std::equal(ct, ct + std::extent_v<decltype(ct)>, ctRef) == true); // FAILS

    __HAL_RCC_AES_CLK_DISABLE();
}
RJMSTM commented 3 months ago

Hello,

In fact, the key and IV cannot be transmitted in little-endian format; they must always be sent in a one direction.

Regards,

fermentedfly commented 3 months ago

Correct, This was your design decision. Thus the request to add the option to feed key & IV in little-endian.

RJMSTM commented 3 months ago

Hello @fermentedfly,

I apologize, but there is no support planned for the 'little-endian' format for key and IV.

Please allow me to close the issue, thanks again for your contribution

Regards, Rania