Skip to content

slhdsa: add WOLF_CRYPTO_CB and WOLF_PRIVATE_KEY_ID support#18

Open
Frauschi wants to merge 3 commits intomasterfrom
claude/add-slh-dsa-support-lbAbB
Open

slhdsa: add WOLF_CRYPTO_CB and WOLF_PRIVATE_KEY_ID support#18
Frauschi wants to merge 3 commits intomasterfrom
claude/add-slh-dsa-support-lbAbB

Conversation

@Frauschi
Copy link
Copy Markdown
Owner

@Frauschi Frauschi commented May 8, 2026

Summary

Wires SLH-DSA into the existing PQC CryptoCb dispatch (the same path already used by ML-DSA / Dilithium) so a registered device can intercept key generation, signing, verification, and key release. Also adds algorithm-specific id/label storage on SlhDsaKey for use with WOLF_PRIVATE_KEY_ID, mirroring the dilithium_key API.

Changes

  • wolfssl/wolfcrypt/types.h — add WC_PQC_SIG_TYPE_SLHDSA = 3; widen the WC_PK_TYPE_PQC_SIG_* and wc_PqcSignatureType guards to include WOLFSSL_HAVE_SLHDSA.
  • wolfssl/wolfcrypt/cryptocb.h / wolfcrypt/src/cryptocb.c — include wc_slhdsa.h, widen the PQC sig guards, and teach wc_CryptoCb_PqcSigGetDevId to read devId from SlhDsaKey. Reuses the existing generic pqc_sig_kg / pqc_sign / pqc_verify info-struct members — no new union fields.
  • wolfssl/wolfcrypt/wc_slhdsa.h
    • Add void* devCtx alongside int devId under WOLF_CRYPTO_CB.
    • Add id[SLHDSA_MAX_ID_LEN], label[SLHDSA_MAX_LABEL_LEN], idLen, labelLen (both maxes = 32) under WOLF_PRIVATE_KEY_ID.
    • Declare wc_SlhDsaKey_Init_id and wc_SlhDsaKey_Init_label.
  • wolfcrypt/src/wc_slhdsa.c
    • Implement wc_SlhDsaKey_Init_id / wc_SlhDsaKey_Init_label (mirrors the dilithium idiom).
    • Hook wc_CryptoCb_MakePqcSignatureKey in MakeKey, wc_CryptoCb_PqcSign in Sign / SignHash, wc_CryptoCb_PqcVerify in Verify / VerifyHash. Hooks live at the top-level public API only — Deterministic / WithRandom / internal-M' variants run software-only.
    • Add wc_CryptoCb_Free dispatch in wc_SlhDsaKey_Free (gated by WOLF_CRYPTO_CB_FREE) and scrub id / label / devCtx on free.

Test plan

  • ./configure --enable-slhdsa --enable-cryptocb --enable-dilithium CFLAGS="-DWOLF_PRIVATE_KEY_ID -DWOLF_CRYPTO_CB_FREE" builds cleanly.
  • ./wolfcrypt/test/testwolfcrypt passes (SLH-DSA + DILITHIUM + crypto callback tests all green) on the same config.
  • ./configure --enable-cryptocb --enable-dilithium (no SLH-DSA) builds and passes — confirms guard widening doesn't regress existing builds.
  • End-to-end device-callback exercise: extend myCryptoDevCb in wolfcrypt/test/test.c to dispatch WC_PQC_SIG_TYPE_SLHDSA cases back to the software path. Out of scope for this PR; the existing tests still cover the INVALID_DEVID / fall-through path.

🤖 Generated with Claude Code


Generated by Claude Code

claude added 3 commits May 8, 2026 21:21
Wire SLH-DSA into the existing PQC CryptoCb dispatch (the same path
already used by ML-DSA / Dilithium) so a registered device can intercept
key generation, signing, verification, and key release. Also add
algorithm-specific id/label storage on SlhDsaKey for use with
WOLF_PRIVATE_KEY_ID, mirroring the dilithium_key API.

- types.h: add WC_PQC_SIG_TYPE_SLHDSA and widen the WC_PK_TYPE_PQC_SIG_*
  / wc_PqcSignatureType guards to include WOLFSSL_HAVE_SLHDSA.
- cryptocb.h/.c: include wc_slhdsa.h, widen the PQC sig guards, and
  teach wc_CryptoCb_PqcSigGetDevId to extract devId from SlhDsaKey.
