2025-12-18 7:27 PM
Hello, I am trying to use SAES with a wrapped key to decrypt firmware updates on an stm32u5g9vjtxq. I am not sure how I should be changing the InitVect in CRYP_AES_CBC mode after hcryp is already initialized with HAL_CRYP_Init. To elaborate on what I am trying to do I have created a wrapped key that is being stored in OTP memory along with the IV used to encrypt / decrypt that wrapped key. My bootloader calls HAL_CRYPEx_UnwrapKey using the OTP memory address of the wrapped key and the IV also stored in OTP. I then want to change the IV to match that of what was used to encrypt the new firmware image, the unwrapped key is what was used to encrypt the firmware image. I am not sure how to accomplish this without having to call HAL_CRYP_Init again...I think the second call to HAL_CRYP_Init looses the unwrapped key and replaces it with whatever is defined in hcryp.Init.KeySelect which I have set to CRYP_KEYSEL_HW.
To summarize I need to be able to change the InitVect without affecting the key I have unwrapped, My understanding of how all of this is designed to work may be flawed.
My code to initially write the wrapped key and IV used to OTP
uint32_t AESKey256[8] = { MY 256 BIT KEY };
uint32_t pInitVectSAES[4] = { MY IV USED TO WRAP / UNWRAP KEY }
uint32_t Encryptedkey[8];
HAL_CRYPEx_WrapKey(&hcryp, AESKey256, Encryptedkey, HAL_MAX_DELAY)
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, key_addr, (uint32_t)Encryptedkey);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, key_addr + 0x10, (uint32_t)(Encryptedkey + 4) );
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, iv_addr, (uint32_t)(pInitVectSAES) );
My bootloader init code
static void MX_SAES_AES_Init(void) {
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_NO_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.pInitVect = (uint32_t *)pInitVectSAES; // Initalized to 0x00000000...
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
if (HAL_CRYP_Init(&hcryp) != HAL_OK)
{
Error_Handler();
}
}
I update the InitVect pointer to what I have written in OTP and use it to unwrap the key that is also in OTP
uintptr_t key_addr = 0x0BFA00D0;
uint32_t iv_addr = 0x0BFA00F0;
hcryp.Init.pInitVect = (uint32_t*)iv_addr;
HAL_CRYP_DeInit(&hcryp);
HAL_CRYP_Init(&hcryp);
HAL_CRYPEx_UnwrapKey(&hcryp, (uint32_t*)key_addr, HAL_MAX_DELAY);
Up to this point everything works, but I now need to change the InitVect to what was used to encrypt the firmware. I have tried a couple things
// I believe calling Init with the new InitVect pointer looses the unwrapped key
hcryp.Init.pInitVect = (uint32_t*)fw_header.iv;
HAL_CRYP_Init(&hcryp);
// This seems like it should work without having to call Init but the decrypted firmware does not match the original.
hcryp.Instance->IVR3 = *(uint32_t *)(fw_header.iv);
hcryp.Instance->IVR2 = *(uint32_t *)(fw_header.iv + 1U);
hcryp.Instance->IVR1 = *(uint32_t *)(fw_header.iv + 2U);
hcryp.Instance->IVR0 = *(uint32_t *)(fw_header.iv + 3U);Solved! Go to Solution.
2026-01-02 4:39 PM
I finally got this working with the help of Jocelyn's post. I will leave my implementation down below for anyone in the future. I wrote one application that takes a key, wraps it and stores it in OTP flash. Then my bootloader application is able to read the wrapped key out of memory and unwrap it for the firmware decryption to use.
__ALIGN_BEGIN static const uint32_t AESKey256[8] = { private key used to encrypt firmware };
__ALIGN_BEGIN static const uint32_t IV_for_key[4] __ALIGN_END = { IV used to wrap key };
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_BYTE_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_BYTE;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
hcryp.Init.pKey = (uint32_t*)AESKey256;
hcryp.Init.pInitVect = (uint32_t*)IV_for_key;
HAL_CRYP_Init(&hcryp);
HAL_CRYPEx_WrapKey(&hcryp, (uint32_t*)AESKey256, Encryptedkey, HAL_MAX_DELAY);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, PRIVATE_KEY_ADDR, (uint32_t)(uintptr_t)&Encryptedkey[0]);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, (PRIVATE_KEY_ADDR + 0x10), (uint32_t)(uintptr_t)&Encryptedkey[4]);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, PRIVATE_KEY_IV_ADDR, (uint32_t)(uintptr_t)&IV_for_key[0]);
Then in my bootloader...
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_BYTE_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
hcryp.Init.pKey = (uint32_t*)PRIVATE_KEY_ADDR;
hcryp.Init.pInitVect = (uint32_t*)PRIVATE_KEY_IV_ADDR;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
HAL_CRYP_Init(&hcryp);
HAL_CRYPEx_UnwrapKey(&hcryp, (uint32_t*)PRIVATE_KEY_ADDR, HAL_MAX_DELAY);
// Set IV to value used to encrypt the firmware.
hcryp.Init.pInitVect = (uint32_t*)fw_header.iv;
// loop through your encrypted firmware
HAL_CRYP_Decrypt(&hcryp, (uint32_t*)encrypted_buffer, WORDS, (uint32_t*)decrypted_buffer, HAL_MAX_DELAY);2025-12-19 10:25 AM
Hello @brohr01,
You have an example of key wrapping in the CubeU5.
The HAL_CRYP_Init is called only once.
So, I guess that removing this call, between the unwrap and the decrypt should do the job.
You can write the iv directly in registers as you did.
One thing I would first check (if not already done) is a decrypt using directly your key, to check basic decryption work without involving the wrapping.
Best regards
Jocelyn
2025-12-20 3:36 PM
Thank you for your response, I would like to think I am heading in the correct direction but I am still having trouble getting this to work. I have started over and I now have a few projects that I am using to test with.
The first project I am encrypting some data in "KeyMode = normal", "KeySelect = normal" with the init vector and key that is hard-coded into the project and writing that encrypted data(16 words) to flash. This works correctly and the data written to flash is verified to be correct when compared with a python script that is encrypting the same data with the same key and IV.
static void MX_SAES_AES_Init(void) {
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_NO_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.pKey = (uint32_t *)pKeySAES;
hcryp.Init.pInitVect = (uint32_t *)pInitVectSAES;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyMode = CRYP_KEYMODE_NORMAL;
hcryp.Init.KeySelect = CRYP_KEYSEL_NORMAL;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
HAL_CRYP_Init(&hcryp);
}
....
__ALIGN_BEGIN static const uint32_t pKey[8] __ALIGN_END = {
0x75164217, 0x5de1f8a0, 0xacafe185, 0x24628107, 0x9c618087 0xddc3a6cf, 0x97b7e570, 0xff34aa83};
__ALIGN_BEGIN static const uint32_t IV_for_data[4] __ALIGN_END = {
0x165eb5a9, 0x1d1e5b1e, 0x0f2bc94a, 0x943968ca};
CRYP_ConfigTypeDef Conf={0};
HAL_CRYP_GetConfig(&hcryp, &Conf);
Conf.pInitVect = (uint32_t*)IV_for_data;
Conf.pKey = (uint32_t*)pKey;
HAL_CRYP_SetConfig(&hcryp, &Conf);
HAL_CRYP_Encrypt(&hcryp, (uint32_t*)raw_text, 16, (uint32_t*)encrypted, HAL_MAX_DELAY);
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, encrypted_addr, (uint32_t)encrypted);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, encrypted_addr + 0x10, (uint32_t)(encrypted + 4));
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, encrypted_addr + 0x20, (uint32_t)(encrypted + 8));
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, encrypted_addr + 0x30, (uint32_t)(encrypted + 12));
HAL_FLASH_Lock();
I then have another project that takes pKey and wraps it, then writes the wrapped key to flash. I am using a different IV during the wrapping process. This project uses KeyMode = wrapped and KeySelect = hardware.
static void MX_SAES_AES_Init(void) {
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_NO_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.pInitVect = (uint32_t *)pInitVectSAES;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
HAL_CRYP_Init(&hcryp);
}
__ALIGN_BEGIN static const uint32_t IV_for_key[4] __ALIGN_END = {
0x97891a15, 0x1ffec61e, 0x7f2cb94c, 0x13096f9a};
CRYP_ConfigTypeDef Conf={0};
HAL_CRYP_GetConfig(&hcryp, &Conf);
Conf.pInitVect = (uint32_t*)IV_for_key;
HAL_CRYP_SetConfig(&hcryp, &Conf);
__ALIGN_BEGIN uint32_t Encryptedkey[8] __ALIGN_END = { 0 };
if (HAL_CRYPEx_WrapKey(&hcryp, (uint32_t*)pKey, Encryptedkey, HAL_MAX_DELAY) != HAL_OK {
printf("Wrapping Key failed.\n");
}
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, wrapped_key_addr, (uint32_t)Encryptedkey);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, wrapped_key_addr + 0x10, (uint32_t)(Encryptedkey + 4));
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, wrapped_iv_addr, (uint32_t)(IV_for_key));
HAL_FLASH_Lock();
Then finally in the 3rd project I am unwrapping the key that was stored on flash by the previous project, reading the encrypted data from flash and decrypting it with the unwrapped key and the IV that was used to originally encrypt the data. This does not produce the correct decrypted data.
static void MX_SAES_AES_Init(void) {
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_NO_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.pInitVect = (uint32_t *)pInitVectSAES;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
HAL_CRYP_Init(&hcryp);
}
// Unwrap Key in KeyMode = wrapped, KeySelect = hardware
CRYP_ConfigTypeDef Conf={0};
HAL_CRYP_GetConfig(&hcryp, &Conf);
Conf.pInitVect = (uint32_t*)wrapped_iv_addr;
HAL_CRYP_SetConfig(&hcryp, &Conf);
HAL_CRYPEx_UnwrapKey(&hcryp, (uint32_t*)&wrapped_key_addr, HAL_MAX_DELAY);
// Change to keymode = normal, keyselect = normal
HAL_CRYP_GetConfig(&hcryp, &Conf);
Conf.pInitVect = (uint32_t*)IV_for_data;
Conf.KeyMode = CRYP_KEYMODE_NORMAL;
Conf.KeySelect = CRYP_KEYSEL_NORMAL;
HAL_CRYP_SetConfig(&hcryp, &Conf);
// Decrypt data stored in flash that was encrypted using "normal" key
uintptr_t encrypted_addr = 0x08014090;
HAL_CRYP_Decrypt(&hcryp, (uint32_t*)(encrypted_addr + 0), 16, (uint32_t*)(decrypted + 0), HAL_MAX_DELAY);
2025-12-22 2:05 PM
Hello @brohr01,
I made a test on STM32H573, starting from Wrap example.
Here is the code. I start from encrypting a text and display the hexdump.
Then in a second step, I wrap the key used, I reset the SAES IP, then I unwrap the key and encrypt same text, and display hexdump. In both cases I get same encryption.
I'm using CDC with IV for key to wrap the key, and CDC with IV for data to encrypt data.
So, no need to play with the SetConfig, just changing the hcryp.Init parameter is enough.
I hope this will help
Best regards
Jocelyn
/* Common parameters */
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_NO_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.HeaderWidthUnit = CRYP_HEADERWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ALWAYS;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
/* First encrypt directly with pKey and IV data in normal mode */
hcryp.Init.pKey = pKey;
hcryp.Init.pInitVect = IV_for_data;
hcryp.Init.KeyMode = CRYP_KEYMODE_NORMAL;
hcryp.Init.KeySelect = CRYP_KEYSEL_NORMAL;
if (HAL_CRYP_Init(&hcryp) != HAL_OK) {
Error_Handler();
}
printf("Encrypt plain text with normal key\n");
if (HAL_CRYP_Encrypt(&hcryp, Plaintext, PLAINTEXT_SIZE, EncryptedText,
TIMEOUT_VALUE) != HAL_OK) {
/* Processing Error */
Error_Handler();
}
printf("Result : Encrypted text\n");
HexDump((uint8_t*) EncryptedText, sizeof(EncryptedText));
// ******************************
printf("Wrap key\n");
/* Wrap in CB with IV key */
hcryp.Init.pKey = pKey;
hcryp.Init.pInitVect = IV_for_key;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
if (HAL_CRYP_Init(&hcryp) != HAL_OK) {
Error_Handler();
}
if (HAL_CRYPEx_WrapKey(&hcryp, pKey, Encryptedkey, TIMEOUT_VALUE)
!= HAL_OK) {
/* Processing Error */
Error_Handler();
}
// ******************************
/* Reset SAES IP */
HAL_CRYP_DeInit(&hcryp);
__HAL_RCC_SAES_FORCE_RESET();
HAL_Delay(10);
__HAL_RCC_SAES_RELEASE_RESET();
hcryp.Init.pKey = pKey;
hcryp.Init.pInitVect = IV_for_key;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
if (HAL_CRYP_Init(&hcryp) != HAL_OK) {
Error_Handler();
}
printf("Unwrap key\n");
if (HAL_CRYPEx_UnwrapKey(&hcryp, Encryptedkey, TIMEOUT_VALUE) != HAL_OK) {
/* Processing Error */
Error_Handler();
}
hcryp.Init.pInitVect = IV_for_data;
if (HAL_CRYP_Encrypt(&hcryp, Plaintext, 16, EncryptedText, TIMEOUT_VALUE)
!= HAL_OK) {
/* Processing Error */
Error_Handler();
}
printf("Encrypted text with wrapped key:\n");
HexDump((uint8_t*) EncryptedText, sizeof(EncryptedText));
2026-01-02 4:39 PM
I finally got this working with the help of Jocelyn's post. I will leave my implementation down below for anyone in the future. I wrote one application that takes a key, wraps it and stores it in OTP flash. Then my bootloader application is able to read the wrapped key out of memory and unwrap it for the firmware decryption to use.
__ALIGN_BEGIN static const uint32_t AESKey256[8] = { private key used to encrypt firmware };
__ALIGN_BEGIN static const uint32_t IV_for_key[4] __ALIGN_END = { IV used to wrap key };
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_BYTE_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_BYTE;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
hcryp.Init.pKey = (uint32_t*)AESKey256;
hcryp.Init.pInitVect = (uint32_t*)IV_for_key;
HAL_CRYP_Init(&hcryp);
HAL_CRYPEx_WrapKey(&hcryp, (uint32_t*)AESKey256, Encryptedkey, HAL_MAX_DELAY);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, PRIVATE_KEY_ADDR, (uint32_t)(uintptr_t)&Encryptedkey[0]);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, (PRIVATE_KEY_ADDR + 0x10), (uint32_t)(uintptr_t)&Encryptedkey[4]);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, PRIVATE_KEY_IV_ADDR, (uint32_t)(uintptr_t)&IV_for_key[0]);
Then in my bootloader...
hcryp.Instance = SAES;
hcryp.Init.DataType = CRYP_BYTE_SWAP;
hcryp.Init.KeySize = CRYP_KEYSIZE_256B;
hcryp.Init.Algorithm = CRYP_AES_CBC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
hcryp.Init.KeyIVConfigSkip = CRYP_KEYIVCONFIG_ONCE;
hcryp.Init.KeyProtection = CRYP_KEYPROT_DISABLE;
hcryp.Init.pKey = (uint32_t*)PRIVATE_KEY_ADDR;
hcryp.Init.pInitVect = (uint32_t*)PRIVATE_KEY_IV_ADDR;
hcryp.Init.KeyMode = CRYP_KEYMODE_WRAPPED;
hcryp.Init.KeySelect = CRYP_KEYSEL_HW;
HAL_CRYP_Init(&hcryp);
HAL_CRYPEx_UnwrapKey(&hcryp, (uint32_t*)PRIVATE_KEY_ADDR, HAL_MAX_DELAY);
// Set IV to value used to encrypt the firmware.
hcryp.Init.pInitVect = (uint32_t*)fw_header.iv;
// loop through your encrypted firmware
HAL_CRYP_Decrypt(&hcryp, (uint32_t*)encrypted_buffer, WORDS, (uint32_t*)decrypted_buffer, HAL_MAX_DELAY);