Skip to content

Setkey/Export callbacks#9851

Open
night1rider wants to merge 6 commits intowolfSSL:masterfrom
night1rider:setkey-callbacks
Open

Setkey/Export callbacks#9851
night1rider wants to merge 6 commits intowolfSSL:masterfrom
night1rider:setkey-callbacks

Conversation

@night1rider
Copy link
Copy Markdown
Contributor

@night1rider night1rider commented Mar 2, 2026

Add WOLF_CRYPTO_CB_SETKEY and WOLF_CRYPTO_CB_EXPORT_KEY generic crypto callback utilities.

Both callbacks use a temporary key struct pattern to bridge between raw key bytes and hardware. The temporary key is always initialized with INVALID_DEVID so it never recurses into the callback — it serves purely as a software intermediary.

SetKey (Import): When a key import function (e.g. wc_ecc_import_x963_ex2) is called on a hardware-bound key, a temporary key is created and the raw key material is imported into it via the normal software path. The callback handler then receives both the hardware destination key and the fully-parsed temporary key. The handler can call any wolfSSL export function on the temporary key (wc_RsaKeyToDer, wc_ecc_export_x963, raw component getters, etc.) to extract the key in whatever format the hardware needs, program it, and associate it with the destination key. The temporary key is freed after the callback returns.

SetKey callbacks are wired into: wc_AesSetKey, wc_HmacSetKey, wc_RsaPublicKeyDecodeRaw, wc_RsaPrivateKeyDecodeRaw, wc_RsaPrivateKeyDecode, wc_ecc_import_x963_ex2, wc_ecc_import_raw_private, and wc_ecc_import_private_key_ex. All RSA and ECC callsites pass the actual key size in bytes via the keySz parameter. ECC import paths propagate custom curve parameters to the temporary key so custom curves (e.g. brainpool) work correctly through the callback.

ExportKey: When an export function (e.g. wc_ecc_export_x963) is called on a hardware-backed key, an empty temporary key is created and passed to the callback handler alongside the hardware source key. The handler extracts key material from hardware in whatever format it can and imports it into the temporary key using any wolfSSL import function. Once the callback returns, the original export function runs on the now fully-populated temporary key via the normal software path, producing the output the caller originally requested. The temporary key is freed afterward.

ExportKey callbacks are wired into wc_RsaFlattenPublicKey, wc_RsaExportKey, wc_ecc_export_x963, and wc_ecc_export_ex (which covers wc_ecc_export_private_only, wc_ecc_export_public_raw, and wc_ecc_export_private_raw).

Add wc_CryptoCb_EccGetSize and wc_CryptoCb_EccGetSigSize callback hooksto wc_ecc_size() and wc_ecc_sig_size(), allowing hardware-only keys (where dp may be NULL) to report their key and signature sizes through the crypto callback rather than requiring curve info to be present in the key struct.

Both features are enabled via --enable-cryptocbutils=setkey,export, --enable-cryptocbutils, or the corresponding -DWOLF_CRYPTO_CB_SETKEY and -DWOLF_CRYPTO_CB_EXPORT_KEY defines, with CI test configurations in os-check.yml. Includes test handlers in test.c and api.c with small-stack-safe allocations (WC_DECLARE_VAR), a GetSetKeyTypeStr debug helper for readable type names under DEBUG_CRYPTOCB, and RSA free callback support.

@night1rider night1rider self-assigned this Mar 2, 2026
@night1rider night1rider force-pushed the setkey-callbacks branch 4 times, most recently from 1935260 to 3284bc5 Compare March 15, 2026 00:03
@night1rider
Copy link
Copy Markdown
Contributor Author

Jenkins Retest this please

@night1rider night1rider marked this pull request as ready for review March 15, 2026 21:17
@night1rider night1rider changed the title Setkey callbacks Setkey/Export callbacks Mar 15, 2026
@dgarske dgarske self-assigned this Mar 19, 2026
@night1rider
Copy link
Copy Markdown
Contributor Author

