Merge pull request #612 from dgarske/aes_plain

rtl8735b: add plaintext-key AES-GCM device test alongside HUK, selected by devId
pull/616/head
philljj 2026-08-14 14:16:30 -05:00 committed by GitHub
commit cb4a3cb976
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 303 additions and 3 deletions

View File

@ -13,16 +13,17 @@ 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
- 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`.
@ -56,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)
@ -76,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 ===
```

View File

@ -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).
*/
@ -564,9 +570,263 @@ cleanup:
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");
@ -580,6 +840,15 @@ static void wolf_huk_thread(void* param)
ret = wc_Rtl8735b_HukRegister(WC_HUK_DEVID);
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) {
aesRet = wc_Rtl8735b_AesRegister(WC_RTL8735B_AES_DEVID);
CHECK("wc_Rtl8735b_AesRegister", aesRet == 0);
}
#endif
if (ret == 0) {
huk_gcm_test();
huk_ecb_cbc_test();
@ -589,6 +858,15 @@ static void wolf_huk_thread(void* param)
huk_gcm_badiv_test();
huk_hmac_test();
huk_ecdsa_test();
#ifdef WOLFSSL_RTL8735B_AES
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);
}
wolfCrypt_Cleanup();

View File

@ -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
@ -11,6 +13,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