diff --git a/boards/stm32wb55xx_nucleo/board.c b/boards/stm32wb55xx_nucleo/board.c index 698e7f6..a4ace7e 100644 --- a/boards/stm32wb55xx_nucleo/board.c +++ b/boards/stm32wb55xx_nucleo/board.c @@ -53,6 +53,7 @@ static const whal_Stm32wb_Rcc_PeriphClk g_periphClks[] = { {WHAL_STM32WB55_SPI1_GATE}, {WHAL_STM32WB55_RNG_GATE}, {WHAL_STM32WB55_AES1_GATE}, + {WHAL_STM32WB55_PKA_GATE}, {WHAL_STM32WB55_I2C1_GATE}, #ifdef BOARD_WATCHDOG_WWDG {WHAL_STM32WB55_WWDG_GATE}, @@ -256,6 +257,13 @@ whal_Error Board_Init(void) return err; } + /* PKA: platform-specific Init because whal_Crypto_Init is direct-mapped + * to whal_Stm32wb_Aes_Init on this board (AES claims the generic name). */ + err = whal_Stm32wb_Pka_Init(WHAL_INTERNAL_DEV); + if (err) { + return err; + } + err = whal_Timer_Init(WHAL_INTERNAL_DEV); if (err) { return err; @@ -294,6 +302,11 @@ whal_Error Board_Deinit(void) } + err = whal_Stm32wb_Pka_Deinit(WHAL_INTERNAL_DEV); + if (err) { + return err; + } + err = whal_Crypto_Deinit(&g_whalCrypto); if (err) { return err; diff --git a/boards/stm32wb55xx_nucleo/board.h b/boards/stm32wb55xx_nucleo/board.h index f1928db..a571936 100644 --- a/boards/stm32wb55xx_nucleo/board.h +++ b/boards/stm32wb55xx_nucleo/board.h @@ -71,7 +71,7 @@ enum { #define BOARD_AES_GMAC_DEV WHAL_INTERNAL_DEV #define BOARD_AES_CCM_DEV WHAL_INTERNAL_DEV -/* GPIO dev initializer — singleton defined in stm32wb_gpio.c. */ +/* GPIO dev initializer — single-instance device defined in stm32wb_gpio.c. */ #define WHAL_CFG_STM32WB_GPIO_DEV { \ .base = WHAL_STM32WB55_GPIO_BASE, \ .cfg = (void *)&(const whal_Stm32wb_Gpio_Cfg){ \ @@ -117,7 +117,7 @@ enum { }, \ } -/* AES crypto + mode dev initializers — singletons defined in stm32wb_aes.c. +/* AES crypto + mode dev initializers — single-instance devices defined in stm32wb_aes.c. * Mutable GCM/CCM state buffers (g_stm32wbAesGcm/CcmDevState) are static in * the driver TU. */ #define WHAL_CFG_STM32WB_AES_DEV { \ @@ -160,7 +160,20 @@ enum { .state = &g_stm32wbAesCcmDevState, \ } -/* Flash dev initializer — the singleton itself is defined in stm32wb_flash.c +/* PKA peripheral dev initializer — single-instance device defined in + * stm32wb_pka.c. PKA uses vtable dispatch for whal_Crypto_Init (AES + * already claims the direct mapping at the Crypto type), so .driver is + * set. The math primitives (whal_Pka_*) are direct functions and don't + * need a device wrapper. */ +#define WHAL_CFG_STM32WB_PKA_DEV { \ + .base = WHAL_STM32WB55_PKA_BASE, \ + .driver = WHAL_STM32WB55_PKA_DRIVER, \ + .cfg = (void *)&(const whal_Stm32wb_Pka_Cfg){ \ + .timeout = &g_whalTimeout, \ + }, \ +} + +/* Flash dev initializer — the single-instance device itself is defined in stm32wb_flash.c * (which #includes this header). BOARD_FLASH_DEV takes its address so * whal_Flash_* can dispatch via .driver alongside coexisting flash drivers * (e.g. SPI NOR W25Q64). */ @@ -174,7 +187,7 @@ enum { }, \ } -/* IWDG dev initializer — singleton defined in stm32wb_iwdg.c. */ +/* IWDG dev initializer — single-instance device defined in stm32wb_iwdg.c. */ #define WHAL_CFG_STM32WB_IWDG_DEV { \ .base = WHAL_STM32WB55_IWDG_BASE, \ /* .driver: direct API mapping */ \ @@ -185,7 +198,7 @@ enum { }, \ } -/* WWDG dev initializer — singleton defined in stm32wb_wwdg.c. */ +/* WWDG dev initializer — single-instance device defined in stm32wb_wwdg.c. */ #define WHAL_CFG_STM32WB_WWDG_DEV { \ .base = WHAL_STM32WB55_WWDG_BASE, \ /* .driver: direct API mapping */ \ @@ -196,7 +209,7 @@ enum { }, \ } -/* RNG dev initializer — singleton defined in stm32wb_rng.c. */ +/* RNG dev initializer — single-instance device defined in stm32wb_rng.c. */ #define WHAL_CFG_STM32WB_RNG_DEV { \ .base = WHAL_STM32WB55_RNG_BASE, \ /* .driver: direct API mapping */ \ @@ -205,13 +218,13 @@ enum { }, \ } -/* NVIC dev initializer — singleton defined in cortex_m4_nvic.c. */ +/* NVIC dev initializer — single-instance device defined in cortex_m4_nvic.c. */ #define WHAL_CFG_NVIC_DEV { \ .base = WHAL_CORTEX_M4_NVIC_BASE, \ /* .driver: direct API mapping */ \ } -/* SysTick dev initializer — singleton defined in systick.c. */ +/* SysTick dev initializer — single-instance device defined in systick.c. */ #define WHAL_CFG_SYSTICK_DEV { \ .base = WHAL_CORTEX_M4_SYSTICK_BASE, \ /* .driver: direct API mapping */ \ diff --git a/boards/stm32wb55xx_nucleo/board.mk b/boards/stm32wb55xx_nucleo/board.mk index 0a00fdb..18f67fd 100644 --- a/boards/stm32wb55xx_nucleo/board.mk +++ b/boards/stm32wb55xx_nucleo/board.mk @@ -22,7 +22,7 @@ _BOARD_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) PLATFORM = stm32wb -TESTS ?= clock gpio flash timer rng aes_ecb aes_cbc aes_ctr aes_gcm aes_gmac aes_ccm block uart i2c +TESTS ?= clock gpio flash timer rng aes_ecb aes_cbc aes_ctr aes_gcm aes_gmac aes_ccm pka block uart i2c GCC = $(GCC_PATH)arm-none-eabi-gcc LD = $(GCC_PATH)arm-none-eabi-ld @@ -48,6 +48,7 @@ CFLAGS += -Wall -Werror $(INCLUDE) -g3 -Os -ffunction-sections -fdata-sections \ -DWHAL_CFG_STM32WB_AES_GCM_DIRECT_API_MAPPING \ -DWHAL_CFG_STM32WB_AES_GMAC_DIRECT_API_MAPPING \ -DWHAL_CFG_STM32WB_AES_CCM_DIRECT_API_MAPPING \ + -DWHAL_CFG_STM32WB_PKA_DIRECT_API_MAPPING \ -DWHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING \ -DWHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING LDFLAGS = --omagic -static --gc-sections diff --git a/docs/writing_a_driver.md b/docs/writing_a_driver.md index fe3497c..6512f7c 100644 --- a/docs/writing_a_driver.md +++ b/docs/writing_a_driver.md @@ -963,10 +963,16 @@ consumption and avoid unnecessary entropy source wear. Header: `wolfHAL/crypto/crypto.h` The crypto subsystem provides access to hardware cryptographic accelerators -(ciphers, hashes, MACs). Unlike other device types, crypto uses a two-level -device model: a **hardware device** (`whal_Crypto`) for peripheral lifecycle, -and **per-algorithm device structs** (`whal_AesGcm`, `whal_Sha256`, etc.) -that carry typed vtables with direct function arguments. +(ciphers, hashes, MACs, public-key math). Unlike other device types, crypto +uses a two-level device model: a **hardware device** (`whal_Crypto`) for +peripheral lifecycle, and **per-algorithm device structs** (`whal_AesGcm`, +`whal_Sha256`, etc.) that carry typed vtables with direct function +arguments. + +Public-key math accelerators (PKA) are an exception: lifecycle still goes +through `whal_Crypto`, but the math operations themselves are exposed as +**direct functions on big-endian operand byte arrays** — no per-algorithm +device struct, no vtable. See the *Math Primitives* subsection below. ### Architecture @@ -1194,6 +1200,57 @@ whal_AesCbc_Process(BOARD_AES_CBC_DEV, block1, out1, 16); whal_AesCbc_Process(BOARD_AES_CBC_DEV, block2, out2, 16); ``` +### Math Primitives + +Header: `wolfHAL/crypto/pka.h` + +Public-key accelerators (PKA, BIGINT, etc.) expose **stateless math +primitives** over multi-precision integers rather than algorithm-typed +streaming sessions. Each call is one-shot: load operands, run one hardware +opcode, read the result. wolfHAL surfaces these as direct functions on +big-endian byte arrays: + +```c +whal_Error whal_Pka_ModExp(const uint8_t *A, size_t ASz, + const uint8_t *e, size_t eSz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +whal_Error whal_Pka_ModInv (...); +whal_Error whal_Pka_ModReduce(...); +whal_Error whal_Pka_IntMul (...); +whal_Error whal_Pka_IntSub (...); +whal_Error whal_Pka_RsaCrtExp(...); +``` + +There is no `whal_Pka` device struct, no per-primitive vtable, and no +device handle passed to the call. The platform driver owns a single +`whal_Crypto` for the PKA peripheral's lifecycle (`Init`/`Deinit`, +enable/disable, error flag clearing); the math primitives reach the +peripheral through that internal device. + +**Why no algorithm-typed device?** The primitives are single-call, +share no state between invocations, and the same hardware opcode set is +the only useful "algorithm" a PKA exposes. A vtable per primitive would +just be six 1-entry tables. + +**RAM hygiene.** A PKA driver should zero every operand slot it writes +on the success path of each primitive. The peripheral RAM is small and +shared across opcodes — leaving operand or result bytes in place can +corrupt the next call (operands shorter than the configured op length +read stale data from the unwritten high words) and leaks sensitive +material (private exponents, primes, signature results) until the next +op overwrites those slots. + +**Direct API mapping.** The platform-prefixed primitives +(`whal_Stm32wb_Pka_ModExp` etc.) are aliased to the generic +`whal_Pka_*` names via `WHAL_CFG__PKA_DIRECT_API_MAPPING`, the +same pattern as other crypto algorithms. The macros live in the driver +`.c` (not the header) so the binary exports only the generic names. + +**Reference:** `wolfHAL/crypto/stm32wb_pka.h` and +`src/crypto/stm32wb_pka.c`. + ### Writing a Crypto Driver A single platform driver file typically implements multiple algorithms that @@ -1337,6 +1394,12 @@ To add support for a new algorithm (e.g., ChaCha20-Poly1305): 4. Add direct API mapping guards in the driver header. 5. Add `WHAL_CFG_` config guards around the new API functions. +For stateless math primitives (PKA-style), follow the *Math Primitives* +pattern instead: declare the generic functions in `wolfHAL/crypto/pka.h` +(no dispatch layer needed — they're called directly), implement them in +the platform driver, and alias the platform-prefixed names to the +generic ones via the driver's direct-API-mapping flag. + ### Board Integration The board instantiates one `whal_Crypto` device per hardware peripheral, plus @@ -1404,6 +1467,9 @@ worked example. `src/crypto/stm32wb_aes.c` - **Hash/HMAC (SHA-1/SHA-224/SHA-256, HMAC variants)**: `wolfHAL/crypto/stm32wba_hash.h` and `src/crypto/stm32wba_hash.c` +- **PKA math primitives (ModExp, ModInv, ModReduce, IntMul, IntSub, + RsaCrtExp)**: `wolfHAL/crypto/stm32wb_pka.h` and + `src/crypto/stm32wb_pka.c` --- diff --git a/src/crypto/stm32wb_pka.c b/src/crypto/stm32wb_pka.c new file mode 100644 index 0000000..e136a00 --- /dev/null +++ b/src/crypto/stm32wb_pka.c @@ -0,0 +1,496 @@ +/* stm32wb_pka.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include "board.h" /* provides WHAL_CFG_STM32WB_PKA_DEV initializer */ +#include +#include +#include +#include +#include +#include + +/* Direct-API mapping for the math primitives: when the board defines + * WHAL_CFG_STM32WB_PKA_DIRECT_API_MAPPING, the platform-prefixed function + * names below get preprocessor-substituted to the generic whal_Pka_* names + * declared in , so the binary exports those names. */ +#ifdef WHAL_CFG_STM32WB_PKA_DIRECT_API_MAPPING +#define whal_Stm32wb_Pka_ModExp whal_Pka_ModExp +#define whal_Stm32wb_Pka_ModInv whal_Pka_ModInv +#define whal_Stm32wb_Pka_ModReduce whal_Pka_ModReduce +#define whal_Stm32wb_Pka_IntMul whal_Pka_IntMul +#define whal_Stm32wb_Pka_IntSub whal_Pka_IntSub +#define whal_Stm32wb_Pka_RsaCrtExp whal_Pka_RsaCrtExp +#endif + +const whal_Crypto whal_Stm32wb_Pka_Dev = WHAL_CFG_STM32WB_PKA_DEV; + +/* ---- PKA register definitions ---- */ + +/* Control Register */ +#define PKA_CR_REG 0x00 +#define PKA_CR_EN_Pos 0 +#define PKA_CR_EN_Msk (1UL << PKA_CR_EN_Pos) +#define PKA_CR_START_Pos 1 +#define PKA_CR_START_Msk (1UL << PKA_CR_START_Pos) +#define PKA_CR_MODE_Pos 8 +#define PKA_CR_MODE_Msk (0x3FUL << PKA_CR_MODE_Pos) +#define PKA_CR_PROCENDIE_Pos 17 +#define PKA_CR_PROCENDIE_Msk (1UL << PKA_CR_PROCENDIE_Pos) +#define PKA_CR_RAMERRIE_Pos 19 +#define PKA_CR_RAMERRIE_Msk (1UL << PKA_CR_RAMERRIE_Pos) +#define PKA_CR_ADDRERRIE_Pos 20 +#define PKA_CR_ADDRERRIE_Msk (1UL << PKA_CR_ADDRERRIE_Pos) + +/* Status Register */ +#define PKA_SR_REG 0x04 +#define PKA_SR_BUSY_Pos 16 +#define PKA_SR_BUSY_Msk (1UL << PKA_SR_BUSY_Pos) +#define PKA_SR_PROCENDF_Pos 17 +#define PKA_SR_PROCENDF_Msk (1UL << PKA_SR_PROCENDF_Pos) +#define PKA_SR_RAMERRF_Pos 19 +#define PKA_SR_RAMERRF_Msk (1UL << PKA_SR_RAMERRF_Pos) +#define PKA_SR_ADDRERRF_Pos 20 +#define PKA_SR_ADDRERRF_Msk (1UL << PKA_SR_ADDRERRF_Pos) + +/* Clear Flag Register */ +#define PKA_CLRFR_REG 0x08 +#define PKA_CLRFR_PROCENDFC_Pos 17 +#define PKA_CLRFR_PROCENDFC_Msk (1UL << PKA_CLRFR_PROCENDFC_Pos) +#define PKA_CLRFR_RAMERRFC_Pos 19 +#define PKA_CLRFR_RAMERRFC_Msk (1UL << PKA_CLRFR_RAMERRFC_Pos) +#define PKA_CLRFR_ADDRERRFC_Pos 20 +#define PKA_CLRFR_ADDRERRFC_Msk (1UL << PKA_CLRFR_ADDRERRFC_Pos) + +/* Operating modes (PKA_CR.MODE values) */ +#define PKA_MODE_MODEXP_NORMAL 0x00 +#define PKA_MODE_MONTGOMERY_PARAM 0x01 +#define PKA_MODE_MODEXP_FAST 0x02 +#define PKA_MODE_RSA_CRT_EXP 0x07 +#define PKA_MODE_MODINV 0x08 +#define PKA_MODE_INT_ADD 0x09 +#define PKA_MODE_INT_SUB 0x0A +#define PKA_MODE_INT_MUL 0x0B +#define PKA_MODE_INT_CMP 0x0C +#define PKA_MODE_MOD_REDUCE 0x0D +#define PKA_MODE_MOD_ADD 0x0E +#define PKA_MODE_MOD_SUB 0x0F +#define PKA_MODE_MONTGOMERY_MUL 0x10 +#define PKA_MODE_ECC_SCALAR_MUL 0x20 +#define PKA_MODE_ECC_SCALAR_MUL_FAST 0x22 +#define PKA_MODE_ECDSA_SIGN 0x24 +#define PKA_MODE_ECDSA_VERIFY 0x26 +#define PKA_MODE_ECC_POINT_CHECK 0x28 + +/* ---- PKA RAM operand offsets (from PKA base address) ---- */ + +/* MODE 0x00 — Modular exponentiation (normal) */ +#define PKA_MODEXP_EXP_LEN 0x400 +#define PKA_MODEXP_OP_LEN 0x404 +#define PKA_MODEXP_A 0xA44 +#define PKA_MODEXP_E 0xBD0 +#define PKA_MODEXP_N 0xD5C +#define PKA_MODEXP_RESULT 0x724 + +/* MODE 0x01 — Montgomery parameter (R^2 mod n) */ +#define PKA_MONT_PARAM_MOD_LEN 0x404 +#define PKA_MONT_PARAM_N 0xD5C +#define PKA_MONT_PARAM_R2 0x594 + +/* MODE 0x02 — Modular exponentiation (fast, precomputed R^2) */ +#define PKA_MODEXP_FAST_EXP_LEN 0x400 +#define PKA_MODEXP_FAST_OP_LEN 0x404 +#define PKA_MODEXP_FAST_A 0xA44 +#define PKA_MODEXP_FAST_E 0xBD0 +#define PKA_MODEXP_FAST_N 0xD5C +#define PKA_MODEXP_FAST_R2 0x594 +#define PKA_MODEXP_FAST_RESULT 0x724 + +/* MODE 0x07 — RSA CRT exponentiation */ +#define PKA_RSA_CRT_OP_LEN 0x404 +#define PKA_RSA_CRT_DP 0x65C +#define PKA_RSA_CRT_DQ 0xBD0 +#define PKA_RSA_CRT_QINV 0x7EC +#define PKA_RSA_CRT_P 0x97C +#define PKA_RSA_CRT_Q 0xD5C +#define PKA_RSA_CRT_A 0xEEC +#define PKA_RSA_CRT_RESULT 0x724 + +/* MODE 0x08 — Modular inversion */ +#define PKA_MODINV_OP_LEN 0x404 +#define PKA_MODINV_A 0x8B4 +#define PKA_MODINV_N 0xA44 +#define PKA_MODINV_RESULT 0xBD0 + +/* MODE 0x0A — Arithmetic subtraction */ +#define PKA_INT_SUB_OP_LEN 0x404 +#define PKA_INT_SUB_A 0x8B4 +#define PKA_INT_SUB_B 0xA44 +#define PKA_INT_SUB_RESULT 0xBD0 + +/* MODE 0x0B — Arithmetic multiplication */ +#define PKA_INT_MUL_OP_LEN 0x404 +#define PKA_INT_MUL_A 0x8B4 +#define PKA_INT_MUL_B 0xA44 +#define PKA_INT_MUL_RESULT 0xBD0 + +/* MODE 0x0D — Modular reduction */ +#define PKA_MOD_REDUCE_OP_LEN 0x400 +#define PKA_MOD_REDUCE_MOD_LEN 0x404 +#define PKA_MOD_REDUCE_A 0x8B4 +#define PKA_MOD_REDUCE_N 0xA44 +#define PKA_MOD_REDUCE_RESULT 0xBD0 + +/* ---- Internal helpers ---- */ + +/* Write a big-endian integer into the PKA RAM at offset as a little-endian + * 32-bit word array, with the mandatory trailing zero word. */ +static void WriteOperand(size_t base, size_t offset, + const uint8_t *data, size_t dataSz) +{ + size_t numWords = (dataSz + 3) / 4; + size_t i, b; + + for (i = 0; i < numWords; i++) { + uint32_t word = 0; + for (b = 0; b < 4; b++) { + size_t bytePos = i * 4 + b; + if (bytePos < dataSz) + word |= (uint32_t)data[dataSz - 1 - bytePos] << (b * 8); + } + whal_Reg_Write(base, offset + i * 4, word); + } + whal_Reg_Write(base, offset + numWords * 4, 0); +} + +/* Read a little-endian word array from PKA RAM into a big-endian byte array. */ +static void ReadOperand(size_t base, size_t offset, + uint8_t *data, size_t dataSz) +{ + size_t numWords = (dataSz + 3) / 4; + size_t i, b; + + for (i = 0; i < numWords; i++) { + uint32_t word = whal_Reg_Read(base, offset + i * 4); + for (b = 0; b < 4; b++) { + size_t bytePos = i * 4 + b; + if (bytePos < dataSz) + data[dataSz - 1 - bytePos] = (uint8_t)(word >> (b * 8)); + } + } +} + +/* Zero the slot footprint of a prior WriteOperand/ReadOperand call: the + * numWords data words plus the one trailing word. Called on every op's + * success path so the PKA RAM never carries stale operand or result bytes + * into the next operation. */ +static void ZeroOperand(size_t base, size_t offset, size_t dataSz) +{ + size_t numWords = (dataSz + 3) / 4; + size_t i; + + for (i = 0; i <= numWords; i++) + whal_Reg_Write(base, offset + i * 4, 0); +} + +static whal_Error WaitForProcEnd(size_t base, whal_Timeout *timeout) +{ + const uint32_t errFlags = PKA_SR_ADDRERRF_Msk | PKA_SR_RAMERRF_Msk; + const uint32_t doneFlags = errFlags | PKA_SR_PROCENDF_Msk; + const uint32_t clrFlags = PKA_CLRFR_ADDRERRFC_Msk | + PKA_CLRFR_RAMERRFC_Msk | + PKA_CLRFR_PROCENDFC_Msk; + + WHAL_TIMEOUT_START(timeout); + + while (1) { + uint32_t sr = whal_Reg_Read(base, PKA_SR_REG); + if (sr & doneFlags) { + whal_Reg_Write(base, PKA_CLRFR_REG, clrFlags); + return (sr & errFlags) ? WHAL_EHARDWARE : WHAL_SUCCESS; + } + if (WHAL_TIMEOUT_EXPIRED(timeout)) + return WHAL_ETIMEOUT; + } +} + +/* ---- Init / Deinit ---- */ + +whal_Error whal_Stm32wb_Pka_Init(whal_Crypto *dev) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + (void)dev; + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_EN_Msk, PKA_CR_EN_Msk); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Pka_Deinit(whal_Crypto *dev) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + (void)dev; + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_EN_Msk, 0); + return WHAL_SUCCESS; +} + +/* ---- PKA math primitives (whal_Stm32wb_Pka_*, aliased to whal_Pka_* with direct mapping) ---- + * + * Each function maps to one PKA hardware opcode. Operands are big-endian + * byte arrays. The PKA peripheral base and the polling timeout are pulled + * from the whal_Stm32wb_Pka_Dev singleton. + */ + +whal_Error whal_Stm32wb_Pka_ModExp(const uint8_t *A, size_t ASz, + const uint8_t *e, size_t eSz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + const whal_Stm32wb_Pka_Cfg *cfg = + (const whal_Stm32wb_Pka_Cfg *)whal_Stm32wb_Pka_Dev.cfg; + whal_Error err; + + if (A == NULL || e == NULL || N == NULL || result == NULL || + ASz == 0 || eSz == 0 || NSz == 0 || resultSz == 0) + return WHAL_EINVAL; + if (NSz * 8 > 3136 || eSz * 8 > 3136 || + ASz != NSz || eSz != NSz || resultSz != NSz) + return WHAL_ENOTSUP; + + whal_Reg_Write(base, PKA_MODEXP_EXP_LEN, (uint32_t)(eSz * 8)); + whal_Reg_Write(base, PKA_MODEXP_OP_LEN, (uint32_t)(NSz * 8)); + WriteOperand(base, PKA_MODEXP_A, A, ASz); + WriteOperand(base, PKA_MODEXP_E, e, eSz); + WriteOperand(base, PKA_MODEXP_N, N, NSz); + + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_MODE_Msk, + (uint32_t)PKA_MODE_MODEXP_NORMAL << PKA_CR_MODE_Pos); + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_START_Msk, PKA_CR_START_Msk); + + err = WaitForProcEnd(base, cfg->timeout); + if (err == WHAL_SUCCESS) + ReadOperand(base, PKA_MODEXP_RESULT, result, resultSz); + + whal_Reg_Write(base, PKA_MODEXP_EXP_LEN, 0); + whal_Reg_Write(base, PKA_MODEXP_OP_LEN, 0); + ZeroOperand(base, PKA_MODEXP_A, ASz); + ZeroOperand(base, PKA_MODEXP_E, eSz); + ZeroOperand(base, PKA_MODEXP_N, NSz); + ZeroOperand(base, PKA_MODEXP_RESULT, resultSz); + return err; +} + +whal_Error whal_Stm32wb_Pka_ModInv(const uint8_t *A, size_t ASz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + const whal_Stm32wb_Pka_Cfg *cfg = + (const whal_Stm32wb_Pka_Cfg *)whal_Stm32wb_Pka_Dev.cfg; + whal_Error err; + + if (A == NULL || N == NULL || result == NULL || + ASz == 0 || NSz == 0 || resultSz == 0) + return WHAL_EINVAL; + if (NSz * 8 > 3136 || ASz != NSz || resultSz != NSz) + return WHAL_ENOTSUP; + + whal_Reg_Write(base, PKA_MODINV_OP_LEN, (uint32_t)(NSz * 8)); + WriteOperand(base, PKA_MODINV_A, A, ASz); + WriteOperand(base, PKA_MODINV_N, N, NSz); + + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_MODE_Msk, + (uint32_t)PKA_MODE_MODINV << PKA_CR_MODE_Pos); + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_START_Msk, PKA_CR_START_Msk); + + err = WaitForProcEnd(base, cfg->timeout); + if (err == WHAL_SUCCESS) + ReadOperand(base, PKA_MODINV_RESULT, result, resultSz); + + whal_Reg_Write(base, PKA_MODINV_OP_LEN, 0); + ZeroOperand(base, PKA_MODINV_A, ASz); + ZeroOperand(base, PKA_MODINV_N, NSz); + ZeroOperand(base, PKA_MODINV_RESULT, resultSz); + return err; +} + +whal_Error whal_Stm32wb_Pka_ModReduce(const uint8_t *A, size_t ASz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + const whal_Stm32wb_Pka_Cfg *cfg = + (const whal_Stm32wb_Pka_Cfg *)whal_Stm32wb_Pka_Dev.cfg; + whal_Error err; + + if (A == NULL || N == NULL || result == NULL || + ASz == 0 || NSz == 0 || resultSz == 0) + return WHAL_EINVAL; + if (NSz * 8 > 3136 || ASz != NSz || resultSz != NSz) + return WHAL_ENOTSUP; + + whal_Reg_Write(base, PKA_MOD_REDUCE_OP_LEN, (uint32_t)(ASz * 8)); + whal_Reg_Write(base, PKA_MOD_REDUCE_MOD_LEN, (uint32_t)(NSz * 8)); + WriteOperand(base, PKA_MOD_REDUCE_A, A, ASz); + WriteOperand(base, PKA_MOD_REDUCE_N, N, NSz); + + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_MODE_Msk, + (uint32_t)PKA_MODE_MOD_REDUCE << PKA_CR_MODE_Pos); + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_START_Msk, PKA_CR_START_Msk); + + err = WaitForProcEnd(base, cfg->timeout); + if (err == WHAL_SUCCESS) + ReadOperand(base, PKA_MOD_REDUCE_RESULT, result, resultSz); + + whal_Reg_Write(base, PKA_MOD_REDUCE_OP_LEN, 0); + whal_Reg_Write(base, PKA_MOD_REDUCE_MOD_LEN, 0); + ZeroOperand(base, PKA_MOD_REDUCE_A, ASz); + ZeroOperand(base, PKA_MOD_REDUCE_N, NSz); + ZeroOperand(base, PKA_MOD_REDUCE_RESULT, resultSz); + return err; +} + +whal_Error whal_Stm32wb_Pka_IntMul(const uint8_t *A, size_t ASz, + const uint8_t *B, size_t BSz, + uint8_t *result, size_t resultSz) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + const whal_Stm32wb_Pka_Cfg *cfg = + (const whal_Stm32wb_Pka_Cfg *)whal_Stm32wb_Pka_Dev.cfg; + whal_Error err; + + if (A == NULL || B == NULL || result == NULL || + ASz == 0 || BSz == 0 || resultSz == 0) + return WHAL_EINVAL; + if (ASz * 8 > 3136 || ASz != BSz || resultSz != 2 * ASz) + return WHAL_ENOTSUP; + + whal_Reg_Write(base, PKA_INT_MUL_OP_LEN, (uint32_t)(ASz * 8)); + WriteOperand(base, PKA_INT_MUL_A, A, ASz); + WriteOperand(base, PKA_INT_MUL_B, B, BSz); + + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_MODE_Msk, + (uint32_t)PKA_MODE_INT_MUL << PKA_CR_MODE_Pos); + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_START_Msk, PKA_CR_START_Msk); + + err = WaitForProcEnd(base, cfg->timeout); + if (err == WHAL_SUCCESS) + ReadOperand(base, PKA_INT_MUL_RESULT, result, resultSz); + + whal_Reg_Write(base, PKA_INT_MUL_OP_LEN, 0); + ZeroOperand(base, PKA_INT_MUL_A, ASz); + ZeroOperand(base, PKA_INT_MUL_B, BSz); + ZeroOperand(base, PKA_INT_MUL_RESULT, resultSz); + return err; +} + +whal_Error whal_Stm32wb_Pka_IntSub(const uint8_t *A, size_t ASz, + const uint8_t *B, size_t BSz, + uint8_t *result, size_t resultSz) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + const whal_Stm32wb_Pka_Cfg *cfg = + (const whal_Stm32wb_Pka_Cfg *)whal_Stm32wb_Pka_Dev.cfg; + whal_Error err; + + if (A == NULL || B == NULL || result == NULL || + ASz == 0 || BSz == 0 || resultSz == 0) + return WHAL_EINVAL; + if (ASz * 8 > 3136 || ASz != BSz || resultSz != ASz) + return WHAL_ENOTSUP; + + whal_Reg_Write(base, PKA_INT_SUB_OP_LEN, (uint32_t)(ASz * 8)); + WriteOperand(base, PKA_INT_SUB_A, A, ASz); + WriteOperand(base, PKA_INT_SUB_B, B, BSz); + + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_MODE_Msk, + (uint32_t)PKA_MODE_INT_SUB << PKA_CR_MODE_Pos); + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_START_Msk, PKA_CR_START_Msk); + + err = WaitForProcEnd(base, cfg->timeout); + if (err == WHAL_SUCCESS) + ReadOperand(base, PKA_INT_SUB_RESULT, result, resultSz); + + whal_Reg_Write(base, PKA_INT_SUB_OP_LEN, 0); + ZeroOperand(base, PKA_INT_SUB_A, ASz); + ZeroOperand(base, PKA_INT_SUB_B, BSz); + ZeroOperand(base, PKA_INT_SUB_RESULT, resultSz); + return err; +} + +whal_Error whal_Stm32wb_Pka_RsaCrtExp(const uint8_t *A, size_t ASz, + const uint8_t *dP, size_t dPSz, + const uint8_t *dQ, size_t dQSz, + const uint8_t *qInv, size_t qInvSz, + const uint8_t *p, size_t pSz, + const uint8_t *q, size_t qSz, + uint8_t *result, size_t resultSz) +{ + size_t base = whal_Stm32wb_Pka_Dev.base; + const whal_Stm32wb_Pka_Cfg *cfg = + (const whal_Stm32wb_Pka_Cfg *)whal_Stm32wb_Pka_Dev.cfg; + whal_Error err; + + if (A == NULL || dP == NULL || dQ == NULL || qInv == NULL || + p == NULL || q == NULL || result == NULL || + ASz == 0 || dPSz == 0 || dQSz == 0 || qInvSz == 0 || + pSz == 0 || qSz == 0 || resultSz == 0) + return WHAL_EINVAL; + if ((pSz + qSz) * 8 > 3136 || + pSz != qSz || pSz != dPSz || pSz != dQSz || pSz != qInvSz || + ASz != pSz + qSz || resultSz != pSz + qSz) + return WHAL_ENOTSUP; + + whal_Reg_Write(base, PKA_RSA_CRT_OP_LEN, + (uint32_t)((pSz + qSz) * 8)); + WriteOperand(base, PKA_RSA_CRT_DP, dP, dPSz); + WriteOperand(base, PKA_RSA_CRT_DQ, dQ, dQSz); + WriteOperand(base, PKA_RSA_CRT_QINV, qInv, qInvSz); + WriteOperand(base, PKA_RSA_CRT_P, p, pSz); + WriteOperand(base, PKA_RSA_CRT_Q, q, qSz); + WriteOperand(base, PKA_RSA_CRT_A, A, ASz); + + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_MODE_Msk, + (uint32_t)PKA_MODE_RSA_CRT_EXP << PKA_CR_MODE_Pos); + whal_Reg_Update(base, PKA_CR_REG, PKA_CR_START_Msk, PKA_CR_START_Msk); + + err = WaitForProcEnd(base, cfg->timeout); + if (err == WHAL_SUCCESS) + ReadOperand(base, PKA_RSA_CRT_RESULT, result, resultSz); + + whal_Reg_Write(base, PKA_RSA_CRT_OP_LEN, 0); + ZeroOperand(base, PKA_RSA_CRT_DP, dPSz); + ZeroOperand(base, PKA_RSA_CRT_DQ, dQSz); + ZeroOperand(base, PKA_RSA_CRT_QINV, qInvSz); + ZeroOperand(base, PKA_RSA_CRT_P, pSz); + ZeroOperand(base, PKA_RSA_CRT_Q, qSz); + ZeroOperand(base, PKA_RSA_CRT_A, ASz); + ZeroOperand(base, PKA_RSA_CRT_RESULT, resultSz); + return err; +} + +/* ---- Vtable ---- */ + +#ifndef WHAL_CFG_STM32WB_PKA_INIT_DIRECT_API_MAPPING +const whal_CryptoDriver whal_Stm32wb_Pka_CryptoDriver = { + .Init = whal_Stm32wb_Pka_Init, + .Deinit = whal_Stm32wb_Pka_Deinit, +}; +#endif diff --git a/tests/crypto/gen_test_vectors.py b/tests/crypto/gen_test_vectors.py index b523365..a9f121d 100644 --- a/tests/crypto/gen_test_vectors.py +++ b/tests/crypto/gen_test_vectors.py @@ -164,3 +164,42 @@ def hmac_mac(algo, key, data): emit("hmacSha1StreamMac", hmac_mac(hashes.SHA1(), hmac_key, LONG_BUF)) emit("hmacSha224StreamMac", hmac_mac(hashes.SHA224(), hmac_key, LONG_BUF)) emit("hmacSha256StreamMac", hmac_mac(hashes.SHA256(), hmac_key, LONG_BUF)) + + +# ---- RSA-1024 raw primitive KAT ---- +# Fixed key matches the rsaN / rsaE / rsaD / ... arrays in test_pka.c. +# Test plaintext is 0x00..0x7F (128 bytes), guaranteed < n (which starts +# with 0xBD). The PKA computes raw RSA primitives — no padding — so the +# expected ciphertext and signature are simply pow(m, e, n) and pow(m, d, n). + +rsa_n = int.from_bytes(bytes.fromhex( + "BDF8AE2B270559303A254D077AE73339B1F9DCE4A72EAD07CEB5BA9F27D6FA6B" + "F73A92EEB7B80907BA35A4F1FDDFB590E3E7A7A63FEE401EF0E519DB786C5B3A" + "E4235EC4846AE15EF3657A215A78CFA4E4D3F5006044EC8EC8A4160057950F6A" + "A3D68476311F5A8C0194B3D958845356A1D794EF3A8AD0CB06F35EC75E479691" +), 'big') + +rsa_e = 65537 + +rsa_d = int.from_bytes(bytes.fromhex( + "78858EA9DDE5ACC1C601E82EB8B079753F0E902F98492A896B812BD6D145B836" + "4AF1244AC2CFC51ADBEE30A93E3171556CE18921162A270FFA30BF08D80B968C" + "5C32D3C9F49B793F1AACB719956391611C856191CC3F9F48AD44AD77FF44BBEC" + "98685D499B89CBFA85035E0F4B136C85B487282135A611CB5B5029500073DCE9" +), 'big') + +rsa_pt_bytes = bytes(range(128)) +rsa_pt = int.from_bytes(rsa_pt_bytes, 'big') + +assert rsa_pt < rsa_n, "plaintext must be < n for raw RSA" + +rsa_ct = pow(rsa_pt, rsa_e, rsa_n) +rsa_sig = pow(rsa_pt, rsa_d, rsa_n) + +# Self-check: signature verified with public key should recover the plaintext. +assert pow(rsa_sig, rsa_e, rsa_n) == rsa_pt +assert pow(rsa_ct, rsa_d, rsa_n) == rsa_pt + +banner("test_pka.c — known-answer vectors") +emit("expectedCtPublicExp", rsa_ct.to_bytes(128, 'big')) +emit("expectedCtPrivateExp", rsa_sig.to_bytes(128, 'big')) diff --git a/tests/crypto/test_pka.c b/tests/crypto/test_pka.c new file mode 100644 index 0000000..19ace2d --- /dev/null +++ b/tests/crypto/test_pka.c @@ -0,0 +1,389 @@ +/* test_pka.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include +#include "board.h" +#include "test.h" + +/* RSA-1024 test key. Generated with `openssl genrsa 1024` for test use only. + * Stored as raw big-endian byte arrays (leading ASN.1 sign-padding bytes + * stripped). The values are exercised here as plain multi-precision + * integers — the Pka API has no notion of "RSA key", but the components + * satisfy the RSA invariants (n = p*q, d = e^-1 mod phi(n), etc.). */ + +static const uint8_t rsaN[128] = { + 0xBD, 0xF8, 0xAE, 0x2B, 0x27, 0x05, 0x59, 0x30, + 0x3A, 0x25, 0x4D, 0x07, 0x7A, 0xE7, 0x33, 0x39, + 0xB1, 0xF9, 0xDC, 0xE4, 0xA7, 0x2E, 0xAD, 0x07, + 0xCE, 0xB5, 0xBA, 0x9F, 0x27, 0xD6, 0xFA, 0x6B, + 0xF7, 0x3A, 0x92, 0xEE, 0xB7, 0xB8, 0x09, 0x07, + 0xBA, 0x35, 0xA4, 0xF1, 0xFD, 0xDF, 0xB5, 0x90, + 0xE3, 0xE7, 0xA7, 0xA6, 0x3F, 0xEE, 0x40, 0x1E, + 0xF0, 0xE5, 0x19, 0xDB, 0x78, 0x6C, 0x5B, 0x3A, + 0xE4, 0x23, 0x5E, 0xC4, 0x84, 0x6A, 0xE1, 0x5E, + 0xF3, 0x65, 0x7A, 0x21, 0x5A, 0x78, 0xCF, 0xA4, + 0xE4, 0xD3, 0xF5, 0x00, 0x60, 0x44, 0xEC, 0x8E, + 0xC8, 0xA4, 0x16, 0x00, 0x57, 0x95, 0x0F, 0x6A, + 0xA3, 0xD6, 0x84, 0x76, 0x31, 0x1F, 0x5A, 0x8C, + 0x01, 0x94, 0xB3, 0xD9, 0x58, 0x84, 0x53, 0x56, + 0xA1, 0xD7, 0x94, 0xEF, 0x3A, 0x8A, 0xD0, 0xCB, + 0x06, 0xF3, 0x5E, 0xC7, 0x5E, 0x47, 0x96, 0x91, +}; + +/* Zero-padded to NSz (128) to satisfy the equal-operand-size contract. */ +static const uint8_t rsaE[128] = { + [125] = 0x01, [126] = 0x00, [127] = 0x01, +}; + +static const uint8_t rsaD[128] = { + 0x78, 0x85, 0x8E, 0xA9, 0xDD, 0xE5, 0xAC, 0xC1, + 0xC6, 0x01, 0xE8, 0x2E, 0xB8, 0xB0, 0x79, 0x75, + 0x3F, 0x0E, 0x90, 0x2F, 0x98, 0x49, 0x2A, 0x89, + 0x6B, 0x81, 0x2B, 0xD6, 0xD1, 0x45, 0xB8, 0x36, + 0x4A, 0xF1, 0x24, 0x4A, 0xC2, 0xCF, 0xC5, 0x1A, + 0xDB, 0xEE, 0x30, 0xA9, 0x3E, 0x31, 0x71, 0x55, + 0x6C, 0xE1, 0x89, 0x21, 0x16, 0x2A, 0x27, 0x0F, + 0xFA, 0x30, 0xBF, 0x08, 0xD8, 0x0B, 0x96, 0x8C, + 0x5C, 0x32, 0xD3, 0xC9, 0xF4, 0x9B, 0x79, 0x3F, + 0x1A, 0xAC, 0xB7, 0x19, 0x95, 0x63, 0x91, 0x61, + 0x1C, 0x85, 0x61, 0x91, 0xCC, 0x3F, 0x9F, 0x48, + 0xAD, 0x44, 0xAD, 0x77, 0xFF, 0x44, 0xBB, 0xEC, + 0x98, 0x68, 0x5D, 0x49, 0x9B, 0x89, 0xCB, 0xFA, + 0x85, 0x03, 0x5E, 0x0F, 0x4B, 0x13, 0x6C, 0x85, + 0xB4, 0x87, 0x28, 0x21, 0x35, 0xA6, 0x11, 0xCB, + 0x5B, 0x50, 0x29, 0x50, 0x00, 0x73, 0xDC, 0xE9, +}; + +static const uint8_t rsaP[64] = { + 0xE4, 0xBD, 0xEA, 0x46, 0xD8, 0x5F, 0x77, 0xF3, + 0x00, 0x5E, 0xA9, 0x5F, 0x32, 0xFB, 0x1B, 0x85, + 0x6E, 0xF9, 0xDB, 0x93, 0x0E, 0x41, 0xA9, 0x47, + 0x57, 0xE0, 0xC2, 0xE5, 0x5D, 0xC3, 0x3A, 0x7B, + 0x83, 0xD8, 0x5B, 0x28, 0xFA, 0x50, 0xD0, 0x53, + 0x4D, 0x54, 0x38, 0x02, 0x05, 0xED, 0xCD, 0x42, + 0xA9, 0x4D, 0xA3, 0x60, 0x55, 0xA0, 0xC3, 0x2D, + 0x58, 0x64, 0xF1, 0x68, 0xFD, 0x9D, 0xD6, 0x0F, +}; + +static const uint8_t rsaQ[64] = { + 0xD4, 0x9C, 0x04, 0xF4, 0xD2, 0x64, 0xFF, 0xA0, + 0x75, 0x23, 0xBE, 0x2F, 0xCF, 0x2A, 0x9D, 0x6C, + 0x73, 0x0A, 0x91, 0x46, 0x20, 0x18, 0x33, 0x28, + 0x6E, 0x0E, 0xCE, 0x60, 0xF3, 0xF4, 0x1E, 0x96, + 0xE7, 0x6A, 0x5B, 0x16, 0x8E, 0xFE, 0x13, 0x63, + 0x31, 0xBF, 0x24, 0x1C, 0x53, 0x0D, 0x2F, 0x96, + 0xA1, 0x62, 0xCA, 0xED, 0x1F, 0xB4, 0xEB, 0xBB, + 0x14, 0xD4, 0x2B, 0xC8, 0x3E, 0x87, 0x69, 0x5F, +}; + +static const uint8_t rsaDP[64] = { + 0xC0, 0x40, 0x46, 0xA9, 0xA8, 0xAE, 0x04, 0xB8, + 0xBE, 0x74, 0xD1, 0x51, 0xEC, 0x1D, 0x55, 0x32, + 0x3E, 0xE5, 0xCD, 0x80, 0xB9, 0x98, 0xC2, 0x57, + 0xEE, 0x2A, 0x40, 0x64, 0x5A, 0xFB, 0xFD, 0x29, + 0x56, 0xD4, 0xFF, 0x39, 0x5F, 0xB9, 0x90, 0x7E, + 0x93, 0x1D, 0xD7, 0xBD, 0x0B, 0x92, 0xA4, 0xD4, + 0x04, 0x4B, 0xF6, 0xA6, 0xBF, 0x11, 0x51, 0x39, + 0xC9, 0x73, 0x7D, 0xDB, 0x4A, 0x5D, 0xE0, 0x4D, +}; + +static const uint8_t rsaDQ[64] = { + 0xD1, 0xE5, 0xBA, 0x2A, 0xED, 0xAF, 0xD2, 0x82, + 0xDA, 0x42, 0xD0, 0x57, 0xA8, 0xE2, 0x3C, 0x8A, + 0xBD, 0xC4, 0x98, 0x09, 0xB0, 0x53, 0xD3, 0xE5, + 0xBD, 0x4A, 0x16, 0xCB, 0xB6, 0xEC, 0xB2, 0x6A, + 0x6E, 0xCB, 0x1C, 0x64, 0x58, 0x05, 0x54, 0xA4, + 0xA0, 0x8F, 0x41, 0x1A, 0x49, 0x90, 0x02, 0x04, + 0x67, 0x0D, 0xFE, 0xA3, 0x3D, 0xA3, 0x42, 0xFA, + 0xEE, 0xA4, 0x4D, 0x19, 0x23, 0x4A, 0x52, 0x67, +}; + +static const uint8_t rsaQInv[64] = { + 0x29, 0x20, 0x33, 0x9A, 0xBC, 0xCB, 0x09, 0x29, + 0x77, 0x35, 0xCF, 0x50, 0xEA, 0x2B, 0x5E, 0xFA, + 0xE2, 0x22, 0xB7, 0x45, 0x69, 0xA9, 0x2A, 0xE2, + 0x2C, 0x07, 0xAC, 0xE5, 0x77, 0x0D, 0x52, 0xEF, + 0xCC, 0x86, 0x88, 0x04, 0x37, 0x24, 0x26, 0x06, + 0x90, 0x7B, 0x57, 0x7F, 0xBE, 0x03, 0x5F, 0x7B, + 0x3B, 0x7E, 0x12, 0x04, 0x59, 0x08, 0xDA, 0x5B, + 0x5E, 0xEB, 0xC4, 0xE7, 0xEF, 0x9F, 0x71, 0xA8, +}; + +/* Expected raw ciphertext = pow(testInput, e, n) and signature = + * pow(testInput, d, n). Generated by tests/crypto/gen_test_vectors.py + * from the same operands; re-run that script to regenerate. */ +static const uint8_t expectedCtPublicExp[128] = { + 0x52, 0xE5, 0x6D, 0xAD, 0xB6, 0xFC, 0xBC, 0x94, + 0x6B, 0x75, 0x2B, 0x5F, 0x27, 0x7B, 0x2C, 0x8A, + 0x88, 0x65, 0x33, 0xA5, 0x9D, 0x58, 0x03, 0x52, + 0xA1, 0x4E, 0x41, 0x6D, 0x4C, 0x75, 0xAC, 0x44, + 0xE7, 0xCA, 0x81, 0x58, 0x9D, 0x44, 0x24, 0x1A, + 0xC0, 0xED, 0x20, 0x34, 0xE4, 0xE8, 0x12, 0x7A, + 0xB5, 0xFD, 0x8A, 0xC0, 0xA6, 0x7B, 0x0C, 0x09, + 0xB1, 0xD3, 0x89, 0x27, 0x51, 0xC5, 0xD7, 0xDC, + 0xD1, 0x60, 0x72, 0x1E, 0x10, 0x49, 0x57, 0x03, + 0xA7, 0x48, 0x9D, 0x2F, 0xBD, 0xD1, 0x4D, 0x9C, + 0x6E, 0xBC, 0x5D, 0x71, 0xFD, 0xB1, 0x88, 0xCC, + 0x66, 0xA1, 0x29, 0xAC, 0xA4, 0x6A, 0x3D, 0x88, + 0x43, 0xE5, 0xF0, 0x8F, 0xFF, 0xBC, 0x20, 0x83, + 0x28, 0x61, 0x55, 0x36, 0xB2, 0x76, 0x6B, 0xC8, + 0x4C, 0xB2, 0x46, 0xDE, 0x83, 0xDF, 0x9D, 0x93, + 0xF6, 0xD3, 0x63, 0x04, 0x94, 0x9C, 0x10, 0x0E, +}; + +static const uint8_t expectedCtPrivateExp[128] = { + 0x90, 0xF1, 0xFC, 0x0E, 0x0B, 0x01, 0x3B, 0x16, + 0xB3, 0xDE, 0xF3, 0x8C, 0x2B, 0x53, 0x55, 0xCF, + 0x73, 0xDA, 0xD2, 0xC2, 0xB4, 0x1F, 0x46, 0x8F, + 0xBC, 0x59, 0xFE, 0xF5, 0xAF, 0x46, 0x32, 0x97, + 0xB8, 0x59, 0x70, 0xFF, 0xE4, 0xE0, 0x79, 0x28, + 0xB8, 0x23, 0x26, 0x5A, 0x5B, 0xCF, 0x74, 0x3A, + 0xC4, 0xAD, 0x73, 0xC1, 0x94, 0x51, 0x09, 0xA6, + 0x10, 0x26, 0x75, 0x6F, 0xFF, 0x04, 0xD7, 0xA8, + 0x6D, 0xBF, 0x6E, 0x50, 0x6F, 0x4B, 0x01, 0x22, + 0x30, 0xD0, 0xBE, 0x06, 0xF5, 0xD2, 0x43, 0xE7, + 0x25, 0x17, 0x88, 0x46, 0x2C, 0x87, 0x17, 0xF4, + 0x2D, 0x2A, 0xC3, 0x09, 0x27, 0xE8, 0x1E, 0xFF, + 0x07, 0x55, 0x32, 0x90, 0x65, 0x92, 0xC3, 0xB3, + 0xFD, 0x0A, 0x53, 0xDC, 0x77, 0x87, 0x85, 0x3D, + 0x26, 0xC7, 0xEF, 0x2A, 0xAB, 0xCC, 0xCF, 0x93, + 0x65, 0x3A, 0xEE, 0xAE, 0x15, 0x54, 0xA4, 0xBF, +}; + +/* Input: 128 bytes whose value as a big-endian integer is well below n + * (n starts with 0xBD; this starts with 0x00, so input < n is guaranteed). */ +static const uint8_t testInput[128] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, +}; + +/* ExpMod with the public exponent e (the "Encrypt"/"Verify" direction) + * followed by ExpMod with the private exponent d (the "Decrypt"/"Sign" + * direction) should recover the original input. */ +static void Test_Pka_ModExp_RoundTrip(void) +{ + uint8_t ct[128]; + uint8_t recovered[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + ct, sizeof(ct)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Pka_ModExp(ct, sizeof(ct), + rsaD, sizeof(rsaD), + rsaN, sizeof(rsaN), + recovered, sizeof(recovered)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(recovered, testInput, sizeof(testInput)); +} + +/* ExpMod with public exponent followed by RsaCrtExp with the CRT params + * should also recover the original input. Validates the CRT path. */ +static void Test_Pka_RsaCrtExp_RoundTrip(void) +{ + uint8_t ct[128]; + uint8_t recovered[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + ct, sizeof(ct)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(ct, sizeof(ct), + rsaDP, sizeof(rsaDP), + rsaDQ, sizeof(rsaDQ), + rsaQInv, sizeof(rsaQInv), + rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + recovered, sizeof(recovered)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(recovered, testInput, sizeof(testInput)); +} + +/* Plain ExpMod-with-d and RsaCrtExp must produce identical results for + * the same ciphertext. Catches CRT-vs-plain divergence. */ +static void Test_Pka_CrtMatchesPlain(void) +{ + uint8_t ct[128]; + uint8_t plainOut[128]; + uint8_t crtOut[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + ct, sizeof(ct)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Pka_ModExp(ct, sizeof(ct), + rsaD, sizeof(rsaD), + rsaN, sizeof(rsaN), + plainOut, sizeof(plainOut)), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(ct, sizeof(ct), + rsaDP, sizeof(rsaDP), + rsaDQ, sizeof(rsaDQ), + rsaQInv, sizeof(rsaQInv), + rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + crtOut, sizeof(crtOut)), + WHAL_SUCCESS); + + WHAL_ASSERT_MEM_EQ(plainOut, testInput, sizeof(testInput)); + WHAL_ASSERT_MEM_EQ(crtOut, testInput, sizeof(testInput)); +} + +/* Known-answer tests against vectors generated off-device by + * gen_test_vectors.py. These pin the exact bytes the PKA must produce. */ + +static void Test_Pka_ModExp_KnownAnswerPublic(void) +{ + uint8_t ct[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaE, sizeof(rsaE), + rsaN, sizeof(rsaN), + ct, sizeof(ct)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, expectedCtPublicExp, sizeof(expectedCtPublicExp)); +} + +static void Test_Pka_ModExp_KnownAnswerPrivate(void) +{ + uint8_t ct[128]; + + WHAL_ASSERT_EQ(whal_Pka_ModExp(testInput, sizeof(testInput), + rsaD, sizeof(rsaD), + rsaN, sizeof(rsaN), + ct, sizeof(ct)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(ct, expectedCtPrivateExp, sizeof(expectedCtPrivateExp)); +} + +static void Test_Pka_RsaCrtExp_KnownAnswer(void) +{ + uint8_t ct[128]; + + WHAL_ASSERT_EQ(whal_Pka_RsaCrtExp(testInput, sizeof(testInput), + rsaDP, sizeof(rsaDP), + rsaDQ, sizeof(rsaDQ), + rsaQInv, sizeof(rsaQInv), + rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + ct, sizeof(ct)), + WHAL_SUCCESS); + /* CRT and plain compute the same function. */ + WHAL_ASSERT_MEM_EQ(ct, expectedCtPrivateExp, sizeof(expectedCtPrivateExp)); +} + +/* q^-1 mod p == qInv is one of the RSA CRT invariants, so the existing + * rsaQ / rsaP / rsaQInv arrays double as a free known-answer vector. */ +static void Test_Pka_ModInv_KnownAnswer(void) +{ + uint8_t out[64]; + + WHAL_ASSERT_EQ(whal_Pka_ModInv(rsaQ, sizeof(rsaQ), + rsaP, sizeof(rsaP), + out, sizeof(out)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(out, rsaQInv, sizeof(rsaQInv)); +} + +/* p * q == n is the RSA modulus invariant, exercising IntMul against + * the existing key vectors. */ +static void Test_Pka_IntMul_KnownAnswer(void) +{ + uint8_t out[128]; + + WHAL_ASSERT_EQ(whal_Pka_IntMul(rsaP, sizeof(rsaP), + rsaQ, sizeof(rsaQ), + out, sizeof(out)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(out, rsaN, sizeof(rsaN)); +} + +/* 0xFF mod 0xA0 = 0x5F (255 - 160). */ +static void Test_Pka_ModReduce_KnownAnswer(void) +{ + static const uint8_t A[4] = { 0x00, 0x00, 0x00, 0xFF }; + static const uint8_t N[4] = { 0x00, 0x00, 0x00, 0xA0 }; + static const uint8_t expected[4] = { 0x00, 0x00, 0x00, 0x5F }; + uint8_t out[4]; + + WHAL_ASSERT_EQ(whal_Pka_ModReduce(A, sizeof(A), + N, sizeof(N), + out, sizeof(out)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(out, expected, sizeof(expected)); +} + +/* 0x10000000 - 0x01234567 = 0x0EDCBA99. */ +static void Test_Pka_IntSub_KnownAnswer(void) +{ + static const uint8_t A[4] = { 0x10, 0x00, 0x00, 0x00 }; + static const uint8_t B[4] = { 0x01, 0x23, 0x45, 0x67 }; + static const uint8_t expected[4] = { 0x0E, 0xDC, 0xBA, 0x99 }; + uint8_t out[4]; + + WHAL_ASSERT_EQ(whal_Pka_IntSub(A, sizeof(A), + B, sizeof(B), + out, sizeof(out)), + WHAL_SUCCESS); + WHAL_ASSERT_MEM_EQ(out, expected, sizeof(expected)); +} + +void whal_Test_Pka(void) +{ + WHAL_TEST_SUITE_START("pka"); + WHAL_TEST(Test_Pka_ModExp_RoundTrip); + WHAL_TEST(Test_Pka_RsaCrtExp_RoundTrip); + WHAL_TEST(Test_Pka_CrtMatchesPlain); + WHAL_TEST(Test_Pka_ModExp_KnownAnswerPublic); + WHAL_TEST(Test_Pka_ModExp_KnownAnswerPrivate); + WHAL_TEST(Test_Pka_RsaCrtExp_KnownAnswer); + WHAL_TEST(Test_Pka_ModInv_KnownAnswer); + WHAL_TEST(Test_Pka_IntMul_KnownAnswer); + WHAL_TEST(Test_Pka_ModReduce_KnownAnswer); + WHAL_TEST(Test_Pka_IntSub_KnownAnswer); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/main.c b/tests/main.c index c64fea8..13854ae 100644 --- a/tests/main.c +++ b/tests/main.c @@ -98,6 +98,10 @@ void whal_Test_AesGmac(void); void whal_Test_AesCcm(void); #endif +#ifdef WHAL_TEST_ENABLE_PKA +void whal_Test_Pka(void); +#endif + #ifdef WHAL_TEST_ENABLE_SHA1 void whal_Test_Sha1(void); #endif @@ -275,6 +279,10 @@ void main(void) whal_Test_AesCcm(); #endif +#ifdef WHAL_TEST_ENABLE_PKA + whal_Test_Pka(); +#endif + #ifdef WHAL_TEST_ENABLE_SHA1 whal_Test_Sha1(); #endif diff --git a/wolfHAL/crypto/crypto.h b/wolfHAL/crypto/crypto.h index 8937be8..e09e444 100644 --- a/wolfHAL/crypto/crypto.h +++ b/wolfHAL/crypto/crypto.h @@ -905,4 +905,9 @@ whal_Error whal_HmacSha256_Process(whal_HmacSha256 *dev, whal_Error whal_HmacSha256_Finalize(whal_HmacSha256 *dev, void *digest, size_t digestSz); +/* RSA / public-key math primitives live in as + * direct functions, not in this header. The crypto.h device-typed pattern + * is for peripherals with per-instance configuration; the math primitives + * don't need that layer. */ + #endif /* WHAL_CRYPTO_H */ diff --git a/wolfHAL/crypto/pka.h b/wolfHAL/crypto/pka.h new file mode 100644 index 0000000..82586d3 --- /dev/null +++ b/wolfHAL/crypto/pka.h @@ -0,0 +1,178 @@ +/* pka.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WHAL_PKA_H +#define WHAL_PKA_H + +#include +#include +#include + +/** + * @file pka.h + * @brief Public-key accelerator math primitives. + * + * Direct-call API for big-integer arithmetic. A platform driver provides + * the implementations and aliases its platform-prefixed names to the + * whal_Pka_* generic names below via its direct-API-mapping flag. + * + * All operands are big-endian byte arrays. No device handle is passed — + * the underlying peripheral state lives in the backend's own static + * storage. + */ + +/** + * @brief Modular exponentiation: result = A^e mod N. + * + * @param A Base, big-endian. + * @param ASz Size of A in bytes. + * @param e Exponent, big-endian. + * @param eSz Size of e in bytes. + * @param N Modulus, big-endian (odd). + * @param NSz Size of N in bytes. + * @param result Output buffer (must be at least NSz bytes). + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds backend hardware limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE Backend reported a hardware error during the op. + * @retval WHAL_ETIMEOUT Backend polling timeout expired. + */ +whal_Error whal_Pka_ModExp(const uint8_t *A, size_t ASz, + const uint8_t *e, size_t eSz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Modular inversion: result = A^-1 mod N. + * + * @param A Operand, big-endian. + * @param ASz Size of A in bytes. + * @param N Modulus, big-endian (odd). + * @param NSz Size of N in bytes. + * @param result Output buffer. + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds backend hardware limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE Backend reported a hardware error during the op. + * @retval WHAL_ETIMEOUT Backend polling timeout expired. + */ +whal_Error whal_Pka_ModInv(const uint8_t *A, size_t ASz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Modular reduction: result = A mod N. + * + * @param A Operand, big-endian. + * @param ASz Size of A in bytes. + * @param N Modulus, big-endian. + * @param NSz Size of N in bytes. + * @param result Output buffer. + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds backend hardware limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE Backend reported a hardware error during the op. + * @retval WHAL_ETIMEOUT Backend polling timeout expired. + */ +whal_Error whal_Pka_ModReduce(const uint8_t *A, size_t ASz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Big-integer multiplication: result = A * B (non-modular). + * + * @param A Operand A, big-endian. + * @param ASz Size of A in bytes. + * @param B Operand B, big-endian. + * @param BSz Size of B in bytes. + * @param result Output buffer (sized for A*B). + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds backend hardware limit, or + * ASz != BSz, or resultSz != 2 * ASz. + * @retval WHAL_EHARDWARE Backend reported a hardware error during the op. + * @retval WHAL_ETIMEOUT Backend polling timeout expired. + */ +whal_Error whal_Pka_IntMul(const uint8_t *A, size_t ASz, + const uint8_t *B, size_t BSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Big-integer subtraction: result = A - B (non-modular). + * + * @param A Operand A, big-endian. + * @param ASz Size of A in bytes. + * @param B Operand B, big-endian. + * @param BSz Size of B in bytes. + * @param result Output buffer. + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds backend hardware limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE Backend reported a hardware error during the op. + * @retval WHAL_ETIMEOUT Backend polling timeout expired. + */ +whal_Error whal_Pka_IntSub(const uint8_t *A, size_t ASz, + const uint8_t *B, size_t BSz, + uint8_t *result, size_t resultSz); + +/** + * @brief RSA CRT exponentiation: result = A^d mod (p*q) via CRT decomposition. + * + * @param A Operand (ciphertext or message), big-endian. + * @param ASz Size of A in bytes. + * @param dP d mod (p-1), big-endian. + * @param dPSz Size of dP in bytes. + * @param dQ d mod (q-1), big-endian. + * @param dQSz Size of dQ in bytes. + * @param qInv q^-1 mod p, big-endian. + * @param qInvSz Size of qInv in bytes. + * @param p Prime p, big-endian. + * @param pSz Size of p in bytes. + * @param q Prime q, big-endian. + * @param qSz Size of q in bytes. + * @param result Output buffer (must be at least pSz + qSz bytes). + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Combined modulus size exceeds backend hardware limit, or CRT operand size layout violated (pSz == qSz == dPSz == dQSz == qInvSz, ASz == resultSz == pSz + qSz). + * @retval WHAL_EHARDWARE Backend reported a hardware error during the op. + * @retval WHAL_ETIMEOUT Backend polling timeout expired. + */ +whal_Error whal_Pka_RsaCrtExp(const uint8_t *A, size_t ASz, + const uint8_t *dP, size_t dPSz, + const uint8_t *dQ, size_t dQSz, + const uint8_t *qInv, size_t qInvSz, + const uint8_t *p, size_t pSz, + const uint8_t *q, size_t qSz, + uint8_t *result, size_t resultSz); + +#endif /* WHAL_PKA_H */ diff --git a/wolfHAL/crypto/stm32wb_pka.h b/wolfHAL/crypto/stm32wb_pka.h new file mode 100644 index 0000000..0b5adfd --- /dev/null +++ b/wolfHAL/crypto/stm32wb_pka.h @@ -0,0 +1,229 @@ +/* stm32wb_pka.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHAL. + * + * wolfHAL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHAL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WHAL_STM32WB_PKA_H +#define WHAL_STM32WB_PKA_H + +#include +#include + +/** + * @file stm32wb_pka.h + * @brief STM32WB public-key accelerator (PKA) driver. + * + * Owns the PKA's whal_Crypto lifecycle (Init/Deinit) and provides the + * six math primitive implementations. With direct-API mapping enabled, + * the platform-prefixed math functions become the whal_Pka_* generic + * names declared in . + */ + +/** + * @brief PKA device configuration. + */ +typedef struct { + whal_Timeout *timeout; +} whal_Stm32wb_Pka_Cfg; + +/* ---- Direct API mapping ---- */ + +#ifdef WHAL_CFG_STM32WB_PKA_INIT_DIRECT_API_MAPPING +#define whal_Stm32wb_Pka_Init whal_Crypto_Init +#define whal_Stm32wb_Pka_Deinit whal_Crypto_Deinit +#endif + +/* The whal_Stm32wb_Pka_* math-primitive → whal_Pka_* alias macros live in + * src/crypto/stm32wb_pka.c (gated on WHAL_CFG_STM32WB_PKA_DIRECT_API_MAPPING). + * Callers should use the generic whal_Pka_* names declared in + * . */ + +/* ---- Init / Deinit ---- */ + +/** + * @brief Initialize the STM32WB PKA peripheral. + * + * @param dev Crypto device instance. + * + * @retval WHAL_SUCCESS PKA peripheral enabled. + */ +whal_Error whal_Stm32wb_Pka_Init(whal_Crypto *dev); + +/** + * @brief Deinitialize the STM32WB PKA peripheral. + * + * @param dev Crypto device instance. + * + * @retval WHAL_SUCCESS PKA peripheral disabled. + */ +whal_Error whal_Stm32wb_Pka_Deinit(whal_Crypto *dev); + +/* ---- Math primitives ---- */ + +/** + * @brief Modular exponentiation: result = A^e mod N. + * + * @param A Base, big-endian. + * @param ASz Size of A in bytes. + * @param e Exponent, big-endian. + * @param eSz Size of e in bytes. + * @param N Modulus, big-endian (odd). + * @param NSz Size of N in bytes. + * @param result Output buffer (must be at least NSz bytes). + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds the 3136-bit PKA limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE PKA reported ADDRERR or RAMERR. + * @retval WHAL_ETIMEOUT Polling timeout expired waiting for PROCENDF. + */ +whal_Error whal_Stm32wb_Pka_ModExp(const uint8_t *A, size_t ASz, + const uint8_t *e, size_t eSz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Modular inversion: result = A^-1 mod N. + * + * @param A Operand, big-endian. + * @param ASz Size of A in bytes. + * @param N Modulus, big-endian (odd). + * @param NSz Size of N in bytes. + * @param result Output buffer. + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds the 3136-bit PKA limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE PKA reported ADDRERR or RAMERR. + * @retval WHAL_ETIMEOUT Polling timeout expired waiting for PROCENDF. + */ +whal_Error whal_Stm32wb_Pka_ModInv(const uint8_t *A, size_t ASz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Modular reduction: result = A mod N. + * + * @param A Operand, big-endian. + * @param ASz Size of A in bytes. + * @param N Modulus, big-endian. + * @param NSz Size of N in bytes. + * @param result Output buffer. + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds the 3136-bit PKA limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE PKA reported ADDRERR or RAMERR. + * @retval WHAL_ETIMEOUT Polling timeout expired waiting for PROCENDF. + */ +whal_Error whal_Stm32wb_Pka_ModReduce(const uint8_t *A, size_t ASz, + const uint8_t *N, size_t NSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Big-integer multiplication: result = A * B (non-modular). + * + * @param A Operand A, big-endian. + * @param ASz Size of A in bytes. + * @param B Operand B, big-endian. + * @param BSz Size of B in bytes. + * @param result Output buffer (sized for A*B). + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds the 3136-bit PKA limit, or + * ASz != BSz, or resultSz != 2 * ASz. + * @retval WHAL_EHARDWARE PKA reported ADDRERR or RAMERR. + * @retval WHAL_ETIMEOUT Polling timeout expired waiting for PROCENDF. + */ +whal_Error whal_Stm32wb_Pka_IntMul(const uint8_t *A, size_t ASz, + const uint8_t *B, size_t BSz, + uint8_t *result, size_t resultSz); + +/** + * @brief Big-integer subtraction: result = A - B (non-modular). + * + * @param A Operand A, big-endian. + * @param ASz Size of A in bytes. + * @param B Operand B, big-endian. + * @param BSz Size of B in bytes. + * @param result Output buffer. + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Operand size exceeds the 3136-bit PKA limit, or operand sizes are not all equal (incl. resultSz). + * @retval WHAL_EHARDWARE PKA reported ADDRERR or RAMERR. + * @retval WHAL_ETIMEOUT Polling timeout expired waiting for PROCENDF. + */ +whal_Error whal_Stm32wb_Pka_IntSub(const uint8_t *A, size_t ASz, + const uint8_t *B, size_t BSz, + uint8_t *result, size_t resultSz); + +/** + * @brief RSA CRT exponentiation: result = A^d mod (p*q) via CRT decomposition. + * + * @param A Operand (ciphertext or message), big-endian. + * @param ASz Size of A in bytes. + * @param dP d mod (p-1), big-endian. + * @param dPSz Size of dP in bytes. + * @param dQ d mod (q-1), big-endian. + * @param dQSz Size of dQ in bytes. + * @param qInv q^-1 mod p, big-endian. + * @param qInvSz Size of qInv in bytes. + * @param p Prime p, big-endian. + * @param pSz Size of p in bytes. + * @param q Prime q, big-endian. + * @param qSz Size of q in bytes. + * @param result Output buffer (must be at least pSz + qSz bytes). + * @param resultSz Output size in bytes. + * + * @retval WHAL_SUCCESS Result written to @p result. + * @retval WHAL_EINVAL Null pointer or zero-length operand. + * @retval WHAL_ENOTSUP Combined modulus size exceeds the 3136-bit PKA limit, or CRT operand size layout violated (pSz == qSz == dPSz == dQSz == qInvSz, ASz == resultSz == pSz + qSz). + * @retval WHAL_EHARDWARE PKA reported ADDRERR or RAMERR. + * @retval WHAL_ETIMEOUT Polling timeout expired waiting for PROCENDF. + */ +whal_Error whal_Stm32wb_Pka_RsaCrtExp(const uint8_t *A, size_t ASz, + const uint8_t *dP, size_t dPSz, + const uint8_t *dQ, size_t dQSz, + const uint8_t *qInv, size_t qInvSz, + const uint8_t *p, size_t pSz, + const uint8_t *q, size_t qSz, + uint8_t *result, size_t resultSz); + +/* ---- Single-instance device extern ---- */ + +/* + * @brief Platform-owned PKA peripheral device. Defined in the driver TU + * from the WHAL_CFG_STM32WB_PKA_DEV initializer in board.h. + */ +extern const whal_Crypto whal_Stm32wb_Pka_Dev; + +/* ---- Vtable extern ---- */ + +#ifndef WHAL_CFG_STM32WB_PKA_INIT_DIRECT_API_MAPPING +extern const whal_CryptoDriver whal_Stm32wb_Pka_CryptoDriver; +#endif + +#endif /* WHAL_STM32WB_PKA_H */ diff --git a/wolfHAL/platform/st/stm32wb55xx.h b/wolfHAL/platform/st/stm32wb55xx.h index e7d81c1..7f024c2 100644 --- a/wolfHAL/platform/st/stm32wb55xx.h +++ b/wolfHAL/platform/st/stm32wb55xx.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,9 @@ #define WHAL_STM32WB55_AES1_BASE 0x50060000 #define WHAL_STM32WB55_AES1_DRIVER &whal_Stm32wb_Aes_CryptoDriver +#define WHAL_STM32WB55_PKA_BASE 0x58002000 +#define WHAL_STM32WB55_PKA_DRIVER &whal_Stm32wb_Pka_CryptoDriver + #define WHAL_STM32WB55_I2C1_BASE 0x40005400 #define WHAL_STM32WB55_I2C1_DRIVER &whal_Stm32wb_I2c_Driver @@ -106,6 +110,11 @@ .enableMask = (1UL << 16), \ .enablePos = 16 +#define WHAL_STM32WB55_PKA_GATE \ + .regOffset = 0x50, \ + .enableMask = (1UL << 16), \ + .enablePos = 16 + #define WHAL_STM32WB55_WWDG_GATE \ .regOffset = 0x58, \ .enableMask = (1UL << 11), \