From 5c4aa26609c575f27c8ac9b9e32f2330a7294f6f Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 18 Jun 2026 13:00:25 -0700 Subject: [PATCH] Add RealTek AmebaPro2 (RTL8735B) HUK wolfCrypt example (AES, HMAC-SHA256, ECDSA) --- rtl8735b/README.md | 81 ++++++ rtl8735b/main.c | 543 +++++++++++++++++++++++++++++++++++ rtl8735b/user_settings.h | 79 +++++ rtl8735b/wolfcrypt_huk.cmake | 57 ++++ 4 files changed, 760 insertions(+) create mode 100644 rtl8735b/README.md create mode 100644 rtl8735b/main.c create mode 100644 rtl8735b/user_settings.h create mode 100644 rtl8735b/wolfcrypt_huk.cmake diff --git a/rtl8735b/README.md b/rtl8735b/README.md new file mode 100644 index 00000000..d317ab52 --- /dev/null +++ b/rtl8735b/README.md @@ -0,0 +1,81 @@ +# wolfCrypt on RealTek AmebaPro2 (RTL8735B) -- Hardware Unique Key (HUK) + +This example demonstrates wolfCrypt AES-GCM, AES-ECB, AES-CBC, AES-CTR, HMAC-SHA256 and ECDSA P-256 bound to the RealTek RTL8735B silicon **Hardware Unique Key (HUK)** through the wolfCrypt crypto-callback (CryptoCb) framework. A 256-bit "seed" is run through the AmebaPro2 HAL secure HKDF key-ladder against the HUK to derive a per-purpose working key inside a secure key-storage slot; that working key never enters software. The application just sets the seed as the key and uses the normal wolfCrypt APIs -- the RealTek HUK crypto-callback device does the rest. + +It is built inside the RealTek AmebaPro2 FreeRTOS SDK (which provides the startup, HAL, and the `elf2bin`/`uartfwburn` image+flash tooling). + +## What it shows + +- Registering the HUK crypto-callback device: `wc_Rtl8735b_HukRegister(WC_HUK_DEVID)`. +- AES-GCM (full payload), AES-ECB, AES-CBC and AES-CTR run under the HUK-derived key (init an `Aes` with `devId = WC_HUK_DEVID`, then use the usual `wc_AesGcmSetKey`/`wc_AesGcmEncrypt`, `wc_AesSetKey`/`wc_AesEcb*`/`wc_AesCbc*`, `wc_AesSetKeyDirect`/`wc_AesCtrEncrypt`). +- The key is device-bound and deterministic: the same seed yields the same key (so GMAC verifies and AES round-trips), and a different seed yields a different key (GCM decrypt returns `AES_GCM_AUTH_E`). +- The port tolerates unaligned caller buffers (it bounces them to satisfy the HAL's 32-byte alignment); the example includes an explicitly-unaligned GCM check. +- HMAC-SHA256 under the HUK (`wc_HmacInit` with `devId = WC_HUK_DEVID`, then `wc_HmacSetKey`/`wc_HmacUpdate`/`wc_HmacFinal`): the MAC is deterministic for a given seed and differs for a wrong seed. +- HUK-bound ECDSA P-256 sign: a key is generated in software, its private scalar is wrapped under the HUK (ECB-encrypted with the HUK device), and signing then goes through the HUK device (unwrap + sign); the signature verifies against the software public key. + +## Prerequisites + +- RealTek AmebaPro2 FreeRTOS SDK: https://github.com/Ameba-AIoT/ameba-rtos-pro2 +- RealTek ASDK 10.3.0 toolchain (GCC 10.3.0): https://github.com/Ameba-AIoT/ameba-toolchain (tag `V10.3.0-ameba-rtos-pro2`). The stock system `arm-none-eabi-gcc` does not build the SDK (newlib/lwip header clashes); use the ASDK toolchain. +- wolfSSL with the RealTek HUK port (`wolfcrypt/src/port/realtek/amebapro2.c` and `wolfssl/wolfcrypt/port/realtek/rtl8735b.h`). Enable with `WOLFSSL_RTL8735B_HUK` (which implies `WOLFSSL_DHUK`) and `WOLF_CRYPTO_CB`. + +## Files + +- `main.c` -- the example application (a FreeRTOS task that registers the HUK device and runs the AES-GCM/ECB/CBC/CTR checks plus unaligned-buffer GCM, in-place/multi-call CBC, and non-12-byte-IV rejection checks). +- `user_settings.h` -- a lean wolfCrypt configuration for this example. +- `wolfcrypt_huk.cmake` -- wiring that adds wolfCrypt + the RealTek HUK port to the SDK app build. + +## Build + +1. Copy this directory into the SDK as an example component: + `cp -r rtl8735b /component/example/wolfcrypt_huk` +2. Install the example `main()` as the project app: + `cp /component/example/wolfcrypt_huk/main.c /project/realtek_amebapro2_v0_example/src/main.c` +3. Configure and build with the ASDK toolchain on PATH, pointing `WOLFSSL_ROOT` at your wolfSSL tree: + ``` + export PATH=/asdk-10.3.0/linux/newlib/bin:$PATH + cd /project/realtek_amebapro2_v0_example/GCC-RELEASE + mkdir -p build && cd build + cmake .. -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake \ + -DBUILD_FPGA=OFF -DBUILD_PXP=OFF \ + -DEXAMPLE=wolfcrypt_huk -DWOLFSSL_ROOT= + make flash -j8 + ``` + The output image is `build/flash_ntz.bin`. + +## Flash and run + +Put the board in download mode (J27 jumper + reset), then flash over UART with the SDK `uartfwburn` tool (from the SDK `tools/Pro2_PG_tool*` directory, which also holds `boot_recover.bin`, `flash_loader_nor.bin`, `flash_control_info.bin`): +``` +./uartfwburn.linux -p -f /flash_ntz.bin -b 3000000 -U +``` +Remove the J27 jumper and reset to boot. The console (UART, 115200) prints: +``` +=== wolfCrypt AmebaPro2 (RTL8735B) HUK example === +[PASS] wolfCrypt_Init +[PASS] wc_Rtl8735b_HukRegister +== AES-GCM (full payload) under HUK-derived key == +[PASS] AesInit(devId=WC_HUK_DEVID) +[PASS] AesGcmSetKey(seed,32) +[PASS] AesGcmEncrypt +[PASS] deterministic tag +[PASS] AesGcmDecrypt verifies +[PASS] plaintext round-trips +[PASS] wrong seed -> AES_GCM_AUTH_E +== AES-ECB under HUK-derived key == ... round-trip PASS +== AES-CBC under HUK-derived key == ... round-trip PASS +== AES-CTR under HUK-derived key == ... round-trip PASS +== AES-GCM with UNALIGNED buffers (port bounces) == ... round-trip PASS +== AES-CBC in-place + multi-call under HUK-derived key == ... round-trip PASS +== AES-GCM non-12-byte IV must hard-fail == ... 16-byte IV rejected PASS +== HMAC-SHA256 under HUK-derived key == ... deterministic / wrong-seed PASS +== ECDSA P-256 under HUK-derived key == ... sign + verify PASS +=== done === +``` + +## Notes + +- The AmebaPro2 HAL crypto engine DMAs its key/IV/AAD/tag buffers on 32-byte boundaries; the port stages and bounces buffers as needed, so callers need not align (the example aligns its buffers anyway, and adds an explicitly-unaligned GCM case to exercise the bounce path). +- The seed is HKDF input, not the AES key itself -- it diversifies the HUK into a working key. Keep the seed stable for a given purpose to get a stable derived key. +- HUK-bound ECDSA P-256 signing uses the software-after-unwrap path by default; set `hk.useHwEngine = 1` in `huk_ecdsa_test` to route through the HW ECDSA engine (which requires on-silicon validation), or `hk.otpPrkSel` for an OTP-resident key. +- Port internals (the HKDF key-ladder, slot configuration macros such as `WC_HUK_DEVID`, `WC_AMEBAPRO2_*`) are documented in `wolfssl/wolfcrypt/port/realtek/rtl8735b.h` and `wolfcrypt/src/port/realtek/README.md` in the wolfSSL tree. diff --git a/rtl8735b/main.c b/rtl8735b/main.c new file mode 100644 index 00000000..b107d87a --- /dev/null +++ b/rtl8735b/main.c @@ -0,0 +1,543 @@ +/* wolfCrypt AmebaPro2 (RTL8735B) HUK example -- built inside the RealTek + * FreeRTOS SDK. Registers the wolfCrypt HUK crypto-callback device and runs + * AES-GCM (full payload) / AES-ECB / AES-CBC / AES-CTR, HMAC-SHA256, and ECDSA + * P-256 under a key derived from the silicon Hardware Unique Key; the working + * key never enters software. The 256-bit "seed" passed as the key is HKDF input + * diversifying the HUK. It also runs regression checks: unaligned-buffer GCM + * (port bounce path), in-place multi-call CBC, and a non-12-byte GCM IV that + * must hard-fail (no silent software fallback). + * + * Build: configure with -DEXAMPLE=wolfcrypt_huk (see wolfcrypt_huk.cmake). + */ + +#include + +#include "platform_stdlib.h" +#include "FreeRTOS.h" +#include "task.h" +#include "device_lock.h" +#include "hal_trng_sec.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STACKSIZE 8192 + +#define CHECK(label, cond) \ + dbg_printf("[%s] %s\r\n", (cond) ? "PASS" : "FAIL", (label)) + +/* wolfCrypt RNG seed hook (user_settings: CUSTOM_RAND_GENERATE_SEED). Fills from + * the AmebaPro2 secure (self-tested) hardware TRNG -- the same source the HUK + * port exposes as a crypto-callback SEED. (Using the secure TRNG directly also + * avoids the non-secure hal_trng wrapper, whose C-cut path references a ROM + * symbol not resolvable in a from-source SDK build.) */ +int rtl8735b_rand_seed(unsigned char* output, unsigned int sz) +{ + static int inited = 0; + unsigned int i, n; + u32 r; + + if (!inited) { + if (hal_trng_sec_init() != 0) { + return -1; + } + inited = 1; + } + for (i = 0; i < sz; ) { + r = hal_trng_sec_get_rand(); + n = (sz - i) < 4u ? (sz - i) : 4u; + memcpy(output + i, &r, n); + i += n; + } + return 0; +} + +static void huk_gcm_test(void) +{ + Aes aes; + /* HAL crypto engine requires 32-byte-aligned key/iv/aad/tag buffers. */ + byte seed[32] __attribute__((aligned(32))); + byte iv[12] __attribute__((aligned(32))); + byte aad[16] __attribute__((aligned(32))); + byte pt[32] __attribute__((aligned(32))); + byte ct[32] __attribute__((aligned(32))); + byte dec[32] __attribute__((aligned(32))); + byte tag[16] __attribute__((aligned(32))); + byte tag2[16] __attribute__((aligned(32))); + int ret; + + memset(seed, 0xA5, sizeof(seed)); memset(iv, 0x11, sizeof(iv)); + memset(aad, 0x22, sizeof(aad)); memset(pt, 0x33, sizeof(pt)); + + dbg_printf("\r\n== AES-GCM (full payload) under HUK-derived key ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, seed, sizeof(seed)); + CHECK("AesGcmSetKey(seed,32)", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag, sizeof(tag), aad, sizeof(aad)); + CHECK("AesGcmEncrypt", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + + memset(tag2, 0, sizeof(tag2)); + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag2, sizeof(tag2), aad, sizeof(aad)); + CHECK("deterministic tag", ret == 0 && memcmp(tag, tag2, 16) == 0); + + ret = wc_AesGcmDecrypt(&aes, dec, ct, sizeof(ct), iv, sizeof(iv), + tag, sizeof(tag), aad, sizeof(aad)); + CHECK("AesGcmDecrypt verifies", ret == 0); + CHECK("plaintext round-trips", memcmp(dec, pt, sizeof(pt)) == 0); + + seed[0] ^= 0xFF; + ret = wc_AesGcmSetKey(&aes, seed, sizeof(seed)); + CHECK("AesGcmSetKey(wrong seed)", ret == 0); + ret = wc_AesGcmDecrypt(&aes, dec, ct, sizeof(ct), iv, sizeof(iv), + tag, sizeof(tag), aad, sizeof(aad)); + CHECK("wrong seed -> AES_GCM_AUTH_E", ret == AES_GCM_AUTH_E); + wc_AesFree(&aes); +} + +static void huk_ecb_cbc_test(void) +{ + Aes aes; + byte seed[32] __attribute__((aligned(32))); + byte iv[16] __attribute__((aligned(32))); + byte pt[32] __attribute__((aligned(32))); + byte ct[32] __attribute__((aligned(32))); + byte dec[32] __attribute__((aligned(32))); + int ret; + + memset(seed, 0x5A, sizeof(seed)); memset(iv, 0x44, sizeof(iv)); + memset(pt, 0x77, sizeof(pt)); + + dbg_printf("\r\n== AES-ECB under HUK-derived key ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKey(&aes, seed, sizeof(seed), NULL, AES_ENCRYPTION); + CHECK("AesSetKey(ECB enc)", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + ret = wc_AesEcbEncrypt(&aes, ct, pt, sizeof(pt)); + CHECK("AesEcbEncrypt", ret == 0); + ret = wc_AesSetKey(&aes, seed, sizeof(seed), NULL, AES_DECRYPTION); + CHECK("AesSetKey(ECB dec)", ret == 0); + ret = wc_AesEcbDecrypt(&aes, dec, ct, sizeof(ct)); + CHECK("AesEcb round-trip", ret == 0 && memcmp(dec, pt, sizeof(pt)) == 0); + wc_AesFree(&aes); + + dbg_printf("\r\n== AES-CBC under HUK-derived key ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKey(&aes, seed, sizeof(seed), iv, AES_ENCRYPTION); + CHECK("AesSetKey(CBC enc)", ret == 0); + ret = wc_AesCbcEncrypt(&aes, ct, pt, sizeof(pt)); + CHECK("AesCbcEncrypt", ret == 0); + ret = wc_AesSetKey(&aes, seed, sizeof(seed), iv, AES_DECRYPTION); + CHECK("AesSetKey(CBC dec)", ret == 0); + ret = wc_AesCbcDecrypt(&aes, dec, ct, sizeof(ct)); + CHECK("AesCbc round-trip", ret == 0 && memcmp(dec, pt, sizeof(pt)) == 0); + wc_AesFree(&aes); +} + +static void huk_ctr_test(void) +{ + Aes aes; + byte seed[32] __attribute__((aligned(32))); + byte iv[16] __attribute__((aligned(32))); + byte pt[20] __attribute__((aligned(32))); /* non-block-multiple: partial */ + byte ct[20] __attribute__((aligned(32))); + byte dec[20] __attribute__((aligned(32))); + int ret; + + memset(seed, 0x5A, sizeof(seed)); memset(iv, 0x66, sizeof(iv)); + memset(pt, 0x99, sizeof(pt)); + + dbg_printf("\r\n== AES-CTR under HUK-derived key ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKeyDirect(&aes, seed, sizeof(seed), iv, AES_ENCRYPTION); + CHECK("AesSetKeyDirect(CTR)", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + ret = wc_AesCtrEncrypt(&aes, ct, pt, sizeof(pt)); + CHECK("AesCtrEncrypt", ret == 0); + CHECK("CTR ciphertext != plaintext", memcmp(ct, pt, sizeof(pt)) != 0); + wc_AesFree(&aes); + + /* CTR is its own inverse with the same key+IV */ + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKeyDirect(&aes, seed, sizeof(seed), iv, AES_ENCRYPTION); + CHECK("AesSetKeyDirect(CTR round-trip)", ret == 0); + ret = wc_AesCtrEncrypt(&aes, dec, ct, sizeof(ct)); + CHECK("AesCtr round-trip", ret == 0 && memcmp(dec, pt, sizeof(pt)) == 0); + wc_AesFree(&aes); +} + +static void huk_gcm_unaligned_test(void) +{ + Aes aes; + /* 32-byte-aligned backing; the +1 views below are deliberately unaligned so + * the port's bounce-to-aligned path is exercised. */ + byte buf[7][48] __attribute__((aligned(32))); + byte* seed = buf[0] + 1; + byte* iv = buf[1] + 1; + byte* aad = buf[2] + 1; + byte* pt = buf[3] + 1; + byte* ct = buf[4] + 1; + byte* dec = buf[5] + 1; + byte* tag = buf[6] + 1; + int ret; + + memset(seed, 0xA5, 32); memset(iv, 0x11, 12); + memset(aad, 0x22, 16); memset(pt, 0x33, 32); + + dbg_printf("\r\n== AES-GCM with UNALIGNED buffers (port bounces) ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, seed, 32); + CHECK("AesGcmSetKey (unaligned seed)", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + ret = wc_AesGcmEncrypt(&aes, ct, pt, 32, iv, 12, tag, 16, aad, 16); + CHECK("AesGcmEncrypt (unaligned)", ret == 0); + ret = wc_AesGcmDecrypt(&aes, dec, ct, 32, iv, 12, tag, 16, aad, 16); + CHECK("AesGcmDecrypt (unaligned) verifies", ret == 0); + CHECK("unaligned round-trip", memcmp(dec, pt, 32) == 0); + wc_AesFree(&aes); +} + +/* In-place CBC decrypt (in == out) across two chained calls -- regression for + * the in-place chaining / IV-advance fix. */ +static void huk_cbc_inplace_test(void) +{ + Aes aes; + byte seed[32] __attribute__((aligned(32))); + byte iv[16] __attribute__((aligned(32))); + byte pt[48] __attribute__((aligned(32))); /* 3 blocks */ + byte buf[48] __attribute__((aligned(32))); /* encrypted then decrypted in place */ + int ret, i; + + memset(seed, 0x5A, sizeof(seed)); memset(iv, 0x44, sizeof(iv)); + for (i = 0; i < (int)sizeof(pt); i++) { + pt[i] = (byte)i; + } + memcpy(buf, pt, sizeof(pt)); + + dbg_printf("\r\n== AES-CBC in-place + multi-call under HUK-derived key ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKey(&aes, seed, sizeof(seed), iv, AES_ENCRYPTION); + CHECK("AesSetKey(CBC enc)", ret == 0); + ret = wc_AesCbcEncrypt(&aes, buf, buf, sizeof(buf)); /* in == out */ + CHECK("CBC in-place encrypt", ret == 0); + wc_AesFree(&aes); + + /* Decrypt in place across two calls (32 then 16 bytes) -- the 2nd call relies + * on aes->reg having been advanced to the correct last-ciphertext block. */ + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKey(&aes, seed, sizeof(seed), iv, AES_DECRYPTION); + CHECK("AesSetKey(CBC dec)", ret == 0); + ret = wc_AesCbcDecrypt(&aes, buf, buf, 32); + if (ret == 0) { + ret = wc_AesCbcDecrypt(&aes, buf + 32, buf + 32, 16); + } + CHECK("CBC in-place multi-call decrypt", ret == 0); + CHECK("CBC in-place round-trip", memcmp(buf, pt, sizeof(pt)) == 0); + wc_AesFree(&aes); +} + +/* A non-12-byte GCM IV must hard-fail, NOT silently fall back to software GCM + * keyed on the seed -- regression for the IV hard-error fix. */ +static void huk_gcm_badiv_test(void) +{ + Aes aes; + byte seed[32] __attribute__((aligned(32))); + byte iv[16] __attribute__((aligned(32))); /* 16 bytes, not the supported 12 */ + byte pt[16] __attribute__((aligned(32))); + byte ct[16] __attribute__((aligned(32))); + byte tag[16] __attribute__((aligned(32))); + int ret; + + memset(seed, 0xA5, sizeof(seed)); memset(iv, 0x11, sizeof(iv)); + memset(pt, 0x33, sizeof(pt)); + + dbg_printf("\r\n== AES-GCM non-12-byte IV must hard-fail ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, seed, sizeof(seed)); + CHECK("AesGcmSetKey(seed,32)", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + CHECK("16-byte IV rejected (no silent SW fallback)", ret != 0); + wc_AesFree(&aes); +} + +/* HMAC-SHA256 under the HUK-derived key: same seed -> same MAC (deterministic), + * a different seed -> different MAC. The 32-byte seed is the HMAC key; the port + * runs the MAC over the HUK-derived secure-key slot (key never in software). */ +static void huk_hmac_test(void) +{ + Hmac hmac; + byte seed[32] __attribute__((aligned(32))); + byte msg[40] __attribute__((aligned(32))); + byte mac1[WC_SHA256_DIGEST_SIZE]; + byte mac2[WC_SHA256_DIGEST_SIZE]; + byte mac3[WC_SHA256_DIGEST_SIZE]; + int ret; + + memset(seed, 0x5A, sizeof(seed)); + memset(msg, 0x33, sizeof(msg)); + + dbg_printf("\r\n== HMAC-SHA256 under HUK-derived key ==\r\n"); + + ret = wc_HmacInit(&hmac, NULL, WC_HUK_DEVID); + CHECK("HmacInit(devId=WC_HUK_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_HmacSetKey(&hmac, WC_SHA256, seed, sizeof(seed)); + CHECK("HmacSetKey(seed,32)", ret == 0); + if (ret == 0) { + ret = wc_HmacUpdate(&hmac, msg, sizeof(msg)); + } + if (ret == 0) { + ret = wc_HmacFinal(&hmac, mac1); + } + CHECK("HmacFinal", ret == 0); + wc_HmacFree(&hmac); + if (ret != 0) { + return; + } + + /* Same seed + message -> identical MAC. */ + ret = wc_HmacInit(&hmac, NULL, WC_HUK_DEVID); + if (ret == 0) { + ret = wc_HmacSetKey(&hmac, WC_SHA256, seed, sizeof(seed)); + } + if (ret == 0) { + ret = wc_HmacUpdate(&hmac, msg, sizeof(msg)); + } + if (ret == 0) { + ret = wc_HmacFinal(&hmac, mac2); + } + wc_HmacFree(&hmac); + CHECK("deterministic MAC", ret == 0 && + memcmp(mac1, mac2, sizeof(mac1)) == 0); + + /* Different seed -> different MAC. */ + seed[0] ^= 0xFF; + ret = wc_HmacInit(&hmac, NULL, WC_HUK_DEVID); + if (ret == 0) { + ret = wc_HmacSetKey(&hmac, WC_SHA256, seed, sizeof(seed)); + } + if (ret == 0) { + ret = wc_HmacUpdate(&hmac, msg, sizeof(msg)); + } + if (ret == 0) { + ret = wc_HmacFinal(&hmac, mac3); + } + wc_HmacFree(&hmac); + CHECK("wrong seed -> different MAC", ret == 0 && + memcmp(mac1, mac3, sizeof(mac1)) != 0); +} + +/* HUK-bound ECDSA P-256 sign. A key is generated in software, its private + * scalar wrapped under the HUK (ECB-encrypted with the HUK device using the + * seed), then signing goes through the HUK device (which unwraps under the HUK + * and signs). The signature is checked against the software public key. To + * exercise the HW ECDSA engine instead of the software-after-unwrap path, set + * hk.useHwEngine = 1 (requires on-silicon validation). */ +static void huk_ecdsa_test(void) +{ + Aes aes; + ecc_key key; + ecc_key huk; + WC_RNG rng; + wc_Rtl8735b_EccKey hk; + byte seed[32] __attribute__((aligned(32))); + byte d[32] __attribute__((aligned(32))); + byte wrapped[32] __attribute__((aligned(32))); + byte hash[32] __attribute__((aligned(32))); + byte sig[80]; + word32 dLen = sizeof(d); + word32 sigLen = sizeof(sig); + int ret; + int verified = 0; + + memset(seed, 0x6B, sizeof(seed)); + memset(hash, 0x42, sizeof(hash)); + memset(d, 0, sizeof(d)); + + dbg_printf("\r\n== ECDSA P-256 under HUK-derived key ==\r\n"); + + ret = wc_InitRng(&rng); + CHECK("InitRng", ret == 0); + if (ret != 0) { + return; + } + ret = wc_ecc_init(&key); + if (ret == 0) { + ret = wc_ecc_make_key(&rng, 32, &key); + } + CHECK("ecc_make_key (P-256)", ret == 0); + if (ret != 0) { + wc_FreeRng(&rng); + return; + } + ret = wc_ecc_export_private_only(&key, d, &dLen); + CHECK("export private scalar (32B)", ret == 0 && dLen == 32); + if (ret != 0 || dLen != 32) { + wc_ecc_free(&key); + wc_FreeRng(&rng); + return; + } + + /* Wrap the scalar under the HUK: ECB-encrypt with the HUK device + seed. */ + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + if (ret == 0) { + ret = wc_AesSetKey(&aes, seed, sizeof(seed), NULL, AES_ENCRYPTION); + } + if (ret == 0) { + ret = wc_AesEcbEncrypt(&aes, wrapped, d, sizeof(d)); + } + wc_AesFree(&aes); + CHECK("wrap scalar under HUK (ECB)", ret == 0); + if (ret != 0) { + wc_ecc_free(&key); + wc_FreeRng(&rng); + return; + } + + /* Sign via the HUK-bound key (unwraps under the HUK, then signs). */ + memset(&hk, 0, sizeof(hk)); + hk.seed = seed; hk.seedSz = sizeof(seed); + hk.wrapped = wrapped; hk.wrappedLen = sizeof(wrapped); + hk.plainLen = 32; + /* Default: software sign after the HUK unwrap (validated on silicon). + * Setting hk.useHwEngine = 1 routes through the HW ECDSA engine (hal_ecdsa), + * but that engine is access-gated by SEC_ECDSA_DIS: from a non-secure (NTZ) + * app its registers read back 0, so the HW path needs secure-world access. + * See wolfcrypt/src/port/realtek/README.md. */ + /* hk.useHwEngine = 1; */ + + ret = wc_ecc_init_ex(&huk, NULL, WC_HUK_DEVID); + if (ret == 0) { + ret = wc_ecc_set_curve(&huk, 32, ECC_SECP256R1); + } + if (ret == 0) { + huk.devCtx = &hk; + ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigLen, &rng, &huk); + } + CHECK("HUK ECDSA sign", ret == 0); + + if (ret == 0) { + ret = wc_ecc_verify_hash(sig, sigLen, hash, sizeof(hash), &verified, + &key); + } + CHECK("verify with public key", ret == 0 && verified == 1); + + wc_ecc_free(&huk); + wc_ecc_free(&key); + wc_FreeRng(&rng); +} + +static void wolf_huk_thread(void* param) +{ + int ret; + (void)param; + + dbg_printf("\r\n=== wolfCrypt AmebaPro2 (RTL8735B) HUK example ===\r\n"); + + device_mutex_lock(RT_DEV_LOCK_CRYPTO); + + ret = wolfCrypt_Init(); + CHECK("wolfCrypt_Init", ret == 0); + + if (ret == 0) { + ret = wc_Rtl8735b_HukRegister(WC_HUK_DEVID); + CHECK("wc_Rtl8735b_HukRegister", ret == 0); + } + if (ret == 0) { + huk_gcm_test(); + huk_ecb_cbc_test(); + huk_ctr_test(); + huk_gcm_unaligned_test(); + huk_cbc_inplace_test(); + huk_gcm_badiv_test(); + huk_hmac_test(); + huk_ecdsa_test(); + wc_Rtl8735b_HukUnRegister(WC_HUK_DEVID); + } + wolfCrypt_Cleanup(); + + device_mutex_unlock(RT_DEV_LOCK_CRYPTO); + dbg_printf("\r\n=== done ===\r\n"); + vTaskDelete(NULL); +} + +int main(void) +{ + if (xTaskCreate(wolf_huk_thread, "wolf_huk", STACKSIZE, NULL, + tskIDLE_PRIORITY + 1, NULL) != pdPASS) { + dbg_printf("xTaskCreate failed\r\n"); + } + else { + vTaskStartScheduler(); + } + while (1) { + } +} diff --git a/rtl8735b/user_settings.h b/rtl8735b/user_settings.h new file mode 100644 index 00000000..2944e71f --- /dev/null +++ b/rtl8735b/user_settings.h @@ -0,0 +1,79 @@ +/* user_settings.h -- wolfCrypt config for the AmebaPro2 (RTL8735B) HUK example + * built inside the RealTek FreeRTOS SDK. HUK crypto-callback demo: AES + * (GCM/ECB/CBC/CTR), HMAC-SHA256, and ECDSA P-256. */ + +#ifndef RTL8735B_HUK_USER_SETTINGS_H +#define RTL8735B_HUK_USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- HUK crypto-callback device (our RealTek port) ---- */ +#define WOLFSSL_RTL8735B_HUK +#define WOLF_CRYPTO_CB + +/* ---- platform / RTOS ---- */ +#define WOLFSSL_GENERAL_ALIGNMENT 4 +#define SIZEOF_LONG_LONG 8 +#define SINGLE_THREADED /* HW crypto serialized by the SDK device_lock */ +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_DRIVER +#define WOLFCRYPT_ONLY /* no TLS layer */ +#define WOLFSSL_SMALL_STACK +#define WOLFSSL_IGNORE_FILE_WARN +#define NO_ERROR_STRINGS + +/* ---- AES modes exercised by the HUK device ---- */ +#define HAVE_AESGCM +#define WOLFSSL_AES_DIRECT +#define HAVE_AES_ECB +#define HAVE_AES_CBC +#define WOLFSSL_AES_COUNTER +#define WOLFSSL_AES_256 +#define WOLFSSL_AES_128 +#define GCM_TABLE_4BIT + +/* ---- hashing + DRBG (seeded from the SDK TRNG, see main.c) ---- */ +#undef NO_SHA256 +#define WOLFSSL_SHA256 +#define HAVE_HASHDRBG +/* HMAC-SHA256 is enabled by default (no NO_HMAC) for the HUK HMAC demo. */ + +/* ---- ECC / ECDSA P-256 (HUK-bound sign demo) ---- */ +#define HAVE_ECC +#define HAVE_ECC_SIGN +#define HAVE_ECC_VERIFY +#define ECC_USER_CURVES /* limit curve tables to those enabled below */ +#define HAVE_ECC256 /* P-256 only */ +#define ECC_TIMING_RESISTANT +#define WOLFSSL_SP_MATH_ALL /* generic SP big-integer math backend (ECC) */ + +/* ---- trims ---- */ +#define NO_RSA +#define NO_DSA +#define NO_DH +#define NO_DES3 +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_SHA +#define NO_PWDBASED +#define NO_PKCS12 +#define NO_PKCS8 +#define NO_CERTS +#define NO_OLD_TLS + +/* ---- custom RNG seed hook (provided in main.c via the SDK TRNG) ---- */ +#define CUSTOM_RAND_GENERATE_SEED rtl8735b_rand_seed +#ifndef __ASSEMBLER__ + #include + int rtl8735b_rand_seed(unsigned char* output, unsigned int sz); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* RTL8735B_HUK_USER_SETTINGS_H */ diff --git a/rtl8735b/wolfcrypt_huk.cmake b/rtl8735b/wolfcrypt_huk.cmake new file mode 100644 index 00000000..02f47032 --- /dev/null +++ b/rtl8735b/wolfcrypt_huk.cmake @@ -0,0 +1,57 @@ +# wolfCrypt AmebaPro2 (RTL8735B) HUK example -- RealTek FreeRTOS SDK wiring. +# +# Install this directory at /component/example/wolfcrypt_huk/ and select it +# at configure time: +# cmake .. -DEXAMPLE=wolfcrypt_huk -DWOLFSSL_ROOT=/path/to/wolfssl ... +# (also copy main.c to /project/realtek_amebapro2_v0_example/src/main.c) +# +# Adds the wolfCrypt sources + the RealTek HUK crypto-callback port + this +# example's include path and -DWOLFSSL_USER_SETTINGS to the SDK app build. + +# Locate the wolfSSL tree: -DWOLFSSL_ROOT=..., or $WOLFSSL_ROOT, else a guess. +if(NOT DEFINED WOLFSSL_ROOT OR WOLFSSL_ROOT STREQUAL "") + if(DEFINED ENV{WOLFSSL_ROOT}) + set(WOLFSSL_ROOT $ENV{WOLFSSL_ROOT}) + else() + set(WOLFSSL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../wolfssl) + endif() +endif() +if(NOT EXISTS "${WOLFSSL_ROOT}/wolfcrypt/src/aes.c") + message(FATAL_ERROR + "WOLFSSL_ROOT='${WOLFSSL_ROOT}' is not a wolfSSL tree. " + "Pass -DWOLFSSL_ROOT=/path/to/wolfssl.") +endif() +message(STATUS "wolfCrypt HUK example: WOLFSSL_ROOT=${WOLFSSL_ROOT}") + +### header search paths ### +list(APPEND app_example_inc_path + ${WOLFSSL_ROOT} + ${CMAKE_CURRENT_LIST_DIR} # user_settings.h +) + +### compile definitions (become -D...) ### +list(APPEND app_example_flags + WOLFSSL_USER_SETTINGS +) + +### source files ### +list(APPEND app_example_sources + ${WOLFSSL_ROOT}/wolfcrypt/src/aes.c + ${WOLFSSL_ROOT}/wolfcrypt/src/sha256.c + ${WOLFSSL_ROOT}/wolfcrypt/src/hash.c + ${WOLFSSL_ROOT}/wolfcrypt/src/hmac.c + ${WOLFSSL_ROOT}/wolfcrypt/src/random.c + ${WOLFSSL_ROOT}/wolfcrypt/src/memory.c + ${WOLFSSL_ROOT}/wolfcrypt/src/wc_port.c + ${WOLFSSL_ROOT}/wolfcrypt/src/cryptocb.c + ${WOLFSSL_ROOT}/wolfcrypt/src/error.c + ${WOLFSSL_ROOT}/wolfcrypt/src/logging.c + ${WOLFSSL_ROOT}/wolfcrypt/src/wc_encrypt.c + ${WOLFSSL_ROOT}/wolfcrypt/src/port/realtek/rtl8735b.c + # ECC / ECDSA P-256 (HUK sign demo) + its ASN.1 + SP big-integer math + ${WOLFSSL_ROOT}/wolfcrypt/src/ecc.c + ${WOLFSSL_ROOT}/wolfcrypt/src/asn.c + ${WOLFSSL_ROOT}/wolfcrypt/src/coding.c + ${WOLFSSL_ROOT}/wolfcrypt/src/sp_int.c + ${WOLFSSL_ROOT}/wolfcrypt/src/wolfmath.c +)