Rebased to fix merge conflict with master's rsa.c

@night1rider
Copy link
Copy Markdown
Contributor Author

Jenkins retest this please

Copy link
Copy Markdown

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #9851

Scan targets checked: wolfcrypt-bugs, wolfcrypt-src
Findings: 5

High (4)

Infinite recursion in wc_RsaPublicKeyDecodeRaw when WOLF_CRYPTO_CB_FIND is defined

File: wolfcrypt/src/asn.c:45163-45185
Function: wc_RsaPublicKeyDecodeRaw
Category: Logic errors

When WOLF_CRYPTO_CB_FIND is defined, the #ifndef WOLF_CRYPTO_CB_FIND / if (key->devId != INVALID_DEVID) guard is compiled out, making the SETKEY block execute unconditionally. The function allocates tmpKey with INVALID_DEVID and then calls itself recursively: tmpErr = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, tmpKey). On the recursive call, tmpKey->devId == INVALID_DEVID, but since the guard is absent, the block executes again, allocating another tmpKey and recursing infinitely. This causes a stack overflow crash. The recursive self-call occurs before wc_CryptoCb_SetKey is reached, so even though FindDevice(INVALID_DEVID) would return NULL, execution never gets there. Compare with wc_RsaPrivateKeyDecode in the same file which correctly avoids this by calling the internal _RsaPrivateKeyDecode helper instead of itself.

#ifndef WOLF_CRYPTO_CB_FIND
    if (key->devId != INVALID_DEVID)
#endif
    {
        ...
        tmpErr = wc_InitRsaKey_ex(tmpKey, key->heap, INVALID_DEVID);
        ...
        /* Recursive call imports n, e into temp via software */
        tmpErr = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, tmpKey);

Recommendation: Follow the pattern used in wc_RsaPrivateKeyDecode — call an internal helper function (e.g., a static _RsaPublicKeyDecodeRaw) for the software import instead of recursing into the public API. Alternatively, add an explicit tmpKey->devId == INVALID_DEVID check inside the block to skip the SETKEY path on recursive calls, independent of the WOLF_CRYPTO_CB_FIND conditional.


Infinite recursion in multiple ECC import/export functions when WOLF_CRYPTO_CB_FIND is defined

File: wolfcrypt/src/ecc.c:10733-10753
Function: wc_ecc_import_x963_ex2
Category: Logic errors

The same infinite recursion pattern affects five functions in ecc.c. When WOLF_CRYPTO_CB_FIND is defined, the devId != INVALID_DEVID guard is compiled out, and the functions recurse into themselves with a tmpKey that has INVALID_DEVID, causing infinite recursion and stack overflow. Affected functions: (1) wc_ecc_import_x963_ex2 calls wc_ecc_import_x963_ex2(in, inLen, tmpKey, curve_id, untrusted), (2) wc_ecc_import_private_key_ex calls wc_ecc_import_private_key_ex(priv, privSz, pub, pubSz, tmpKey, curve_id), (3) wc_ecc_import_raw_private calls wc_ecc_import_raw_private(tmpKey, qx, qy, d, curve_id, encType), (4) wc_ecc_export_x963 calls wc_ecc_export_x963(tmpKey, out, outLen), (5) wc_ecc_export_ex calls wc_ecc_export_ex(tmpKey, qx, qxLen, qy, qyLen, d, dLen, encType).

#ifndef WOLF_CRYPTO_CB_FIND
    if (key->devId != INVALID_DEVID)