- wc_slhdsa.h: add devCtx (alongside devId) under WOLF_CRYPTO_CB; add
  id/label storage and SLHDSA_MAX_{ID,LABEL}_LEN under WOLF_PRIVATE_KEY_ID;
  declare wc_SlhDsaKey_Init_id and _Init_label.
- wc_slhdsa.c: implement Init_id / Init_label; hook
  wc_CryptoCb_MakePqcSignatureKey in MakeKey, wc_CryptoCb_PqcSign in
  Sign / SignHash, wc_CryptoCb_PqcVerify in Verify / VerifyHash, and
  wc_CryptoCb_Free in Free; scrub id/label/devCtx on free.
Six fixes from PR review:

1. wc_SlhDsaKey_Verify / VerifyHash: stop collapsing non-zero cryptocb
   error codes (MEMORY_E, transport errors) into SIG_VERIFY_E. Real
   errors are returned as-is; only res != 1 with ret == 0 maps to
   SIG_VERIFY_E.

2. wc_SlhDsaKey_SignHash / VerifyHash: pre-compute the digest via
   slhdsakey_prehash_msg before invoking the cryptocb, then pass
   (digest, digestLen, hashType) to the device. Matches the existing
   ML-DSA cryptocb contract for the pre-hash variant; device backends
   can be written once for both algorithms.

3. cryptocb test coverage: route slhdsa_test through cryptocb_test by
   passing the global devId to wc_SlhDsaKey_Init in slhdsa_test_param
   (mirrors dilithium_test); extend myCryptoDevCb's WC_PK_TYPE_PQC_SIG_KEYGEN
   free dispatch with a WC_PQC_SIG_TYPE_SLHDSA arm. Exercises
   PqcSigGetDevId's new SLHDSA branch and wc_CryptoCb_Free for SLH-DSA.

4. wc_SlhDsaKey_Init_id: reject (id == NULL, len > 0) and
   (id != NULL, len <= 0) as BAD_FUNC_ARG instead of silently
   continuing with idLen = 0.

5. wc_SlhDsaKey_Free: hoist wc_CryptoCb_Free above the params != NULL
   gate so a partially-initialized key still releases its device-side
   handle.

6. SignHash / VerifyHash: reject negative wc_HashType before casting
   to word32 to avoid silently mis-routing the device call.
1. wc_SlhDsaKey_Init_id: drop the over-strict (id != NULL, len <= 0)
   rejection. Restore dilithium_init_id parity — only the silent
   contradiction (id == NULL, len > 0) is a caller bug.

2. wc_SlhDsaKey_VerifyHash / SignHash: compute the digest once. VerifyHash
   hoists slhdsakey_prehash_msg above the cryptocb hook so the same
   ph/phLen/oid/oidLen feed both the cryptocb dispatch and the SW path.
   SignHash extracts a new static slhdsakey_signhash_with_digest helper
   from slhdsakey_signhash_external; the cryptocb fall-through reuses the
   already-computed digest instead of re-hashing through SignHashWithRandom.
   slhdsakey_signhash_external now just prehashes + delegates.

3. cryptocb_test: extend myCryptoDevCtx with slhdsaInjectVerifyError; when
   set, the SLH-DSA verify dispatch returns that exact code. Add an
   inline test that signs a message, sets the inject flag to WC_HW_E,
   calls wc_SlhDsaKey_Verify, and asserts WC_HW_E propagates (not
   SIG_VERIFY_E). Initialize myCtx via XMEMSET — the new field would
   otherwise read garbage from stack and inject a random value.

4. slhdsa_test: add slhdsa_id_label_test() (gated on WOLF_PRIVATE_KEY_ID +
   one 128-bit param). Covers Init_id NULL key / NULL+len>0 / oversized
   len / negative len / round-trip; Init_label NULL key / NULL label /
   empty label / round-trip; and dilithium-parity case (id != NULL, len
   == 0 succeeds with idLen == 0).

5. wc_SlhDsaKey_Free: revert round-1's hoist of wc_CryptoCb_Free above
   the params!=NULL gate. wc_SlhDsaKey_Init is atomic — devId and params
   are set together — so params != NULL reliably signals "fully
   initialized" and prevents wc_CryptoCb_Free firing on a never-init key
   with garbage devId.

6. wc_SlhDsaKey_SignHash / VerifyHash: hoist the negative-hashType check
   into the function-level validation block, before any large stack
   buffers (ph[WC_MAX_DIGEST_SIZE]) are reserved.

7. Cleaned up: the cryptocb hook no longer keeps unused oidBuf/oidBufLen
   locals — the function-scope ph/oid plumbing replaces them.
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.

2 participants