Skip to content

Commit 511da24

Browse files
committed
Add STM32C5 CCB ECDSA hardware blob-create support
1 parent a97af7c commit 511da24

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

wolfcrypt/src/port/st/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,9 @@ Worked example: `STM32_Bare_Test/src/main_ccb.c` (examples repo, PR #13) runs th
224224
### Current state
225225

226226
- Validated on STM32U385 (NUCLEO-U385RG-Q, TZEN=0), P-256, on both the bare-metal and CubeMX/HAL build paths: `wc_ecc_make_key` -> `wc_ecc_sign_hash` -> `wc_ecc_verify_hash` round-trips, with the private scalar never present in software.
227+
- Also validated on STM32C5 (NUCLEO-C5A3ZG, TZEN=0), P-256, bare-metal: the same `wc_ecc_make_key` -> `wc_ecc_sign_hash` -> `wc_ecc_verify_hash` flow plus a persisted-blob re-import (`wc_ecc_import_wrapped_private_ex`) round-trip, all on the CCB hardware. On STM32C5 the blob-create step is a combined create-and-sign: the C5 OPSTEP machine only advances through the GCM-final phase when the random k is drawn and the PKA sign is started during creation (the r,s are a by-product and discarded). That extra sequence is gated by `WOLFSSL_STM32C5` in the bare driver; the U3 OPSTEP machine does not require it.
227228
- `Stm32Ccb_Init()` pulse-resets the PKA / SAES / RNG before each operation, so the first CCB op is robust even when prior standalone crypto (RNG seeding, ECC keygen) left an engine in a state that would otherwise stall the CCB's chained SAES GCM step. The family-specific reset register name is abstracted (`WC_STM32_CCB_RSTR`).
228-
- CCB requires the U3 at its full clock; the reference clock-tree bring-up (96 MHz) is in the bare example's `boards/u3/hw_init.c`.
229+
- CCB requires the U3 / C5 at its full clock; the reference clock-tree bring-up is in the bare example's `boards/u3/hw_init.c` (96 MHz) and `boards/c5a3/hw_init.c`.
229230

230231

231232
## STM32 BARE-metal port

wolfcrypt/src/port/st/stm32.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4274,8 +4274,10 @@ int wc_Stm32_Ccb_EccMakeBlob(int curveId, const byte* d, word32 dLen,
42744274
if (curveId != ECC_SECP256R1) {
42754275
return NOT_COMPILED_IN;
42764276
}
4277+
/* Require pubX/pubY non-NULL to match the CubeMX/HAL implementation's
4278+
* contract (one public API, one NULL-handling rule across build flavors). */
42774279
if (d == NULL || dLen != 32u || iv == NULL || tag == NULL ||
4278-
wrapped == NULL || wrappedSz == NULL) {
4280+
wrapped == NULL || wrappedSz == NULL || pubX == NULL || pubY == NULL) {
42794281
return BAD_FUNC_ARG;
42804282
}
42814283
opsz = WC_CCB_P256_OPSZ;
@@ -4345,6 +4347,41 @@ int wc_Stm32_Ccb_EccMakeBlob(int curveId, const byte* d, word32 dLen,
43454347
WC_CCB_PKA_RAMW[PKA_ECDSA_SIGN_IN_PRIVATE_KEY_D + cipsz + 1u] = 0u;
43464348
if ((ret = Stm32Ccb_PkaWaitFlag(PKA_SR_DATAOKF)) != 0) { goto done; }
43474349

4350+
#if defined(WOLFSSL_STM32C5)
4351+
/* STM32C5: blob-create is a combined create+SIGN -- the OPSTEP machine only
4352+
* advances through the GCM-final tag phase if the random k is drawn and the
4353+
* PKA sign is started. Draw k, run the GCM final phase, read the tag, then
4354+
* START the PKA; the resulting r,s are a creation by-product and discarded
4355+
* (the blob is still {iv, tag, wrapped}). Mirrors the C5 HAL
4356+
* CCB_ECDSA_SignBlobCreation. The U3 OPSTEP machine does not require this. */
4357+
if ((ret = Stm32Ccb_WaitOpStep(0x09u)) != 0) { goto done; }
4358+
for (off = 0u; off < (opsz - 2u); off++) {
4359+
if ((ret = Stm32Ccb_RngWaitDrdy()) != 0) { goto done; }
4360+
WC_CCB_PKA_RAMW[PKA_ECDSA_SIGN_IN_K + off] = WC_CCB_MAGIC;
4361+
}
4362+
if ((PKA->SR & PKA_SR_RNGERRF) != 0u) { ret = WC_HW_E; goto done; }
4363+
WC_CCB_PKA_RAMW[PKA_ECDSA_SIGN_IN_K + (opsz - 2u)] = 0u;
4364+
WC_CCB_PKA_RAMW[PKA_ECDSA_SIGN_IN_K + (opsz - 2u) + 1u] = 0u;
4365+
if ((ret = Stm32Ccb_PkaWaitFlag(PKA_SR_RNGOKF)) != 0) { goto done; }
4366+
if ((ret = Stm32Ccb_SaesWaitBusy()) != 0) { goto done; }
4367+
SAES->CR = (SAES->CR & ~WC_STM32_AES_CR_PHASE) | WC_STM32_AES_CR_PHASE_0 | WC_STM32_AES_CR_PHASE_1;
4368+
PKA->CLRFR = PKA_CLRFR_CMFC;
4369+
SAES->DINR = 0u;
4370+
SAES->DINR = WC_CCB_GCM_HDR_LEN(opsz);
4371+
SAES->DINR = 0u;
4372+
SAES->DINR = cipsz * 32u;
4373+
if ((ret = Stm32Ccb_SaesWaitCcf()) != 0) { goto done; }
4374+
for (i = 0u; i < 4u; i++) {
4375+
tagw[i] = SAES->DOUTR;
4376+
}
4377+
PKA->CR |= PKA_CR_START;
4378+
if ((ret = Stm32Ccb_PkaWaitFlag(PKA_SR_PROCENDF)) != 0) { goto done; }
4379+
if ((ret = Stm32Ccb_WaitOpStep(0x1Au)) != 0) { goto done; }
4380+
if (WC_CCB_PKA_RAMW[PKA_ECDSA_SIGN_OUT_ERROR] != WC_CCB_PKA_OK) {
4381+
ret = WC_HW_E;
4382+
goto done;
4383+
}
4384+
#else
43484385
/* GCM final phase: feed the length block and read the authentication tag. */
43494386
SAES->CR = (SAES->CR & ~WC_STM32_AES_CR_PHASE) | WC_STM32_AES_CR_PHASE_0 | WC_STM32_AES_CR_PHASE_1;
43504387
if ((ret = Stm32Ccb_WaitOpStep(0x0Au)) != 0) { goto done; }
@@ -4357,6 +4394,7 @@ int wc_Stm32_Ccb_EccMakeBlob(int curveId, const byte* d, word32 dLen,
43574394
for (i = 0u; i < 4u; i++) {
43584395
tagw[i] = SAES->DOUTR;
43594396
}
4397+
#endif
43604398

43614399
XMEMCPY(iv, ivw, sizeof(ivw));
43624400
XMEMCPY(tag, tagw, sizeof(tagw));
@@ -4368,8 +4406,9 @@ int wc_Stm32_Ccb_EccMakeBlob(int curveId, const byte* d, word32 dLen,
43684406
Stm32Ccb_Reset();
43694407
SAES->CR &= ~AES_CR_EN;
43704408
wolfSSL_CryptHwMutexUnLock();
4371-
/* Derive the public key from the fresh blob (separate locked op). */
4372-
if (ret == 0 && pubX != NULL && pubY != NULL) {
4409+
/* Derive the public key from the fresh blob (separate locked op).
4410+
* pubX/pubY are guaranteed non-NULL by the argument check above. */
4411+
if (ret == 0) {
43734412
ret = Stm32Ccb_ComputePub(iv, tag, wrapped, pubX, pubY);
43744413
}
43754414
return ret;

0 commit comments

Comments
 (0)