#endif
    {
        ...
        err = wc_ecc_init_ex(tmpKey, key->heap, INVALID_DEVID);
        ...
        err = wc_ecc_import_x963_ex2(in, inLen, tmpKey, curve_id, untrusted);
        if (err == MP_OKAY) {
            cbRet = wc_CryptoCb_SetKey(key->devId, ...);
        }

Recommendation: For each affected function, either (a) create an internal static helper (prefixed with _) that performs the software-only operation and call that instead of the public function, or (b) add a runtime guard within the SETKEY/EXPORT_KEY block to check key->devId != INVALID_DEVID even when WOLF_CRYPTO_CB_FIND is defined, since keys with INVALID_DEVID should never be routed through the callback path.


Infinite recursion in RSA SETKEY/EXPORT_KEY functions when WOLF_CRYPTO_CB_FIND is defined

File: wolfcrypt/src/rsa.c:5490-5516
Function: wc_RsaPrivateKeyDecodeRaw
Category: Logic errors

Three functions in rsa.c have the same infinite recursion bug when WOLF_CRYPTO_CB_FIND is defined. The devId != INVALID_DEVID guard is compiled out, causing unconditional entry into the callback block where the function calls itself with a tmpKey initialized with INVALID_DEVID. Affected functions: (1) wc_RsaPrivateKeyDecodeRaw — SETKEY path calls wc_RsaPrivateKeyDecodeRaw(n, nSz, e, eSz, d, dSz, u, uSz, p, pSz, q, qSz, dP, dPSz, dQ, dQSz, tmpKey), (2) wc_RsaFlattenPublicKey — EXPORT_KEY path calls wc_RsaFlattenPublicKey(tmpKey, e, eSz, n, nSz), (3) wc_RsaExportKey — EXPORT_KEY path calls wc_RsaExportKey(tmpKey, e, eSz, n, nSz, d, dSz, p, pSz, q, qSz).

#ifndef WOLF_CRYPTO_CB_FIND
    if (err == MP_OKAY && key->devId != INVALID_DEVID)
#else
    if (err == MP_OKAY)
#endif
    {
        ...
        err = wc_InitRsaKey_ex(tmpKey, key->heap, INVALID_DEVID);
        ...
        err = wc_RsaPrivateKeyDecodeRaw(n, nSz, e, eSz, d, dSz,
            u, uSz, p, pSz, q, qSz, dP, dPSz, dQ, dQSz, tmpKey);

Recommendation: Use the same approach as wc_RsaPrivateKeyDecode in asn.c, which correctly calls the internal _RsaPrivateKeyDecode helper instead of the public function. Create internal static helpers for these functions, or add an unconditional devId != INVALID_DEVID check that cannot be compiled out by WOLF_CRYPTO_CB_FIND.


Infinite recursion / stack overflow when WOLF_CRYPTO_CB_FIND is defined

File: wolfcrypt/src/ecc.c:wc_ecc_import_x963_ex2, wolfcrypt/src/ecc.c:wc_ecc_import_private_key_ex, wolfcrypt/src/ecc.c:wc_ecc_import_raw_private, wolfcrypt/src/asn.c:wc_RsaPublicKeyDecodeRaw, wolfcrypt/src/rsa.c:wc_RsaPrivateKeyDecodeRaw
Function: wc_ecc_import_x963_ex2 (and 4 others)
Category: NULL pointer dereference

Five functions use a self-recursive pattern to import key material into a tmpKey with INVALID_DEVID, then pass it to wc_CryptoCb_SetKey. The guard that prevents entering the WOLF_CRYPTO_CB_SETKEY block is #ifndef WOLF_CRYPTO_CB_FIND / if (key->devId != INVALID_DEVID) / #endif. When WOLF_CRYPTO_CB_FIND is defined, the if guard is compiled out and the block executes unconditionally. Since tmpKey has INVALID_DEVID, the recursive call enters the same block again, allocates another tmpKey, recurses again, and so on — causing unbounded recursion and stack overflow. The recursive call happens before wc_CryptoCb_SetKey (which would call wc_CryptoCb_FindDevice and return CRYPTOCB_UNAVAILABLE for INVALID_DEVID), so there is no opportunity to break the cycle. Affected functions: wc_RsaPublicKeyDecodeRaw (asn.c), wc_ecc_import_x963_ex2 (ecc.c), wc_ecc_import_private_key_ex (ecc.c), wc_ecc_import_raw_private (ecc.c), wc_RsaPrivateKeyDecodeRaw (rsa.c). Note: wc_RsaPrivateKeyDecode in asn.c is NOT affected because it calls the internal _RsaPrivateKeyDecode instead of itself.

#ifndef WOLF_CRYPTO_CB_FIND
    if (key->devId != INVALID_DEVID)
#endif
    {
        WC_ALLOC_VAR(tmpKey, ecc_key, 1, key->heap);
        /* ... init tmpKey with INVALID_DEVID ... */
        err = wc_ecc_import_x963_ex2(in, inLen, tmpKey, curve_id, untrusted);
        /* ^^^ recursive call — with WOLF_CRYPTO_CB_FIND, enters this block again */
        if (err == MP_OKAY) {
            cbRet = wc_CryptoCb_SetKey(...);
        }
    }

Recommendation: Add an explicit devId != INVALID_DEVID check that is always compiled in, independent of WOLF_CRYPTO_CB_FIND. For example, change the guard to: #ifndef WOLF_CRYPTO_CB_FIND / if (key->devId != INVALID_DEVID) / #else / if (key->devId != INVALID_DEVID) / #endif — or simply always check key->devId != INVALID_DEVID before the recursive call regardless of WOLF_CRYPTO_CB_FIND. Alternatively, follow the pattern used in wc_RsaPrivateKeyDecode which calls the internal _RsaPrivateKeyDecode helper instead of recursing into itself.


Low (1)

Missing ForceZero on tmpKey holding private key material in import paths

File: wolfcrypt/src/rsa.c:wc_RsaPrivateKeyDecodeRaw, wolfcrypt/src/asn.c:wc_RsaPrivateKeyDecode
Function: wc_RsaPrivateKeyDecodeRaw, wc_RsaPrivateKeyDecode
Category: Missing ForceZero

In the new WOLF_CRYPTO_CB_SETKEY code paths, tmpKey (an RsaKey struct holding decoded private key material) is cleaned up with wc_FreeRsaKey(tmpKey); WC_FREE_VAR(tmpKey, key->heap); but without a preceding ForceZero(tmpKey, sizeof(RsaKey)). In contrast, the new export paths in the same PR (wc_RsaFlattenPublicKey and wc_RsaExportKey in rsa.c) correctly call ForceZero(tmpKey, sizeof(RsaKey)) before wc_FreeRsaKey. While wc_FreeRsaKey does call mp_forcezero on individual private mp_int components, the RsaKey struct itself (which may contain residual private key data in padding or non-mp_int fields) is not zeroed in the import paths before being freed back to heap. The same pattern applies to ecc_key tmpKeys in ecc.c import paths (wc_ecc_import_private_key_ex, wc_ecc_import_raw_private) which lack ForceZero before wc_ecc_free.

/* rsa.c wc_RsaPrivateKeyDecodeRaw - import path (missing ForceZero): */
        wc_FreeRsaKey(tmpKey);
        WC_FREE_VAR(tmpKey, key->heap);

/* rsa.c wc_RsaExportKey - export path (has ForceZero): */
        ForceZero(tmpKey, sizeof(RsaKey));
        wc_FreeRsaKey(tmpKey);
        WC_FREE_VAR(tmpKey, key->heap);

Recommendation: Add ForceZero(tmpKey, sizeof(RsaKey)) (or ForceZero(tmpKey, sizeof(ecc_key)) for ECC) before wc_FreeRsaKey/wc_ecc_free in the import paths, consistent with the export paths in the same PR.


This review was generated automatically by Fenrir. Findings are non-blocking.

@dgarske dgarske assigned night1rider and unassigned dgarske and night1rider Mar 23, 2026
dgarske
dgarske previously approved these changes Mar 23, 2026
Copy link
Copy Markdown
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want another set of eyes on this to merge. Preferably @douzzer or @bigbrett

bigbrett
bigbrett previously approved these changes Mar 25, 2026
Copy link
Copy Markdown
Contributor

@bigbrett bigbrett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work. Approved but have a few nits should you wish to address them.

As we discussed offline, this should eventually be unifed with, or supersede this, ideally leaving the existing AES-only API useable for backwards compat. No action on that front needed now.

@night1rider night1rider dismissed stale reviews from bigbrett and dgarske via 3007dfc March 25, 2026 17:25
@night1rider
Copy link
Copy Markdown
Contributor Author

rebased

bigbrett
bigbrett previously approved these changes Mar 25, 2026
@night1rider night1rider added the Not For This Release Not for release 5.9.1 label Mar 27, 2026
@night1rider
Copy link
Copy Markdown
Contributor Author

Jenkins retest this please

@night1rider
Copy link
Copy Markdown
Contributor Author

rebased and force pushed

@night1rider
Copy link
Copy Markdown
Contributor Author

Jenkins retest this please

@dgarske dgarske removed the Not For This Release Not for release 5.9.1 label Apr 8, 2026
int ret;
int keySz = 0;

ret = wc_CryptoCb_EccGetSize(key, &keySz);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH-1: wc_ecc_size() and wc_ecc_sig_size() callback guarded too broadly — affects all WOLF_CRYPTO_CB builds

  • File: wolfcrypt/src/ecc.c:12247-12273 and wolfcrypt/src/ecc.c:12295-12340
  • Function: wc_ecc_size, wc_ecc_sig_size
  • Category: [api]
  • Confidence: High

Description: The crypto callback lookup in wc_ecc_size() and wc_ecc_sig_size() is guarded only by #ifdef WOLF_CRYPTO_CB, not by the more specific WOLF_CRYPTO_CB_SETKEY or WOLF_CRYPTO_CB_EXPORT_KEY. This means ALL builds with crypto callbacks enabled (not just those opting into the new SETKEY/EXPORT features) will incur a wc_CryptoCb_FindDevice() lookup for every wc_ecc_size() and wc_ecc_sig_size() call when key->devId != INVALID_DEVID.

These are extremely hot functions called throughout ECC operations (key gen, sign, verify, import, export, certificate handling). The overhead adds up. More importantly, existing third-party callbacks that don't handle WC_PK_TYPE_EC_GET_SIZE / WC_PK_TYPE_EC_GET_SIG_SIZE may not return CRYPTOCB_UNAVAILABLE gracefully — they could return an unrecognized-type error, causing wc_ecc_size() to return 0 and breaking downstream operations silently.

Code:

// wc_ecc_size — guarded only by WOLF_CRYPTO_CB
#ifdef WOLF_CRYPTO_CB
    if (key->devId != INVALID_DEVID) {
        int ret;
        int keySz = 0;
        ret = wc_CryptoCb_EccGetSize(key, &keySz);
        if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
            if (ret != 0) {
                return 0;  // silently returns 0 on error
            }
            return keySz;
        }
    }
#endif

Recommendation: Either guard these with a more specific define like WOLF_CRYPTO_CB_SETKEY or WOLF_CRYPTO_CB_EXPORT_KEY, or add compile-time guard documentation that the new WC_PK_TYPE_EC_GET_SIZE/WC_PK_TYPE_EC_GET_SIG_SIZE callbacks must be handled (or return CRYPTOCB_UNAVAILABLE) by all crypto callback implementations when WOLF_CRYPTO_CB is enabled. The CRYPTO_CB_VER bump from 2→3 helps but doesn't prevent runtime breakage.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed both guards from #ifdef WOLF_CRYPTO_CB to
#if defined(WOLF_CRYPTO_CB) && (defined(WOLF_CRYPTO_CB_SETKEY) || defined(WOLF_CRYPTO_CB_EXPORT_KEY)).
These callbacks are only relevant when SETKEY or EXPORT_KEY features are enabled.

WC_PK_TYPE_RSA_PKCS = 25,
WC_PK_TYPE_RSA_PSS = 26,
WC_PK_TYPE_RSA_OAEP = 27,
WC_PK_TYPE_EC_GET_SIZE = 28,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM-2: WC_PK_TYPE_EC_GET_SIZE/WC_PK_TYPE_EC_GET_SIG_SIZE exceed WC_PK_TYPE_MAX

  • File: wolfssl/wolfcrypt/types.h:1571-1573
  • Function: N/A (enum definition)
  • Category: [api]
  • Confidence: High

Description: The new enum values WC_PK_TYPE_EC_GET_SIZE = 28 and WC_PK_TYPE_EC_GET_SIG_SIZE = 29 are placed after WC_PK_TYPE_RSA_OAEP = 27, but WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX resolves to at most 24 (with Dilithium/Falcon). This means these new values exceed WC_PK_TYPE_MAX, making the MAX misleading. While no code currently uses WC_PK_TYPE_MAX for array sizing (I verified), this continues and worsens a pattern that started with values 25-27.

Code:

    WC_PK_TYPE_RSA_OAEP = 27,
    WC_PK_TYPE_EC_GET_SIZE = 28,      // New: above _WC_PK_TYPE_MAX
    WC_PK_TYPE_EC_GET_SIG_SIZE = 29,  // New: above _WC_PK_TYPE_MAX
    WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX  // Could be 17, 20, or 24

Recommendation: The unconditional values 25-29 should update _WC_PK_TYPE_MAX after the conditional blocks, e.g.:

Suggested change
WC_PK_TYPE_EC_GET_SIZE = 28,
WC_PK_TYPE_EC_GET_SIG_SIZE = 29,
#undef _WC_PK_TYPE_MAX
#define _WC_PK_TYPE_MAX WC_PK_TYPE_EC_GET_SIG_SIZE
WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added #undef _WC_PK_TYPE_MAX / #define _WC_PK_TYPE_MAX WC_PK_TYPE_EC_GET_SIG_SIZE
before WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX so MAX correctly resolves to 29.

return ret;
}

