SLH-DSA fixes

Follow up to PR #10450 with some minor fixes:

* FIPS 205 numbering: slh_sign is §10.2.1 Alg 22; slh_verify is Alg 24;
  hash_slh_verify is Alg 25 (impl comments and doxygen).
* Widen wc_SlhDsaKey_SignHashWithRandom's addRnd to const byte* to
  match wc_SlhDsaKey_SignWithRandom.
* Make the SLHDSA_PHMSG_MAX_LEN invariant explicit with a named
  SLHDSA_LARGEST_APPROVED_PHM_LEN constant and a wc_static_assert.
* SHAKE128/SHAKE256 round-trip and length-rejection coverage for both
  SignHash and VerifyHash.
* Doxygen: briefs for the five DER encode/decode APIs; accurate
  decoder failure-rollback wording; tighter return-code lists for
  Verify and VerifyMsg.
* ChangeLog: silent-failure caveat for raw messages whose length
  happens to equal the digest size of the chosen hashType.
This commit is contained in:
Tobias Frauenschläger
2026-05-12 13:24:14 +02:00
parent a2b054e3b8
commit bec6c0fef2
5 changed files with 256 additions and 28 deletions
+10 -5
View File
@@ -12,11 +12,16 @@
for SHAKE128, 64 bytes for SHAKE256 per FIPS 205 Section 10.2.2); otherwise
`BAD_LENGTH_E` is returned. Migration: hash the message yourself before the
call (callers using positional arguments are source-compatible; only the
parameter names changed). The pre-existing `wc_SlhDsaKey_SignMsgDeterministic`
and `wc_SlhDsaKey_SignMsgWithRandom` (FIPS 205 internal interface, M'
supplied directly) are unaffected and gain stricter input validation
matching the `*Hash*` family. `wc_SlhDsaKey_VerifyMsg` is unchanged. All
three gain doxygen coverage.
parameter names changed). Caveat: callers who today pass a raw message
whose length happens to equal the digest size for the chosen `hashType`
(e.g., signing a 32-byte handle/IV/seed with `WC_HASH_TYPE_SHA256`) will
not trip `BAD_LENGTH_E`; the resulting signature is syntactically valid
but is over the wrong bytes. The pre-existing
`wc_SlhDsaKey_SignMsgDeterministic` and `wc_SlhDsaKey_SignMsgWithRandom`
retain their M'-supplied-directly contract (FIPS 205 internal interface,
Algorithm 19); their input validation is hardened with the same
NULL/length/`MISSING_KEY` checks as the `*Hash*` family.
`wc_SlhDsaKey_VerifyMsg` is unchanged. All three gain doxygen coverage.
* TLS 1.3: zero traffic key staging buffers in `SetKeysSide()` once a
CryptoCB callback has imported the AES key into a Secure Element
+195 -4
View File
@@ -260,11 +260,15 @@ int wc_SlhDsaKey_Sign(SlhDsaKey* key, const byte* ctx,
\ingroup SLH_DSA
\brief Verifies an SLH-DSA signature over a message using the external
(pure) interface. This is FIPS 205 Algorithm 23. The message is wrapped
(pure) interface. This is FIPS 205 Algorithm 24. The message is wrapped
internally as M' = 0x00 || len(ctx) || ctx || M before verification.
\return 0 on success (signature valid).
\return BAD_FUNC_ARG if key, msg, or sig is NULL.
\return BAD_FUNC_ARG if key, msg, or sig is NULL, or ctx is NULL but
ctxSz is greater than 0.
\return BAD_LENGTH_E if sigSz does not match the parameter set's
signature length.
\return MISSING_KEY if the public key has not been set.
\return SIG_VERIFY_E if the signature is invalid.
\param [in] key Pointer to a public SlhDsaKey.
@@ -397,6 +401,9 @@ int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key,
\return 0 on success (signature valid).
\return BAD_FUNC_ARG if key, mprime, or sig is NULL.
\return BAD_LENGTH_E if sigSz does not match the parameter set's
signature length.
\return MISSING_KEY if the public key has not been set.
\return SIG_VERIFY_E if the signature is invalid.
\param [in] key Pointer to a public SlhDsaKey.
@@ -513,7 +520,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key,
*/
int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key,
const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz,
enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd);
enum wc_HashType hashType, byte* sig, word32* sigSz, const byte* addRnd);
/*!
\ingroup SLH_DSA
@@ -553,7 +560,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx,
\ingroup SLH_DSA
\brief Verifies an SLH-DSA signature using the external HashSLH-DSA
interface (FIPS 205 Algorithm 24). The caller must hash the application
interface (FIPS 205 Algorithm 25). The caller must hash the application
message with hashType first and pass the digest as hash; this function
does NOT hash its input.
@@ -835,3 +842,187 @@ int wc_SlhDsaKey_PublicSizeFromParam(enum SlhDsaParam param);
\sa wc_SlhDsaKey_SigSize
*/
int wc_SlhDsaKey_SigSizeFromParam(enum SlhDsaParam param);
/*!
\ingroup SLH_DSA
\brief Decodes a DER-encoded SLH-DSA private key in the PKCS#8
OneAsymmetricKey format defined by RFC 9909. The privateKey OCTET STRING
contains the raw concatenation SK.seed || SK.prf || PK.seed || PK.root
(4*n bytes) directly, without a nested OCTET STRING wrapper as used by
Ed25519/Ed448. The SLH-DSA parameter set is detected from the
AlgorithmIdentifier OID and key->params is updated to match. Available
only when WOLFSSL_SLHDSA_VERIFY_ONLY is not defined.
On a failure that is detected before any write to key->sk
(BAD_FUNC_ARG, header/OID parse errors, or wrong privateKey length), the
key state is left untouched. On a failure detected after
wc_SlhDsaKey_ImportPrivate has populated key->sk (a SHA-2 precompute
error, or a trailing-field validation error), key->sk is scrubbed with
ForceZero and the WC_SLHDSA_FLAG_PRIVATE/PUBLIC flags are cleared so
flags can never claim valid bytes that were zeroed. In both rollback
cases, key->params and inOutIdx are restored to their pre-call values.
\return 0 on success.
\return BAD_FUNC_ARG if input, inOutIdx, or key is NULL, or inSz is 0.
\return ASN_PARSE_E if the DER cannot be parsed as an SLH-DSA private
key (malformed input, wrong key size, or trailing-field violation).
\return NOT_COMPILED_IN if the OID names an SLH-DSA variant that is not
built into this library.
\param [in] input DER-encoded key data.
\param [in,out] inOutIdx On input, starting offset into input. On output,
advanced past the parsed key (unchanged on failure).
\param [in,out] key SLH-DSA key. Parameter set is auto-detected from the
encoded OID.
\param [in] inSz Total size of input in bytes.
\sa wc_SlhDsaKey_KeyToDer
\sa wc_SlhDsaKey_PublicKeyDecode
\sa wc_SlhDsaKey_ImportPrivate
*/
int wc_SlhDsaKey_PrivateKeyDecode(const byte* input, word32* inOutIdx,
SlhDsaKey* key, word32 inSz);
/*!
\ingroup SLH_DSA
\brief Decodes a DER-encoded SLH-DSA public key in the
SubjectPublicKeyInfo (SPKI) format. The SLH-DSA parameter set is
detected from the AlgorithmIdentifier OID and key->params is updated
accordingly.
As a fast path, if key->params is already set the function first hands
the entire window from inOutIdx to inSz to wc_SlhDsaKey_ImportPublic.
ImportPublic's length check is the disambiguator: a window of exactly
2*n bytes is accepted as a raw public key (PK.seed || PK.root) and
consumed in full; any other length is rejected and the function falls
through to SPKI parsing. SPKI input always carries enough
AlgorithmIdentifier/BIT STRING overhead that it never collides with the
2*n raw length, so it falls through cleanly. The caller does not need
to pre-trim the window to 2*n.
On a failure detected before any write (BAD_FUNC_ARG or a malformed
SPKI), the key state is left untouched. On a failure detected after
ImportPublic has populated the public half of key->sk (a SHA-2
precompute error), the public half sk[2*n .. 4*n] is scrubbed and
WC_SLHDSA_FLAG_PUBLIC is cleared from the flags; the private half is
left intact in case the caller imported it earlier. key->params and
inOutIdx are restored to their pre-call values.
\return 0 on success.
\return BAD_FUNC_ARG if input, inOutIdx, or key is NULL, or inSz is 0.
\return ASN_PARSE_E if the DER cannot be parsed as an SLH-DSA public
key.
\return NOT_COMPILED_IN if the OID names an SLH-DSA variant that is not
built into this library.
\param [in] input DER-encoded key data, or a raw 2*n public key when
key->params is already set.
\param [in,out] inOutIdx On input, starting offset into input. On output,
advanced past the parsed key (unchanged on failure).
\param [in,out] key SLH-DSA key. Parameter set is auto-detected from the
encoded OID, or honored as-is in the raw fast path.
\param [in] inSz Total size of input in bytes.
\sa wc_SlhDsaKey_PublicKeyToDer
\sa wc_SlhDsaKey_PrivateKeyDecode
\sa wc_SlhDsaKey_ImportPublic
*/
int wc_SlhDsaKey_PublicKeyDecode(const byte* input, word32* inOutIdx,
SlhDsaKey* key, word32 inSz);
/*!
\ingroup SLH_DSA
\brief Encodes an SLH-DSA private key to DER in the PKCS#8
OneAsymmetricKey format defined by RFC 9909. The privateKey OCTET STRING
contains the raw 4*n bytes (SK.seed || SK.prf || PK.seed || PK.root)
directly, without the nested OCTET STRING wrapping used by Ed25519/Ed448.
Available only when WOLFSSL_SLHDSA_VERIFY_ONLY is not defined and
WC_ENABLE_ASYM_KEY_EXPORT is set.
\return Size of the encoded DER in bytes on success. Pass NULL as output
to query the required buffer size without writing.
\return BAD_FUNC_ARG if key or key->params is NULL.
\return MISSING_KEY if the private key has not been set.
\return BUFFER_E if output is non-NULL and inLen is smaller than the
required size.
\return NOT_COMPILED_IN if key->params names an SLH-DSA variant whose
parameter set is not built in.
\param [in] key SLH-DSA key with a populated private key.
\param [out] output Buffer to receive the DER encoding, or NULL to query
the required size.
\param [in] inLen Size of output in bytes (ignored when output is NULL).
\sa wc_SlhDsaKey_PrivateKeyDecode
\sa wc_SlhDsaKey_PrivateKeyToDer
\sa wc_SlhDsaKey_PublicKeyToDer
*/
int wc_SlhDsaKey_KeyToDer(SlhDsaKey* key, byte* output, word32 inLen);
/*!
\ingroup SLH_DSA
\brief Encodes an SLH-DSA private key to DER. RFC 9909 packs
SK.seed || SK.prf || PK.seed || PK.root into a single OCTET STRING, so
SLH-DSA has no distinct private-only encoding. This function is an
intentional alias of wc_SlhDsaKey_KeyToDer, kept for API parity with
Ed25519/Ed448 which do have a separate private form.
Available only when WOLFSSL_SLHDSA_VERIFY_ONLY is not defined and
WC_ENABLE_ASYM_KEY_EXPORT is set.
Return codes are inherited unchanged from wc_SlhDsaKey_KeyToDer.
\return Size of the encoded DER in bytes on success. Pass NULL as output
to query the required buffer size.
\return BAD_FUNC_ARG if key or key->params is NULL.
\return MISSING_KEY if the private key has not been set.
\return BUFFER_E if output is non-NULL and inLen is smaller than the
required size.
\return NOT_COMPILED_IN if key->params names an SLH-DSA variant whose
parameter set is not built in.
\param [in] key SLH-DSA key with a populated private key.
\param [out] output Buffer to receive the DER encoding, or NULL to query
the required size.
\param [in] inLen Size of output in bytes (ignored when output is NULL).
\sa wc_SlhDsaKey_KeyToDer
\sa wc_SlhDsaKey_PrivateKeyDecode
*/
int wc_SlhDsaKey_PrivateKeyToDer(SlhDsaKey* key, byte* output, word32 inLen);
/*!
\ingroup SLH_DSA
\brief Encodes an SLH-DSA public key to DER. When withAlg is non-zero
the output is a full SubjectPublicKeyInfo structure (AlgorithmIdentifier
plus BIT STRING). When withAlg is zero the output contains the raw
public key bytes without the SPKI wrapping.
Available only when WC_ENABLE_ASYM_KEY_EXPORT is set.
\return Size of the encoded DER in bytes on success. Pass NULL as output
to query the required buffer size.
\return BAD_FUNC_ARG if key or key->params is NULL.
\return BUFFER_E if output is non-NULL and inLen is smaller than the
required size.
\return NOT_COMPILED_IN if key->params names an SLH-DSA variant whose
parameter set is not built in.
\param [in] key SLH-DSA key with a populated public key.
\param [out] output Buffer to receive the DER encoding, or NULL to query
the required size.
\param [in] inLen Size of output in bytes (ignored when output is NULL).
\param [in] withAlg Non-zero to emit SubjectPublicKeyInfo (with
AlgorithmIdentifier); zero to emit the raw public key only.
\sa wc_SlhDsaKey_PublicKeyDecode
\sa wc_SlhDsaKey_KeyToDer
*/
int wc_SlhDsaKey_PublicKeyToDer(SlhDsaKey* key, byte* output, word32 inLen,
int withAlg);
+20
View File
@@ -1151,6 +1151,22 @@ int test_wc_slhdsa_sign_hash(void)
WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif
#ifdef WOLFSSL_SHAKE128
/* SHAKE128 PHM is fixed at 256 bits per FIPS 205 Section 10.2.2. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHAKE128, sig, &sigLen, &rng), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHAKE128, sig, sigLen), 0);
/* SignHash and VerifyHash both reject mismatched digest length. */
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64,
WC_HASH_TYPE_SHAKE128, sig, &sigLen, &rng),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 64,
WC_HASH_TYPE_SHAKE128, sig, sigLen),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif
#ifdef WOLFSSL_SHAKE256
/* SHAKE256 PHM is fixed at 512 bits per FIPS 205 Section 10.2.2. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
@@ -1158,9 +1174,13 @@ int test_wc_slhdsa_sign_hash(void)
WC_HASH_TYPE_SHAKE256, sig, &sigLen, &rng), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 64,
WC_HASH_TYPE_SHAKE256, sig, sigLen), 0);
/* SignHash and VerifyHash both reject mismatched digest length. */
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHAKE256, sig, &sigLen, &rng),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHAKE256, sig, sigLen),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif
wc_SlhDsaKey_Free(&key);
+30 -18
View File
@@ -7015,7 +7015,7 @@ static int slhdsakey_sign_internal_msg(SlhDsaKey* key, const byte* m,
/* Upper-level sign: construct M' from ctx + msg, then call internal.
*
* FIPS 205. Section 10.2.2. Algorithm 22.
* FIPS 205. Section 10.2.1. Algorithm 22.
* slh_sign(M, ctx, SK)
* 8: M' <- toByte(0, 1) || toByte(|ctx|, 1) || ctx || M
* 9: SIG <- slh_sign_internal(M', SK, addrnd)
@@ -7414,7 +7414,7 @@ static int slhdsakey_verify(SlhDsaKey* key, byte* md, const byte* sig)
* 9: md <- digest [0 : upper(k.a / 8)] > first upper(k.a / 8) bytes
* ...
*
* FIPS 205. Section 10.3. Algorithm 23.
* FIPS 205. Section 10.3. Algorithm 24.
* slh_verify(M, SIG, ctx, PK)
* 1: if |ctx| > 255 then
* 2: return false
@@ -7464,7 +7464,7 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx, byte ctxSz,
byte n = key->params->n;
byte hdr[2];
/* Alg 23, Step 4: Make M' header. */
/* Alg 24, Step 4: Make M' header. */
hdr[0] = 0;
hdr[1] = ctxSz;
@@ -7501,7 +7501,7 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx, byte ctxSz,
}
}
if (ret == 0) {
/* Alg 23, Step 5: Verify M'.
/* Alg 24, Step 5: Verify M'.
* Alg 20, Steps 4,6-18: Verify digest. */
ret = slhdsakey_verify(key, md, sig);
}
@@ -7582,12 +7582,20 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime,
/* All HashSLH-DSA hash OIDs are DER-encoded as tag(0x06) + length(0x09) + 9
* bytes, so any approved hash OID is exactly 11 bytes. The PRF_msg / H_msg
* input for the SHA-2 path is the concatenation OID || PHM, bounded by
* SLHDSA_OID_MAX_LEN + WC_MAX_DIGEST_SIZE. WC_MAX_DIGEST_SIZE is the project-
* wide max digest size (>= 64 today) and absorbs any future hash with a
* larger digest as long as slhdsakey_validate_prehash continues to enforce
* hashSz <= WC_MAX_DIGEST_SIZE. */
#define SLHDSA_OID_MAX_LEN 11
#define SLHDSA_PHMSG_MAX_LEN (SLHDSA_OID_MAX_LEN + WC_MAX_DIGEST_SIZE)
* SLHDSA_OID_MAX_LEN + WC_MAX_DIGEST_SIZE. The PHM buffer fits in
* WC_MAX_DIGEST_SIZE bytes because slhdsakey_validate_prehash enforces
* hashSz == expectedLen[hashType] for every supported hashType and every
* supported expectedLen is <= WC_MAX_DIGEST_SIZE. The largest FIPS 205
* approved PHM is 64 bytes (SHA-512 digest size, also the SHAKE256 PHM
* length fixed at 512 bits per Section 10.2.2). The static assert below
* catches a future hash being added whose digest exceeds the bound. The
* literal 64 is used directly because WC_SHA512_DIGEST_SIZE is only
* defined when SHA-512 is compiled in. */
#define SLHDSA_OID_MAX_LEN 11
#define SLHDSA_LARGEST_APPROVED_PHM_LEN 64
#define SLHDSA_PHMSG_MAX_LEN (SLHDSA_OID_MAX_LEN + \
WC_MAX_DIGEST_SIZE)
wc_static_assert(WC_MAX_DIGEST_SIZE >= SLHDSA_LARGEST_APPROVED_PHM_LEN);
#ifdef WOLFSSL_SHA224
/* OID for SHA-224 for hash signing/verification. */
@@ -7863,13 +7871,13 @@ static int slhdsakey_validate_prehash(word32 hashSz,
* @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0.
* @return BAD_LENGTH_E when sigSz is less than required signature length, or
* when hashSz does not equal the digest size for hashType.
* @return NOT_COMPILED in when hash algorithm is not supported.
* @return NOT_COMPILED_IN when hash algorithm is not supported.
* @return MEMORY_E on dynamic memory allocation failure.
* @return SHAKE-256 error return code on digest failure.
*/
static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx,
byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType,
byte* sig, word32* sigSz, byte* addRnd)
byte* sig, word32* sigSz, const byte* addRnd)
{
int ret = 0;
const byte* oid = NULL;
@@ -8064,7 +8072,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx,
*/
int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz,
const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig,
word32* sigSz, byte* addRnd)
word32* sigSz, const byte* addRnd)
{
/* HashSLH-DSA sign with caller-supplied digest. */
return slhdsakey_signhash_external(key, ctx, ctxSz, hash, hashSz, hashType,
@@ -8105,7 +8113,11 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
int ret = 0;
byte addRnd[SLHDSA_MAX_N];
/* Validate parameters before generating random. */
/* Validate parameters before generating random.
* hashSz / hashType validation lives in the internal worker and therefore
* runs after wc_RNG_GenerateBlock. A call with a bad hashSz/hashType will
* waste n bytes of DRBG output before the error is reported (similar to
* ML-DSA pre-hash handling). */
if ((key == NULL) || (key->params == NULL) ||
((ctx == NULL) && (ctxSz > 0)) || (hash == NULL) || (sig == NULL) ||
(sigSz == NULL) || (rng == NULL)) {
@@ -8149,7 +8161,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
* 9: md <- digest [0 : upper(k.a / 8)] > first upper(k.a / 8) bytes
* ...
*
* FIPS 205. Section 10.3. Algorithm 24.
* FIPS 205. Section 10.3. Algorithm 25.
* hash_slh_verify(M, SIG, ctx, PH, PK)
* 1: if |ctx| > 255 then
* 2: return false
@@ -8222,7 +8234,7 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
ret = MISSING_KEY;
}
if (ret == 0) {
/* Alg 24, Steps 4-19: Validate caller-supplied pre-hashed digest length
/* Alg 25, Steps 4-19: Validate caller-supplied pre-hashed digest length
* and select OID for the chosen hash algorithm. */
ret = slhdsakey_validate_prehash(hashSz, hashType, &oid, &oidLen);
}
@@ -8231,7 +8243,7 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
byte md[SLHDSA_MAX_MD];
byte hdr[2];
/* Alg 24, Step 20: Make M' header. */
/* Alg 25, Step 20: Make M' header. */
hdr[0] = 1;
hdr[1] = ctxSz;
@@ -8277,7 +8289,7 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
}
}
if (ret == 0) {
/* Alg 24, Step 21: Verify M'.
/* Alg 25, Step 21: Verify M'.
* Alg 20, Steps 4,6-18: Verify digest. */
ret = slhdsakey_verify(key, md, sig);
}
+1 -1
View File
@@ -685,7 +685,7 @@ WOLFSSL_API int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key,
enum wc_HashType hashType, byte* sig, word32* sigSz);
WOLFSSL_API int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key,
const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz,
enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd);
enum wc_HashType hashType, byte* sig, word32* sigSz, const byte* addRnd);
WOLFSSL_API int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx,
byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType,
byte* sig, word32* sigSz, WC_RNG* rng);