From 27f686821b7b31a5e9a1d4e70c1e560c9372e506 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 28 Jul 2026 15:44:14 -0700 Subject: [PATCH 1/2] rtl8735b: add plaintext-key AES-GCM device test alongside HUK, selected by devId --- rtl8735b/README.md | 1 + rtl8735b/main.c | 123 +++++++++++++++++++++++++++++++++++++++ rtl8735b/user_settings.h | 1 + 3 files changed, 125 insertions(+) diff --git a/rtl8735b/README.md b/rtl8735b/README.md index 69999e0bc..f699d286d 100644 --- a/rtl8735b/README.md +++ b/rtl8735b/README.md @@ -13,6 +13,7 @@ It is built inside the RealTek AmebaPro2 FreeRTOS SDK (which provides the startu - 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. - HW ECDSA P-256 verify offload: the same signature is then verified through the HW engine (the public point is imported into a `WC_HUK_DEVID` key so `wc_ecc_verify_hash` dispatches to `hal_ecdsa`), and a tampered digest is shown to be rejected. No HUK context is needed to verify -- any P-256 public key. +- **Plaintext-key AES (second device):** with `WOLFSSL_RTL8735B_AES` also enabled (see `user_settings.h`), the example registers a second crypto-callback device, `wc_Rtl8735b_AesRegister(WC_RTL8735B_AES_DEVID)`, that runs a caller-supplied AES key directly on the HW engine with no HUK binding. It reproduces a published AES-GCM test vector (proving the key is used verbatim), then encrypts the same 32 key bytes through both devices to show the plaintext-key ciphertext differs from the HUK device-bound ciphertext -- the two devices coexist and are selected per `Aes` by `devId`. ## Prerequisites diff --git a/rtl8735b/main.c b/rtl8735b/main.c index b9eddf141..f22f6c9b3 100644 --- a/rtl8735b/main.c +++ b/rtl8735b/main.c @@ -126,6 +126,119 @@ static void huk_gcm_test(void) ForceZero(seed, sizeof(seed)); /* scrub key material before return */ } +#ifdef WOLFSSL_RTL8735B_AES +/* AES-GCM with a plaintext key through the second device (WC_RTL8735B_AES_DEVID). + * Unlike the HUK device, the key bytes are the actual AES key, so the output + * matches a published GCM vector. Then the same 32 key bytes are run through + * both devices to show they coexist and are selected per Aes by devId: the + * plaintext-key ciphertext differs from the HUK device-bound ciphertext. */ +static void plain_gcm_test(void) +{ + Aes aes; + /* McGrew & Viega GCM test case 3: 128-bit key, 64-byte payload, no AAD. */ + static const byte katKey[16] = { + 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, + 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08 + }; + static const byte katIv[12] = { + 0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad,0xde,0xca,0xf8,0x88 + }; + static const byte katPt[64] = { + 0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5, + 0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda, + 0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53, + 0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57, + 0xba,0x63,0x7b,0x39,0x1a,0xaf,0xd2,0x55 + }; + static const byte katCt[64] = { + 0x42,0x83,0x1e,0xc2,0x21,0x77,0x74,0x24, + 0x4b,0x72,0x21,0xb7,0x84,0xd0,0xd4,0x9c, + 0xe3,0xaa,0x21,0x2f,0x2c,0x02,0xa4,0xe0, + 0x35,0xc1,0x7e,0x23,0x29,0xac,0xa1,0x2e, + 0x21,0xd5,0x14,0xb2,0x54,0x66,0x93,0x1c, + 0x7d,0x8f,0x6a,0x5a,0xac,0x84,0xaa,0x05, + 0x1b,0xa3,0x0b,0x39,0x6a,0x0a,0xac,0x97, + 0x3d,0x58,0xe0,0x91,0x47,0x3f,0x59,0x85 + }; + static const byte katTag[16] = { + 0x4d,0x5c,0x2a,0xf3,0x27,0xcd,0x64,0xa6, + 0x2c,0xf3,0x5a,0xbd,0x2b,0xa6,0xfa,0xb4 + }; + /* HAL crypto engine requires 32-byte-aligned key/iv/buffers. */ + byte key[32] __attribute__((aligned(32))); + byte iv[12] __attribute__((aligned(32))); + byte pt[64] __attribute__((aligned(32))); + byte ct[64] __attribute__((aligned(32))); + byte ctHuk[64] __attribute__((aligned(32))); + byte dec[64] __attribute__((aligned(32))); + byte tag[16] __attribute__((aligned(32))); + byte tagHuk[16] __attribute__((aligned(32))); + int ret; + + dbg_printf("\r\n== AES-GCM with a plaintext key " + "(devId=WC_RTL8735B_AES_DEVID) ==\r\n"); + + /* Known-answer test: the plaintext key must reproduce the published vector. */ + memcpy(iv, katIv, sizeof(iv)); + memcpy(pt, katPt, sizeof(pt)); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + CHECK("AesInit(devId=WC_RTL8735B_AES_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, katKey, sizeof(katKey)); + CHECK("AesGcmSetKey(plaintext 128-bit key)", ret == 0); + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + CHECK("AesGcmEncrypt", ret == 0); + } + CHECK("ciphertext matches published vector", + ret == 0 && memcmp(ct, katCt, sizeof(katCt)) == 0); + CHECK("tag matches published vector", + ret == 0 && memcmp(tag, katTag, sizeof(katTag)) == 0); + if (ret == 0) { + ret = wc_AesGcmDecrypt(&aes, dec, ct, sizeof(ct), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + CHECK("AesGcmDecrypt verifies", ret == 0); + CHECK("plaintext round-trips", memcmp(dec, pt, sizeof(pt)) == 0); + } + wc_AesFree(&aes); + + /* Coexistence: same 32 key bytes -> plaintext device vs HUK device produce + * different ciphertext (one uses the key verbatim, the other as a seed). */ + memset(key, 0x5A, sizeof(key)); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); + } + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + } + wc_AesFree(&aes); + CHECK("plaintext-key encrypt (256-bit)", ret == 0); + if (ret == 0) { + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); + } + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, ctHuk, pt, sizeof(pt), iv, sizeof(iv), + tagHuk, sizeof(tagHuk), NULL, 0); + } + wc_AesFree(&aes); + CHECK("HUK-seed encrypt (same 32 bytes)", ret == 0); + CHECK("plaintext CT != HUK CT (distinct keys per devId)", + ret == 0 && memcmp(ct, ctHuk, sizeof(ct)) != 0); + } + ForceZero(key, sizeof(key)); /* scrub key material before return */ +} +#endif /* WOLFSSL_RTL8735B_AES */ + static void huk_ecb_cbc_test(void) { Aes aes; @@ -580,6 +693,12 @@ static void wolf_huk_thread(void* param) ret = wc_Rtl8735b_HukRegister(WC_HUK_DEVID); CHECK("wc_Rtl8735b_HukRegister", ret == 0); } +#ifdef WOLFSSL_RTL8735B_AES + if (ret == 0) { + ret = wc_Rtl8735b_AesRegister(WC_RTL8735B_AES_DEVID); + CHECK("wc_Rtl8735b_AesRegister", ret == 0); + } +#endif if (ret == 0) { huk_gcm_test(); huk_ecb_cbc_test(); @@ -589,6 +708,10 @@ static void wolf_huk_thread(void* param) huk_gcm_badiv_test(); huk_hmac_test(); huk_ecdsa_test(); +#ifdef WOLFSSL_RTL8735B_AES + plain_gcm_test(); + wc_Rtl8735b_AesUnRegister(WC_RTL8735B_AES_DEVID); +#endif wc_Rtl8735b_HukUnRegister(WC_HUK_DEVID); } wolfCrypt_Cleanup(); diff --git a/rtl8735b/user_settings.h b/rtl8735b/user_settings.h index 57d0d7a5a..4e6873589 100644 --- a/rtl8735b/user_settings.h +++ b/rtl8735b/user_settings.h @@ -11,6 +11,7 @@ extern "C" { /* ---- HUK crypto-callback device (our RealTek port) ---- */ #define WOLFSSL_RTL8735B_HUK +#define WOLFSSL_RTL8735B_AES /* plaintext-key AES device (2nd devId) */ #define WOLF_CRYPTO_CB #define WOLF_CRYPTO_CB_COPY /* HUK HMAC buffer copy/free ops */ #define WOLF_CRYPTO_CB_FREE From 31bc616dc8bd0a9a4cf58e89658cdd005fccc143 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 30 Jul 2026 10:51:01 -0700 Subject: [PATCH 2/2] Peer review fixes --- rtl8735b/README.md | 22 ++- rtl8735b/main.c | 389 +++++++++++++++++++++++++++------------ rtl8735b/user_settings.h | 4 +- 3 files changed, 295 insertions(+), 120 deletions(-) diff --git a/rtl8735b/README.md b/rtl8735b/README.md index f699d286d..6a04053d8 100644 --- a/rtl8735b/README.md +++ b/rtl8735b/README.md @@ -19,11 +19,11 @@ It is built inside the RealTek AmebaPro2 FreeRTOS SDK (which provides the startu - 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/rtl8735b.c` and `wolfssl/wolfcrypt/port/realtek/rtl8735b.h`). Enable with `WOLFSSL_RTL8735B_HUK` and `WOLF_CRYPTO_CB`. +- wolfSSL with the RealTek HUK port (`wolfcrypt/src/port/realtek/rtl8735b.c` and `wolfssl/wolfcrypt/port/realtek/rtl8735b.h`). Enable with `WOLFSSL_RTL8735B_HUK` and `WOLF_CRYPTO_CB`. This example's `user_settings.h` also defines `WOLFSSL_RTL8735B_AES` (the plaintext-key AES device), so the wolfSSL tree must be new enough to provide `wc_Rtl8735b_AesRegister` / `WC_RTL8735B_AES_DEVID`; remove that define to build against an older HUK-only port. ## 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, non-12-byte-IV rejection, HMAC-SHA256, and HUK-bound ECDSA sign + HW verify checks). +- `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, non-12-byte-IV rejection, HMAC-SHA256, and HUK-bound ECDSA sign + HW verify checks). With `WOLFSSL_RTL8735B_AES` it also registers the plaintext-key AES device and runs its GCM KAT + coexistence, ECB FIPS-197 KAT, unaligned-buffer, and non-12-byte-IV 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. - `test/` -- a separate `wolfcrypt_test` + `wolfcrypt_benchmark` example for this target with three backend modes (pure C, Thumb-2 / SP Cortex-M, and RealTek hardware offload); see `test/README.md`. @@ -57,6 +57,7 @@ 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 +[PASS] wc_Rtl8735b_AesRegister == AES-GCM (full payload) under HUK-derived key == [PASS] AesInit(devId=WC_HUK_DEVID) [PASS] AesGcmSetKey(seed,32) @@ -77,6 +78,23 @@ Remove the J27 jumper and reset to boot. The console (UART, 115200) prints: [PASS] verify with software public key [PASS] HW ECDSA verify (good sig) [PASS] HW ECDSA verify (tampered -> reject) +== AES-GCM with a plaintext key (devId=WC_RTL8735B_AES_DEVID) == +[PASS] AesInit(devId=WC_RTL8735B_AES_DEVID) +[PASS] AesGcmSetKey(plaintext 128-bit key) +[PASS] AesGcmEncrypt +[PASS] ciphertext matches published vector +[PASS] tag matches published vector +[PASS] AesGcmDecrypt verifies +[PASS] plaintext round-trips +[PASS] tampered tag -> AES_GCM_AUTH_E +[PASS] plaintext-key encrypt (256-bit) +[PASS] HUK-seed encrypt (same 32 bytes) +[PASS] plaintext CT != HUK CT (distinct keys per devId) +== AES-ECB with a plaintext key (FIPS-197 KAT) == +[PASS] ciphertext matches FIPS-197 vector +[PASS] AesEcb round-trip +== AES-GCM plaintext key, UNALIGNED buffers (port bounces) == ... round-trip PASS +== AES-GCM plaintext key, non-12-byte IV must hard-fail == ... 16-byte IV rejected PASS === done === ``` diff --git a/rtl8735b/main.c b/rtl8735b/main.c index f22f6c9b3..395cdd19e 100644 --- a/rtl8735b/main.c +++ b/rtl8735b/main.c @@ -7,6 +7,12 @@ * (port bounce path), in-place multi-call CBC, and a non-12-byte GCM IV that * must hard-fail (no silent software fallback). * + * With WOLFSSL_RTL8735B_AES also enabled it registers a second crypto-callback + * device (WC_RTL8735B_AES_DEVID) that runs a caller-supplied plaintext AES key + * directly on the HW engine (no HUK binding), selected per Aes by devId, and + * exercises it with AES-GCM (published KAT + coexistence), an AES-ECB FIPS-197 + * KAT, the unaligned-buffer bounce path, and non-12-byte-IV rejection. + * * Build: configure with -DEXAMPLE=wolfcrypt_huk (see wolfcrypt_huk.cmake). */ @@ -126,119 +132,6 @@ static void huk_gcm_test(void) ForceZero(seed, sizeof(seed)); /* scrub key material before return */ } -#ifdef WOLFSSL_RTL8735B_AES -/* AES-GCM with a plaintext key through the second device (WC_RTL8735B_AES_DEVID). - * Unlike the HUK device, the key bytes are the actual AES key, so the output - * matches a published GCM vector. Then the same 32 key bytes are run through - * both devices to show they coexist and are selected per Aes by devId: the - * plaintext-key ciphertext differs from the HUK device-bound ciphertext. */ -static void plain_gcm_test(void) -{ - Aes aes; - /* McGrew & Viega GCM test case 3: 128-bit key, 64-byte payload, no AAD. */ - static const byte katKey[16] = { - 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, - 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08 - }; - static const byte katIv[12] = { - 0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad,0xde,0xca,0xf8,0x88 - }; - static const byte katPt[64] = { - 0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5, - 0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a, - 0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda, - 0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72, - 0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53, - 0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25, - 0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57, - 0xba,0x63,0x7b,0x39,0x1a,0xaf,0xd2,0x55 - }; - static const byte katCt[64] = { - 0x42,0x83,0x1e,0xc2,0x21,0x77,0x74,0x24, - 0x4b,0x72,0x21,0xb7,0x84,0xd0,0xd4,0x9c, - 0xe3,0xaa,0x21,0x2f,0x2c,0x02,0xa4,0xe0, - 0x35,0xc1,0x7e,0x23,0x29,0xac,0xa1,0x2e, - 0x21,0xd5,0x14,0xb2,0x54,0x66,0x93,0x1c, - 0x7d,0x8f,0x6a,0x5a,0xac,0x84,0xaa,0x05, - 0x1b,0xa3,0x0b,0x39,0x6a,0x0a,0xac,0x97, - 0x3d,0x58,0xe0,0x91,0x47,0x3f,0x59,0x85 - }; - static const byte katTag[16] = { - 0x4d,0x5c,0x2a,0xf3,0x27,0xcd,0x64,0xa6, - 0x2c,0xf3,0x5a,0xbd,0x2b,0xa6,0xfa,0xb4 - }; - /* HAL crypto engine requires 32-byte-aligned key/iv/buffers. */ - byte key[32] __attribute__((aligned(32))); - byte iv[12] __attribute__((aligned(32))); - byte pt[64] __attribute__((aligned(32))); - byte ct[64] __attribute__((aligned(32))); - byte ctHuk[64] __attribute__((aligned(32))); - byte dec[64] __attribute__((aligned(32))); - byte tag[16] __attribute__((aligned(32))); - byte tagHuk[16] __attribute__((aligned(32))); - int ret; - - dbg_printf("\r\n== AES-GCM with a plaintext key " - "(devId=WC_RTL8735B_AES_DEVID) ==\r\n"); - - /* Known-answer test: the plaintext key must reproduce the published vector. */ - memcpy(iv, katIv, sizeof(iv)); - memcpy(pt, katPt, sizeof(pt)); - ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); - CHECK("AesInit(devId=WC_RTL8735B_AES_DEVID)", ret == 0); - if (ret != 0) { - return; - } - ret = wc_AesGcmSetKey(&aes, katKey, sizeof(katKey)); - CHECK("AesGcmSetKey(plaintext 128-bit key)", ret == 0); - if (ret == 0) { - ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), - tag, sizeof(tag), NULL, 0); - CHECK("AesGcmEncrypt", ret == 0); - } - CHECK("ciphertext matches published vector", - ret == 0 && memcmp(ct, katCt, sizeof(katCt)) == 0); - CHECK("tag matches published vector", - ret == 0 && memcmp(tag, katTag, sizeof(katTag)) == 0); - if (ret == 0) { - ret = wc_AesGcmDecrypt(&aes, dec, ct, sizeof(ct), iv, sizeof(iv), - tag, sizeof(tag), NULL, 0); - CHECK("AesGcmDecrypt verifies", ret == 0); - CHECK("plaintext round-trips", memcmp(dec, pt, sizeof(pt)) == 0); - } - wc_AesFree(&aes); - - /* Coexistence: same 32 key bytes -> plaintext device vs HUK device produce - * different ciphertext (one uses the key verbatim, the other as a seed). */ - memset(key, 0x5A, sizeof(key)); - ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); - if (ret == 0) { - ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); - } - if (ret == 0) { - ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), - tag, sizeof(tag), NULL, 0); - } - wc_AesFree(&aes); - CHECK("plaintext-key encrypt (256-bit)", ret == 0); - if (ret == 0) { - ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); - if (ret == 0) { - ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); - } - if (ret == 0) { - ret = wc_AesGcmEncrypt(&aes, ctHuk, pt, sizeof(pt), iv, sizeof(iv), - tagHuk, sizeof(tagHuk), NULL, 0); - } - wc_AesFree(&aes); - CHECK("HUK-seed encrypt (same 32 bytes)", ret == 0); - CHECK("plaintext CT != HUK CT (distinct keys per devId)", - ret == 0 && memcmp(ct, ctHuk, sizeof(ct)) != 0); - } - ForceZero(key, sizeof(key)); /* scrub key material before return */ -} -#endif /* WOLFSSL_RTL8735B_AES */ - static void huk_ecb_cbc_test(void) { Aes aes; @@ -677,9 +570,263 @@ static void huk_ecdsa_test(void) ForceZero(seed, sizeof(seed)); } +#ifdef WOLFSSL_RTL8735B_AES +/* AES-GCM with a plaintext key through the second device (WC_RTL8735B_AES_DEVID). + * Unlike the HUK device, the key bytes are the actual AES key, so the output + * matches a published GCM vector. Then the same 32 key bytes are run through + * both devices to show they coexist and are selected per Aes by devId: the + * plaintext-key ciphertext differs from the HUK device-bound ciphertext. */ +static void plain_gcm_test(void) +{ + Aes aes; + /* McGrew & Viega GCM test case 3: 128-bit key, 64-byte payload, no AAD. */ + static const byte katKey[16] = { + 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, + 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08 + }; + static const byte katIv[12] = { + 0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad,0xde,0xca,0xf8,0x88 + }; + static const byte katPt[64] = { + 0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5, + 0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda, + 0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53, + 0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57, + 0xba,0x63,0x7b,0x39,0x1a,0xaf,0xd2,0x55 + }; + static const byte katCt[64] = { + 0x42,0x83,0x1e,0xc2,0x21,0x77,0x74,0x24, + 0x4b,0x72,0x21,0xb7,0x84,0xd0,0xd4,0x9c, + 0xe3,0xaa,0x21,0x2f,0x2c,0x02,0xa4,0xe0, + 0x35,0xc1,0x7e,0x23,0x29,0xac,0xa1,0x2e, + 0x21,0xd5,0x14,0xb2,0x54,0x66,0x93,0x1c, + 0x7d,0x8f,0x6a,0x5a,0xac,0x84,0xaa,0x05, + 0x1b,0xa3,0x0b,0x39,0x6a,0x0a,0xac,0x97, + 0x3d,0x58,0xe0,0x91,0x47,0x3f,0x59,0x85 + }; + static const byte katTag[16] = { + 0x4d,0x5c,0x2a,0xf3,0x27,0xcd,0x64,0xa6, + 0x2c,0xf3,0x5a,0xbd,0x2b,0xa6,0xfa,0xb4 + }; + /* HAL crypto engine requires 32-byte-aligned key/iv/buffers. */ + byte key[32] __attribute__((aligned(32))); + byte iv[12] __attribute__((aligned(32))); + byte pt[64] __attribute__((aligned(32))); + byte ct[64] __attribute__((aligned(32))); + byte ctHuk[64] __attribute__((aligned(32))); + byte dec[64] __attribute__((aligned(32))); + byte tag[16] __attribute__((aligned(32))); + byte tagHuk[16] __attribute__((aligned(32))); + int ret; + + dbg_printf("\r\n== AES-GCM with a plaintext key " + "(devId=WC_RTL8735B_AES_DEVID) ==\r\n"); + + /* Known-answer test: the plaintext key must reproduce the published vector. + * Stage the vector key into the 32-byte-aligned buffer the HAL DMA wants. */ + memcpy(key, katKey, sizeof(katKey)); + memcpy(iv, katIv, sizeof(iv)); + memcpy(pt, katPt, sizeof(pt)); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + CHECK("AesInit(devId=WC_RTL8735B_AES_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, key, sizeof(katKey)); + CHECK("AesGcmSetKey(plaintext 128-bit key)", ret == 0); + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + CHECK("AesGcmEncrypt", ret == 0); + } + CHECK("ciphertext matches published vector", + ret == 0 && memcmp(ct, katCt, sizeof(katCt)) == 0); + CHECK("tag matches published vector", + ret == 0 && memcmp(tag, katTag, sizeof(katTag)) == 0); + if (ret == 0) { + ret = wc_AesGcmDecrypt(&aes, dec, ct, sizeof(ct), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + CHECK("AesGcmDecrypt verifies", ret == 0); + CHECK("plaintext round-trips", memcmp(dec, pt, sizeof(pt)) == 0); + + /* Negative: a tampered tag must be rejected by the HW decrypt-verify. */ + tag[0] ^= 0xFFu; + ret = wc_AesGcmDecrypt(&aes, dec, ct, sizeof(ct), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + CHECK("tampered tag -> AES_GCM_AUTH_E", ret == AES_GCM_AUTH_E); + } + wc_AesFree(&aes); + + /* Coexistence: same 32 key bytes -> plaintext device vs HUK device produce + * different ciphertext (one uses the key verbatim, the other as a seed). */ + memset(key, 0x5A, sizeof(key)); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + CHECK("AesInit(plaintext devId)", ret == 0); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); + CHECK("AesGcmSetKey(plaintext 256-bit key)", ret == 0); + } + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, ct, pt, sizeof(pt), iv, sizeof(iv), + tag, sizeof(tag), NULL, 0); + } + wc_AesFree(&aes); + CHECK("plaintext-key encrypt (256-bit)", ret == 0); + if (ret == 0) { + ret = wc_AesInit(&aes, NULL, WC_HUK_DEVID); + CHECK("AesInit(HUK devId)", ret == 0); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); + CHECK("AesGcmSetKey(HUK seed, same 32 bytes)", ret == 0); + } + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, ctHuk, pt, sizeof(pt), iv, sizeof(iv), + tagHuk, sizeof(tagHuk), NULL, 0); + } + wc_AesFree(&aes); + CHECK("HUK-seed encrypt (same 32 bytes)", ret == 0); + CHECK("plaintext CT != HUK CT (distinct keys per devId)", + ret == 0 && memcmp(ct, ctHuk, sizeof(ct)) != 0); + } + ForceZero(key, sizeof(key)); /* scrub key material before return */ +} + +/* AES-ECB with a plaintext key: FIPS-197 appendix C.1 known-answer vector. The + * plaintext device runs the key verbatim, so the HW output must match the + * published ciphertext exactly (the HUK device cannot -- its key is derived). */ +static void plain_ecb_test(void) +{ + Aes aes; + static const byte katKey[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + }; + static const byte katPt[16] = { + 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff + }; + static const byte katCt[16] = { + 0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30, + 0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a + }; + byte key[16] __attribute__((aligned(32))); + byte pt[16] __attribute__((aligned(32))); + byte ct[16] __attribute__((aligned(32))); + byte dec[16] __attribute__((aligned(32))); + int ret; + + memcpy(key, katKey, sizeof(key)); + memcpy(pt, katPt, sizeof(pt)); + + dbg_printf("\r\n== AES-ECB with a plaintext key (FIPS-197 KAT) ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + CHECK("AesInit(devId=WC_RTL8735B_AES_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesSetKey(&aes, key, sizeof(key), NULL, AES_ENCRYPTION); + CHECK("AesSetKey(ECB enc)", ret == 0); + if (ret == 0) { + ret = wc_AesEcbEncrypt(&aes, ct, pt, sizeof(pt)); + CHECK("AesEcbEncrypt", ret == 0); + } + CHECK("ciphertext matches FIPS-197 vector", + ret == 0 && memcmp(ct, katCt, sizeof(katCt)) == 0); + ret = wc_AesSetKey(&aes, key, sizeof(key), NULL, AES_DECRYPTION); + CHECK("AesSetKey(ECB dec)", ret == 0); + if (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); + ForceZero(key, sizeof(key)); /* scrub key material before return */ +} + +/* AES-GCM with a plaintext key over deliberately-unaligned caller buffers, so + * the port's bounce-to-aligned path is exercised for the second device too. */ +static void plain_gcm_unaligned_test(void) +{ + Aes aes; + /* 32-byte-aligned backing; the +1 views below are deliberately unaligned. */ + byte buf[6][48] __attribute__((aligned(32))); + byte* key = buf[0] + 1; + byte* iv = buf[1] + 1; + byte* pt = buf[2] + 1; + byte* ct = buf[3] + 1; + byte* dec = buf[4] + 1; + byte* tag = buf[5] + 1; + int ret; + + memset(key, 0x5A, 16); memset(iv, 0x11, 12); + memset(pt, 0x33, 32); + + dbg_printf("\r\n== AES-GCM plaintext key, UNALIGNED buffers " + "(port bounces) ==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + CHECK("AesInit(devId=WC_RTL8735B_AES_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, key, 16); + CHECK("AesGcmSetKey (unaligned key)", ret == 0); + if (ret != 0) { + wc_AesFree(&aes); + return; + } + ret = wc_AesGcmEncrypt(&aes, ct, pt, 32, iv, 12, tag, 16, NULL, 0); + CHECK("AesGcmEncrypt (unaligned)", ret == 0); + ret = wc_AesGcmDecrypt(&aes, dec, ct, 32, iv, 12, tag, 16, NULL, 0); + CHECK("AesGcmDecrypt (unaligned) verifies", ret == 0); + CHECK("unaligned round-trip", memcmp(dec, pt, 32) == 0); + wc_AesFree(&aes); + ForceZero(key, 16); /* scrub key material before return */ +} + +/* A non-12-byte GCM IV must hard-fail on the plaintext device too, NOT silently + * fall back to software GCM (regression for the IV hard-error contract). */ +static void plain_gcm_badiv_test(void) +{ + Aes aes; + byte key[16] __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(key, 0x5A, sizeof(key)); memset(iv, 0x11, sizeof(iv)); + memset(pt, 0x33, sizeof(pt)); + + dbg_printf("\r\n== AES-GCM plaintext key, non-12-byte IV must hard-fail " + "==\r\n"); + ret = wc_AesInit(&aes, NULL, WC_RTL8735B_AES_DEVID); + CHECK("AesInit(devId=WC_RTL8735B_AES_DEVID)", ret == 0); + if (ret != 0) { + return; + } + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); + CHECK("AesGcmSetKey(key,16)", 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); + ForceZero(key, sizeof(key)); /* scrub key material before return */ +} +#endif /* WOLFSSL_RTL8735B_AES */ + static void wolf_huk_thread(void* param) { int ret; +#ifdef WOLFSSL_RTL8735B_AES + int aesRet = 0; +#endif (void)param; dbg_printf("\r\n=== wolfCrypt AmebaPro2 (RTL8735B) HUK example ===\r\n"); @@ -694,9 +841,12 @@ static void wolf_huk_thread(void* param) CHECK("wc_Rtl8735b_HukRegister", ret == 0); } #ifdef WOLFSSL_RTL8735B_AES + /* The plaintext-key AES device is an optional add-on; track its registration + * in its own result so a failure only skips its demo -- it must never + * suppress the HUK suite or leak the HUK device registration. */ if (ret == 0) { - ret = wc_Rtl8735b_AesRegister(WC_RTL8735B_AES_DEVID); - CHECK("wc_Rtl8735b_AesRegister", ret == 0); + aesRet = wc_Rtl8735b_AesRegister(WC_RTL8735B_AES_DEVID); + CHECK("wc_Rtl8735b_AesRegister", aesRet == 0); } #endif if (ret == 0) { @@ -709,8 +859,13 @@ static void wolf_huk_thread(void* param) huk_hmac_test(); huk_ecdsa_test(); #ifdef WOLFSSL_RTL8735B_AES - plain_gcm_test(); - wc_Rtl8735b_AesUnRegister(WC_RTL8735B_AES_DEVID); + if (aesRet == 0) { + plain_gcm_test(); + plain_ecb_test(); + plain_gcm_unaligned_test(); + plain_gcm_badiv_test(); + wc_Rtl8735b_AesUnRegister(WC_RTL8735B_AES_DEVID); + } #endif wc_Rtl8735b_HukUnRegister(WC_HUK_DEVID); } diff --git a/rtl8735b/user_settings.h b/rtl8735b/user_settings.h index 4e6873589..54359efb4 100644 --- a/rtl8735b/user_settings.h +++ b/rtl8735b/user_settings.h @@ -1,6 +1,8 @@ /* 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. */ + * (GCM/ECB/CBC/CTR), HMAC-SHA256, and ECDSA P-256. Also enables the plaintext- + * key AES device (WOLFSSL_RTL8735B_AES) -- a second crypto-callback device that + * runs a caller-supplied AES key directly on the HW engine (no HUK binding). */ #ifndef RTL8735B_HUK_USER_SETTINGS_H #define RTL8735B_HUK_USER_SETTINGS_H