ret = wc_CryptoCb_ExportKey(key->devId, WC_PK_TYPE_RSA,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM-3: Const-qualifying key parameter cast away in ExportKey calls

  • File: wolfcrypt/src/rsa.c:4533-4534 and wolfcrypt/src/rsa.c:4656-4657
  • Function: wc_RsaFlattenPublicKey, wc_RsaExportKey
  • Category: [convention]
  • Confidence: High

Description: Both wc_RsaFlattenPublicKey and wc_RsaExportKey accept const RsaKey* key, but the ExportKey callback invocation casts the const away via (void*)key. The callback implementation then treats the key as mutable (RsaKey* src = (RsaKey*)info->export_key.obj), calling wc_RsaKeyToDer(src, ...) which takes non-const RsaKey*. If a callback implementation modifies the source key, this is undefined behavior.

Code:

// In wc_RsaFlattenPublicKey (key is const RsaKey*):
ret = wc_CryptoCb_ExportKey(key->devId, WC_PK_TYPE_RSA,
                             (void*)key, tmpKey);  // cast-away-const

Recommendation: Either change wc_CryptoCb_ExportKey's obj parameter to const void*, or document that the ExportKey callback MUST NOT modify the source object. The wc_CryptoInfo struct member export_key.obj should be const void*.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed export_key.obj to const void* in the struct definition (cryptocb.h),
updated the wc_CryptoCb_ExportKey declaration and definition to take const void* obj,
and removed the (void*) cast-away-const at all 4 callsites in rsa.c and ecc.c.
Test callbacks left unchanged - they cast away const from export_key.obj since the
wolfSSL export APIs take non-const key parameters.

}
}
#endif /* WOLF_CRYPTO_CB_FREE */
#ifdef WOLF_CRYPTO_CB_SETKEY
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM-4: Significant code duplication between test callback implementations

  • File: tests/api.c:28322-28625 and wolfcrypt/test/test.c:65838-66177
  • Function: test_CryptoCb_Func, myCryptoDevCb
  • Category: [style]
  • Confidence: High

