From c42ad138766bd0bf744af05ef510831471a33643 Mon Sep 17 00:00:00 2001 From: David Garske Date: Sat, 5 Sep 2026 09:37:02 -0700 Subject: [PATCH 1/2] Use Hmac_UpdateFinal for TLS CBC MAC verify when a crypto callback device owns the HMAC key since the raw-hash constant-time path reads software hash state such a device does not maintain --- .github/workflows/cryptocb-only.yml | 5 +- src/tls.c | 23 +- tests/api.c | 414 ++++++++++++++++++++++++++++ wolfcrypt/src/hmac.c | 19 +- 4 files changed, 455 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index 12a23a84494..80417ada607 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -141,7 +141,10 @@ jobs: "configure": ["--enable-slhdsa=yes,sha2", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_SLHDSA"]}, {"name": "only", "minutes": 4.0, "comment": "Same coverage as the \"all\" entry above, but driven by ./configure --enable-cryptocb=only instead of a hand-written CPPFLAGS list. This is the regression test for the configure option: it must emit exactly the WOLF_CRYPTO_CB_ONLY_* set that \"all\" passes by hand, for the algorithms this base enables. The \"all\" entry deliberately stays on explicit CPPFLAGS so a bug in the configure logic cannot silently weaken both. Note the base already passes --enable-cryptocb; this entry's flags are appended after the base, so --enable-cryptocb=only wins.", - "configure": ["--enable-cryptocb=only"]} + "configure": ["--enable-cryptocb=only"]}, + {"name": "setkey", "minutes": 4.0, + "comment": "--enable-cryptocbutils turns on all cryptocb utilities (COPY/FREE/SETKEY/EXPORT_KEY/KEYSTORE); this entry needs WOLF_CRYPTO_CB_SETKEY. Exercises test_wc_CryptoCb_TLS_CBC_HMAC: a TLS 1.2 MAC-then-Encrypt CBC handshake whose record HMAC is computed by a callback that owns the key (services the SETKEY op) and leaves the software hash state empty, so TLS_hmac must route it through Hmac_UpdateFinal. No ONLY_* strip here so the device's own software HMAC keeps its providers; the base already enables swdev. No other cryptocb config enables SETKEY, so the test is skipped everywhere else.", + "configure": ["--enable-cryptocbutils"]} ]} EOF .github/scripts/parallel-make-check.py \ diff --git a/src/tls.c b/src/tls.c index 9a6c3ccaf98..ae3e770a113 100644 --- a/src/tls.c +++ b/src/tls.c @@ -1196,8 +1196,15 @@ static int Hmac_UpdateFinal_CT(Hmac* hmac, byte* digest, const byte* in, #endif +/* A crypto-callback device that services the HMAC SETKEY operation owns the + * key: wc_HmacSetKey() returns before it derives the software ipad/opad, so it + * leaves hmac->keyRaw NULL and the raw-hash constant-time path no software + * state to read. keyRaw NULL is the exact signal for that case -- it also + * covers a WOLF_CRYPTO_CB_FIND device found by algorithm with no devId -- and + * only WOLF_CRYPTO_CB_SETKEY builds can reach it. */ #if defined(WOLFSSL_NO_HASH_RAW) || defined(HAVE_FIPS) || \ - defined(HAVE_SELFTEST) || defined(HAVE_BLAKE2B) + defined(HAVE_SELFTEST) || defined(HAVE_BLAKE2B) || \ + (defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)) /* Calculate the HMAC of the header + message data. * Constant time implementation using normal hashing operations. @@ -1467,6 +1474,20 @@ int TLS_hmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz, int padSz, if (verify && padSz >= 0) { #if !defined(WOLFSSL_NO_HASH_RAW) && !defined(HAVE_FIPS) && \ !defined(HAVE_SELFTEST) + #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY) + /* keyRaw NULL means a crypto-callback device owns the key and the + * software hash state the raw-hash path reads is uncomputed. Use + * the update/final variant: the padding-time equalization (dummy + * blocks) is preserved and the timing profile is the device's, + * outside the Lucky13 software threat model. When software holds + * the key (keyRaw set, incl. PK-only offload or a device that + * declined the HMAC) the raw-hash path below is kept. */ + if (hmac->keyRaw == NULL) { + ret = Hmac_UpdateFinal(hmac, digest, in, + totalSz, myInner, innerSz); + } + else + #endif #ifdef HAVE_BLAKE2B if (wolfSSL_GetHmacType(ssl) == WC_HASH_TYPE_BLAKE2B) { ret = Hmac_UpdateFinal(hmac, digest, in, diff --git a/tests/api.c b/tests/api.c index 10345478656..172cf46b2b4 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34583,6 +34583,282 @@ static int test_CryptoCb_Func(int thisDevId, wc_CryptoInfo* info, void* ctx) return ret; } +/* Device id used for the test devices' internal reference HMACs. It is never + * registered, so those HMACs resolve to software. INVALID_DEVID cannot be used + * here: under WOLF_CRYPTO_CB_FIND the find callback maps INVALID_DEVID back + * onto the test device and the reference HMAC recurses into it forever. */ +#define TEST_CRYPTOCB_UNREG_DEVID 0x6e6f6465 /* 'n' 'o' 'd' 'e' */ + +#if !defined(NO_HMAC) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && \ + defined(HAVE_ECC) && defined(HAVE_AES_CBC) && !defined(NO_SHA256) && \ + defined(HAVE_ENCRYPT_THEN_MAC) && defined(WOLF_CRYPTO_CB) && \ + defined(WOLF_CRYPTO_CB_SETKEY) && defined(HAVE_IO_TESTS_DEPENDENCIES) +#define TEST_CRYPTOCB_HMAC_DEV + +/* State the HMAC-computing test device hangs off an Hmac's devCtx: the key it + * owns (so the caller's software ipad/opad are never computed) plus the + * accumulated message. The key outlives a final, the way a real engine keeps a + * loaded key: the TLS 1.2 PRF (wc_PRF -> p_hash) sets the key once and then + * runs many update/final cycles on the same Hmac, and dropping the key at the + * first final would silently push the rest of them to a software path whose + * ipad/opad were never derived. Accumulators are chained off the device + * context and released when the test tears the device down. */ +typedef struct HmacDevAccum { + struct HmacDevAccum* next; + byte key[WC_MAX_BLOCK_SIZE]; + word32 keyLen; + int macType; + byte* buf; + word32 len; + word32 cap; +} HmacDevAccum; + +/* Per-device context: the private key file the delegated PK operations use, + * plus every accumulator this device handed out. One device serves one side of + * the connection, so this is only ever touched from that side's thread. */ +typedef struct HmacDevCtx { + const char* privKeyFile; + HmacDevAccum* list; +} HmacDevCtx; + +static void test_CryptoCb_HmacDev_Cleanup(HmacDevCtx* devCtx) +{ + HmacDevAccum* a = devCtx->list; + + while (a != NULL) { + HmacDevAccum* next = a->next; + XFREE(a->buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(a, NULL, DYNAMIC_TYPE_TMP_BUFFER); + a = next; + } + devCtx->list = NULL; +} + +/* Crypto callback modelling a hardware HMAC engine that owns the key: it + * services the HMAC SETKEY, so wc_HmacSetKey returns before it computes the + * software ipad/opad, and it buffers the message and computes the MAC at final + * with a fresh software HMAC. The caller's own software hash state is thus + * never populated -- exactly how a real offload engine behaves. That is what + * makes the raw-hash constant-time TLS CBC verify path (Hmac_UpdateFinal_CT, + * which reads that software state) produce a wrong MAC unless a cryptocb devId + * routes verification through the update/final path instead. Requires + * WOLF_CRYPTO_CB_SETKEY so the engine can claim the key; everything other than + * the HMAC key/update/final is delegated to the shared test_CryptoCb_Func so + * the TLS handshake's PK operations still work. */ +static int test_CryptoCb_HmacDev_Func(int thisDevId, wc_CryptoInfo* info, + void* ctx) +{ + HmacDevCtx* devCtx = (HmacDevCtx*)ctx; + + if (info != NULL && info->algo_type == WC_ALGO_TYPE_SETKEY && + info->setkey.type == WC_SETKEY_HMAC) { + Hmac* hmac = (Hmac*)info->setkey.obj; + HmacDevAccum* a; + + if (hmac == NULL || devCtx == NULL || + info->setkey.keySz > (word32)sizeof(a->key)) { + /* cannot hold this key: let software handle it */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + a = (HmacDevAccum*)XMALLOC(sizeof(*a), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (a == NULL) { + return WC_NO_ERR_TRACE(MEMORY_E); + } + XMEMSET(a, 0, sizeof(*a)); + if (info->setkey.key != NULL && info->setkey.keySz > 0) { + XMEMCPY(a->key, info->setkey.key, info->setkey.keySz); + } + a->keyLen = info->setkey.keySz; + a->macType = hmac->macType; + a->next = devCtx->list; + devCtx->list = a; + hmac->devCtx = a; + return 0; /* handled: software ipad/opad are not computed */ + } + + if (info != NULL && info->algo_type == WC_ALGO_TYPE_HMAC) { + Hmac* hmac = info->hmac.hmac; + HmacDevAccum* a; + int ret = 0; + + if (hmac == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + a = (HmacDevAccum*)hmac->devCtx; + if (a == NULL) { + /* not a key this device owns: let software handle it */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + /* update: buffer the data, leave the software hash state untouched */ + if (info->hmac.in != NULL && info->hmac.inSz > 0) { + word32 need = a->len + info->hmac.inSz; + if (need > a->cap) { + word32 cap = (a->cap == 0) ? 256 : a->cap; + byte* nb; + while (cap < need) { + cap *= 2; + } + nb = (byte*)XMALLOC(cap, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (nb == NULL) { + return WC_NO_ERR_TRACE(MEMORY_E); + } + if (a->len > 0) { + XMEMCPY(nb, a->buf, a->len); + } + XFREE(a->buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + a->buf = nb; + a->cap = cap; + } + XMEMCPY(a->buf + a->len, info->hmac.in, info->hmac.inSz); + a->len = need; + } + + /* final: compute the MAC from the buffered message with a separate + * software HMAC keyed from the key this device owns */ + if (info->hmac.digest != NULL) { + Hmac tmp; + + ret = wc_HmacInit(&tmp, NULL, TEST_CRYPTOCB_UNREG_DEVID); + if (ret == 0) { + ret = wc_HmacSetKey(&tmp, a->macType, a->key, a->keyLen); + if (ret == 0 && a->len > 0) { + ret = wc_HmacUpdate(&tmp, a->buf, a->len); + } + if (ret == 0) { + ret = wc_HmacFinal(&tmp, info->hmac.digest); + } + wc_HmacFree(&tmp); + } + /* key stays loaded for the next update/final cycle on this Hmac */ + a->len = 0; + } + return ret; + } + return test_CryptoCb_Func(thisDevId, info, + (devCtx != NULL) ? (void*)devCtx->privKeyFile : NULL); +} + +/* Cleared by test_CryptoCb_cbcMtE_ctx_ready() if forcing the CBC MtE suite + * fails, so the test rejects a run that silently negotiated another suite. */ +static int test_CryptoCb_cbcMtE_ready_ok = 1; + +/* Force a TLS 1.2 MAC-then-Encrypt CBC-SHA256 suite so the record MAC runs + * through the Lucky13 constant-time verify path exercised by the fix. */ +static void test_CryptoCb_cbcMtE_ctx_ready(WOLFSSL_CTX* ctx) +{ + if (wolfSSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES128-SHA256") + != WOLFSSL_SUCCESS || + wolfSSL_CTX_AllowEncryptThenMac(ctx, 0) != WOLFSSL_SUCCESS) { + test_CryptoCb_cbcMtE_ready_ok = 0; + } +} +#endif /* HMAC && !NO_TLS12 && !NO_RSA && HAVE_ECC && HAVE_AES_CBC && + * !NO_SHA256 && HAVE_ENCRYPT_THEN_MAC */ + +#if !defined(NO_HMAC) && !defined(NO_SHA256) && defined(WOLF_CRYPTO_CB) && \ + defined(WOLF_CRYPTO_CB_SETKEY) && defined(WOLF_CRYPTO_CB_FIND) +#define TEST_CRYPTOCB_HMAC_FIND +/* Registered, but never named by the caller: the find callback is what steers + * INVALID_DEVID operations onto it. */ +#define TEST_CRYPTOCB_HMAC_FIND_DEVID 7 + +/* Key-owning HMAC engine for the find-mapping regression. Same idea as + * HmacDevAccum above, with fixed-size state so no allocation is needed. */ +typedef struct HmacFindDev { + byte key[WC_MAX_BLOCK_SIZE]; + word32 keyLen; + int macType; + byte buf[128]; + word32 len; + int setKeyCount; + int hmacCount; +} HmacFindDev; + +/* Services the HMAC SETKEY (claiming the key, so the caller's software + * ipad/opad are never derived) and the HMAC update/final operations. */ +static int test_CryptoCb_HmacFind_Func(int thisDevId, wc_CryptoInfo* info, + void* ctx) +{ + HmacFindDev* dev = (HmacFindDev*)ctx; + + (void)thisDevId; + + if (info == NULL || dev == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + if (info->algo_type == WC_ALGO_TYPE_SETKEY && + info->setkey.type == WC_SETKEY_HMAC) { + Hmac* hmac = (Hmac*)info->setkey.obj; + + if (hmac == NULL || info->setkey.keySz > (word32)sizeof(dev->key)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (info->setkey.key != NULL && info->setkey.keySz > 0) { + XMEMCPY(dev->key, info->setkey.key, info->setkey.keySz); + } + dev->keyLen = info->setkey.keySz; + dev->macType = hmac->macType; + dev->len = 0; + dev->setKeyCount++; + hmac->devCtx = dev; + return 0; /* handled: software ipad/opad are not computed */ + } + + if (info->algo_type == WC_ALGO_TYPE_HMAC) { + Hmac* hmac = info->hmac.hmac; + int ret = 0; + + if (hmac == NULL || hmac->devCtx != (void*)dev) { + /* not a key this device owns (the reference HMAC below) */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + dev->hmacCount++; + + if (info->hmac.in != NULL && info->hmac.inSz > 0) { + if (info->hmac.inSz > (word32)sizeof(dev->buf) - dev->len) { + return WC_NO_ERR_TRACE(BUFFER_E); + } + XMEMCPY(dev->buf + dev->len, info->hmac.in, info->hmac.inSz); + dev->len += info->hmac.inSz; + } + + if (info->hmac.digest != NULL) { + Hmac tmp; + + ret = wc_HmacInit(&tmp, NULL, TEST_CRYPTOCB_UNREG_DEVID); + if (ret == 0) { + ret = wc_HmacSetKey(&tmp, dev->macType, dev->key, dev->keyLen); + if (ret == 0 && dev->len > 0) { + ret = wc_HmacUpdate(&tmp, dev->buf, dev->len); + } + if (ret == 0) { + ret = wc_HmacFinal(&tmp, info->hmac.digest); + } + wc_HmacFree(&tmp); + } + /* key stays loaded, as on the TLS device above */ + dev->len = 0; + } + return ret; + } + + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} + +/* Map operations that carry no device id onto the HMAC engine, the way + * wc_swdev's find callback does. */ +static int test_CryptoCb_HmacFind_FindCb(int currentId, int algoType) +{ + (void)algoType; + if (currentId == INVALID_DEVID) { + return TEST_CRYPTOCB_HMAC_FIND_DEVID; + } + return currentId; +} +#endif /* TEST_CRYPTOCB_HMAC_FIND */ + /* These callback helpers are only referenced by test_wc_CryptoCb_registry, * whose body is compiled only under WOLF_CRYPTO_CB + WOLFSSL_TEST_STATIC_BUILD * (it calls WOLFSSL_LOCAL cryptocb helpers). Match that guard so they are not @@ -35136,6 +35412,141 @@ static int test_wc_CryptoCb(void) return EXPECT_RESULT(); } +/* Regression test: a TLS 1.2 MAC-then-Encrypt CBC handshake where the record + * and PRF HMACs are computed by a crypto callback that never populates the + * software hash state. Without routing device-backed verification through the + * update/final HMAC path, TLS_hmac() takes the raw-hash constant-time path, + * reads the empty software state, produces a wrong record MAC, and the + * handshake fails with a decrypt error. */ +static int test_wc_CryptoCb_TLS_CBC_HMAC(void) +{ + EXPECT_DECLS; +#ifdef TEST_CRYPTOCB_HMAC_DEV + callback_functions client_cbf; + callback_functions server_cbf; + HmacDevCtx client_dev; + HmacDevCtx server_dev; + + XMEMSET(&client_cbf, 0, sizeof(client_cbf)); + XMEMSET(&server_cbf, 0, sizeof(server_cbf)); + XMEMSET(&client_dev, 0, sizeof(client_dev)); + XMEMSET(&server_dev, 0, sizeof(server_dev)); + client_dev.privKeyFile = cliKeyFile; + server_dev.privKeyFile = svrKeyFile; + + client_cbf.method = wolfTLSv1_2_client_method; + server_cbf.method = wolfTLSv1_2_server_method; + + /* RSA credentials; the public key file is loaded and the private key is + * served through the crypto callback (matching test_wc_CryptoCb_TLS). */ + client_cbf.caPemFile = svrCertFile; + client_cbf.certPemFile = cliCertFile; + client_cbf.keyPemFile = cliKeyPubFile; + server_cbf.caPemFile = cliCertFile; + server_cbf.certPemFile = svrCertFile; + server_cbf.keyPemFile = svrKeyPubFile; + + client_cbf.ctx_ready = test_CryptoCb_cbcMtE_ctx_ready; + server_cbf.ctx_ready = test_CryptoCb_cbcMtE_ctx_ready; + test_CryptoCb_cbcMtE_ready_ok = 1; + + client_cbf.devId = 1; + ExpectIntEQ(wc_CryptoCb_RegisterDevice(client_cbf.devId, + test_CryptoCb_HmacDev_Func, &client_dev), 0); + server_cbf.devId = 2; + ExpectIntEQ(wc_CryptoCb_RegisterDevice(server_cbf.devId, + test_CryptoCb_HmacDev_Func, &server_dev), 0); + + test_wolfSSL_client_server(&client_cbf, &server_cbf); + /* both ctx forced the CBC MtE suite; otherwise a GCM/EtM run could pass + * without exercising the raw-hash verify path */ + ExpectIntEQ(test_CryptoCb_cbcMtE_ready_ok, 1); + ExpectIntEQ(server_cbf.return_code, TEST_SUCCESS); + ExpectIntEQ(client_cbf.return_code, TEST_SUCCESS); + + wc_CryptoCb_UnRegisterDevice(client_cbf.devId); + wc_CryptoCb_UnRegisterDevice(server_cbf.devId); + + test_CryptoCb_HmacDev_Cleanup(&client_dev); + test_CryptoCb_HmacDev_Cleanup(&server_dev); +#else + return TEST_SKIPPED; +#endif /* TEST_CRYPTOCB_HMAC_DEV */ + return EXPECT_RESULT(); +} + +/* Regression test: with WOLF_CRYPTO_CB_FIND a find callback can steer an + * INVALID_DEVID operation onto a registered device, and wc_HmacSetKey() honors + * that for the SETKEY operation. wc_HmacUpdate()/wc_HmacFinal() must dispatch + * on the same terms: when they instead kept the plain devId != INVALID_DEVID + * test, the device claimed the key while the message was hashed in software + * against ipad/opad that were never derived, producing a wrong MAC. */ +static int test_wc_CryptoCb_Hmac_Find(void) +{ + EXPECT_DECLS; +#ifdef TEST_CRYPTOCB_HMAC_FIND + HmacFindDev dev; + Hmac hmac; + int hmacInit = 0; + byte expected[WC_SHA256_DIGEST_SIZE]; + byte mac[WC_SHA256_DIGEST_SIZE]; + const byte key[] = "cryptocb find regression key"; + const byte msg[] = "cryptocb find regression message"; + word32 keySz = (word32)XSTRLEN((const char*)key); + word32 msgSz = (word32)XSTRLEN((const char*)msg); + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(expected, 0, sizeof(expected)); + XMEMSET(mac, 0, sizeof(mac)); + + /* software reference, computed with no device in reach */ + ExpectIntEQ(wc_HmacInit(&hmac, NULL, TEST_CRYPTOCB_UNREG_DEVID), 0); + if (EXPECT_SUCCESS()) { + hmacInit = 1; + } + ExpectIntEQ(wc_HmacSetKey(&hmac, WC_SHA256, key, keySz), 0); + ExpectIntEQ(wc_HmacUpdate(&hmac, msg, msgSz), 0); + ExpectIntEQ(wc_HmacFinal(&hmac, expected), 0); + if (hmacInit) { + wc_HmacFree(&hmac); + hmacInit = 0; + } + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_CRYPTOCB_HMAC_FIND_DEVID, + test_CryptoCb_HmacFind_Func, &dev), 0); + if (EXPECT_SUCCESS()) { + wc_CryptoCb_SetDeviceFindCb(test_CryptoCb_HmacFind_FindCb); + } + + /* no device id: only the find callback puts this on the device */ + ExpectIntEQ(wc_HmacInit(&hmac, NULL, INVALID_DEVID), 0); + if (EXPECT_SUCCESS()) { + hmacInit = 1; + } + ExpectIntEQ(wc_HmacSetKey(&hmac, WC_SHA256, key, keySz), 0); + /* the device claimed the key, so nothing else may compute this MAC */ + ExpectIntEQ(dev.setKeyCount, 1); + ExpectIntEQ(wc_HmacUpdate(&hmac, msg, msgSz), 0); + ExpectIntEQ(wc_HmacFinal(&hmac, mac), 0); + ExpectIntGE(dev.hmacCount, 2); + ExpectBufEQ(mac, expected, sizeof(expected)); + if (hmacInit) { + wc_HmacFree(&hmac); + } + + /* restore whatever find callback the harness installed */ +#ifdef WOLFSSL_SWDEV + wc_CryptoCb_SetDeviceFindCb(wc_SwDev_FindCb); +#else + wc_CryptoCb_SetDeviceFindCb(NULL); +#endif + wc_CryptoCb_UnRegisterDevice(TEST_CRYPTOCB_HMAC_FIND_DEVID); +#else + return TEST_SKIPPED; +#endif /* TEST_CRYPTOCB_HMAC_FIND */ + return EXPECT_RESULT(); +} + #if defined(WOLFSSL_STATIC_MEMORY) && defined(HAVE_IO_TESTS_DEPENDENCIES) /* tlsVer: Example: WOLFSSL_TLSV1_2 or WOLFSSL_TLSV1_3 */ @@ -42286,6 +42697,9 @@ TEST_CASE testCases[] = { /* Unconditional shell (body self-guards on WOLF_CRYPTO_CB && * WOLF_CRYPTO_CB_CMD and a big-enough callback table). */ TEST_DECL(test_wc_CryptoCb_nested_register), + /* Unconditional shells (bodies self-guard on their feature macros). */ + TEST_DECL(test_wc_CryptoCb_TLS_CBC_HMAC), + TEST_DECL(test_wc_CryptoCb_Hmac_Find), /* Can't memory test as client/server hangs. */ TEST_DECL(test_wolfSSL_CTX_StaticMemory), #if !defined(NO_FILESYSTEM) && \ diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index 26a2904ed44..5bd6b4f6e88 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -53,6 +53,15 @@ #ifdef WOLF_CRYPTO_CB #include + + /* Whether an HMAC op is a crypto-callback candidate. WOLF_CRYPTO_CB_FIND + * lets the find callback map any id (INVALID_DEVID included) onto a device, + * so every HMAC qualifies; otherwise only one carrying a device id does. */ + #ifdef WOLF_CRYPTO_CB_FIND + #define WC_HMAC_USE_CRYPTOCB(hmac) 1 + #else + #define WC_HMAC_USE_CRYPTOCB(hmac) ((hmac)->devId != INVALID_DEVID) + #endif #endif #ifdef NO_INLINE @@ -1160,7 +1169,7 @@ int wc_HmacUpdate(Hmac* hmac, const byte* msg, word32 length) } #ifdef WOLF_CRYPTO_CB - if (hmac->devId != INVALID_DEVID) { + if (WC_HMAC_USE_CRYPTOCB(hmac)) { ret = wc_CryptoCb_Hmac(hmac, hmac->macType, msg, length, NULL); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; @@ -1296,7 +1305,7 @@ int wc_HmacFinal(Hmac* hmac, byte* hash) } #ifdef WOLF_CRYPTO_CB - if (hmac->devId != INVALID_DEVID) { + if (WC_HMAC_USE_CRYPTOCB(hmac)) { ret = wc_CryptoCb_Hmac(hmac, hmac->macType, NULL, 0, hash); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; @@ -1741,8 +1750,10 @@ void wc_HmacFree(Hmac* hmac) #endif #ifdef WOLF_CRYPTO_CB - /* handle cleanup case where final is not called */ - if (hmac->devId != INVALID_DEVID && hmac->devCtx != NULL) { + /* handle cleanup case where final is not called. devCtx is only set by a + * device, so under WOLF_CRYPTO_CB_FIND (where INVALID_DEVID may be mapped + * to one) that alone identifies an op still owned by the device. */ + if (WC_HMAC_USE_CRYPTOCB(hmac) && hmac->devCtx != NULL) { int ret; byte finalHash[WC_HMAC_BLOCK_SIZE]; ret = wc_CryptoCb_Hmac(hmac, hmac->macType, NULL, 0, finalHash); From f38607497d0830c96138d44f1d39cddfc8255032 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 15 Sep 2026 16:56:15 -0700 Subject: [PATCH 2/2] Guard benchmark PQ option table against NO_MAIN_DRIVER builds where its use sites are compiled out --- wolfcrypt/benchmark/benchmark.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index a42768d53f9..61712ed45df 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -1421,7 +1421,8 @@ static const bench_pq_hash_sig_alg bench_pq_hash_sig_opt[] = { #endif /* !WOLFSSL_BENCHMARK_ALL && !NO_MAIN_DRIVER */ -#if !defined(WOLFSSL_BENCHMARK_ALL) && !defined(MAIN_NO_ARGS) +#if !defined(WOLFSSL_BENCHMARK_ALL) && !defined(MAIN_NO_ARGS) && \ + !defined(NO_MAIN_DRIVER) #if defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_HAVE_FRODOKEM) || \ defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) /* The post-quantum-specific mapping of command line option to bit values and