Description: The SETKEY and EXPORT_KEY callback implementations in tests/api.c and wolfcrypt/test/test.c are nearly identical (~300 lines each). Every key type handler (AES, HMAC, RSA pub/priv, ECC pub/priv) is copy-pasted between the two test files. This makes maintenance error-prone — any bug fix in one copy must be manually replicated in the other.

Recommendation: Consider extracting common callback helper functions into a shared test utility header, or use a common #include file for the shared handler implementations.

Copy link
Copy Markdown
Contributor Author

@night1rider night1rider Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is currently no sharing code between tests/api.c and wolfcrypt/test/test.c they have separate include structures and their existing crypto callback implementations (test_CryptoCb_Func vs myCryptoDevCb) take fundamentally different approaches even for the pre-existing operations (e.g., api.c loads RSA keys from PEM files while test.c uses pre-initialized keys directly).

The SETKEY/EXPORT_KEY handlers are the first significant duplication between them. Introducing a shared test utility header would be a new pattern for the repo.

I can look at consolidating this in a follow-up if desired.

#endif
if (key == NULL || priv == NULL)

if (key == NULL || priv == NULL) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM-5: _ecc_import_private_key_ex calls public wc_ecc_import_x963_ex — potential infinite recursion if devId not cleared

  • File: wolfcrypt/src/ecc.c:11415
  • Function: _ecc_import_private_key_ex
  • Category: [bug]
  • Confidence: Medium

Description: The software-only helper _ecc_import_private_key_ex calls wc_ecc_import_x963_ex(pub, pubSz, key, curve_id), which eventually calls wc_ecc_import_x963_ex2. The x963 import wrapper has SETKEY callback logic that checks key->devId != INVALID_DEVID. Since _ecc_import_private_key_ex is always called with tmpKey that has INVALID_DEVID, this works correctly — the x963 import skips the callback path.

However, this relies on an implicit contract that _ecc_import_private_key_ex is ONLY called with INVALID_DEVID keys. If a future caller passes a key with a valid devId, this would trigger a recursion loop: wc_ecc_import_private_key_ex → _ecc_import_private_key_ex → wc_ecc_import_x963_ex → wc_ecc_import_x963_ex2 (SETKEY path) → callback → wc_ecc_import_private_key_ex → .... The naming convention (_ prefix) suggests internal use, but there's no compile-time enforcement.

Recommendation: Add an assertion or comment at the top of _ecc_import_private_key_ex verifying key->devId == INVALID_DEVID, or call _ecc_import_x963_ex2 directly instead of going through the public wrapper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed _ecc_import_private_key_ex to call _ecc_import_x963_ex2(pub, pubSz, key, curve_id, 0) directly instead of going through the public wc_ecc_import_x963_ex wrapper. This eliminates the recursion risk entirely - the internal function bypasses the SETKEY callback block.

int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen,
const byte* iv, int dir)
{
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW-6: Unused cbRet variable when WOLF_CRYPTO_CB_SETKEY not combined with WOLF_CRYPTO_CB

  • File: wolfcrypt/src/aes.c:4521-4523
  • Function: wc_AesSetKey
  • Category: [style]
  • Confidence: Medium

Description: The variable cbRet is declared under #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY) but its usage is guarded by #ifdef WOLF_CRYPTO_CB_SETKEY alone (inside an #ifdef WOLF_CRYPTO_CB block). While the outer guards make the combination safe in practice, the inconsistent guard style between declaration and use could cause compiler warnings if the guards ever diverge.

Code:

#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)
    int cbRet;  // Declaration
#endif
    ...
#ifdef WOLF_CRYPTO_CB
    ...
    #ifdef WOLF_CRYPTO_CB_SETKEY     // Usage
        cbRet = wc_CryptoCb_SetKey(...);

Recommendation: NIT — the code is correct as-is since WOLF_CRYPTO_CB_SETKEY is always inside #ifdef WOLF_CRYPTO_CB. No action required.

}
return NULL;
}
#ifdef WOLF_CRYPTO_CB_SETKEY
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW-7: GetSetKeyTypeStr missing default WC_SETKEY_NONE case

  • File: wolfcrypt/src/cryptocb.c:116-128
  • Function: GetSetKeyTypeStr
  • Category: [style]
  • Confidence: Low

Description: The debug helper GetSetKeyTypeStr handles all defined WC_SETKEY_* values but returns "Unknown" for WC_SETKEY_NONE = 0. While not a bug, other similar helpers in the file (like GetPkTypeStr) return NULL for unrecognized values. This inconsistency is minor but could confuse debuggers.

Recommendation: Consider adding case WC_SETKEY_NONE: return "None"; for completeness, and returning NULL for the default case to match the pattern used by GetPkTypeStr.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added case WC_SETKEY_NONE: return "None"; and changed default to return NULL, matching the pattern used by GetPkTypeStr.

'--disable-tls --enable-cryptocb --enable-aesgcm CPPFLAGS="-DWOLF_CRYPTO_CB_AES_SETKEY -DWOLF_CRYPTO_CB_FREE"',
'--enable-all --enable-dilithium --enable-cryptocb --enable-cryptocbutils --enable-pkcallbacks',
'--enable-cryptocb --enable-aesgcm CPPFLAGS="-DWOLF_CRYPTO_CB_AES_SETKEY"',
'--enable-cryptocb --enable-keygen --enable-cryptocbutils=setkey',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOW-8: New CI test configurations don't cover WOLF_CRYPTO_CB_FIND interaction

  • File: .github/workflows/os-check.yml:87-92
  • Function: N/A (CI)
  • Category: [test]
  • Confidence: Low

Description: Six new CI configurations test various --enable-cryptocbutils combinations but none test the interaction with WOLF_CRYPTO_CB_FIND. Several code paths have #ifndef WOLF_CRYPTO_CB_FIND guards that change behavior (e.g., skipping the devId != INVALID_DEVID check). Testing this combination would increase coverage of the new feature.

Recommendation: Consider adding at least one CI config that combines --enable-cryptocbutils with WOLF_CRYPTO_CB_FIND.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'--enable-cryptocb --enable-keygen --enable-cryptocbutils=setkey,export CPPFLAGS="-DWOLF_CRYPTO_CB_FIND"'. Also fixed a pre-existing NULL pointer dereference in wc_RsaPrivateKeyDecode (asn.c) where *inOutIdx was accessed in the SETKEY block before validating inOutIdx != NULL. With WOLF_CRYPTO_CB_FIND the SETKEY block runs unconditionally, so the bad-args test case (which passes NULL) crashed. Added early NULL check for input and inOutIdx parameters.

  utilities for generic SetKey and ExportKey operations on HMAC, RSA, ECC,
  and AES. Add wc_ecc_size/wc_ecc_sig_size callback hooks for hardware-only
  keys. Integrate into configure.ac as --enable-cryptocbutils=setkey,export
  options with CI test configurations in os-check.yml.

  Add test handlers in test.c and api.c with export/import delegation
  pattern, small-stack-safe allocations, custom curve support, and
  DEBUG_CRYPTOCB helpers.
…TYPE_MAX, const-qualify export_key.obj, call _ecc_import_x963_ex2 directly, fix GetSetKeyTypeStr, fix NULL deref in wc_RsaPrivateKeyDecode with WOLF_CRYPTO_CB_FIND, add FIND CI config.
@night1rider
Copy link
Copy Markdown
Contributor Author

Had to rebase and force push due to merge conflict with .github/workflows/os-check.yml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants