From d1f4e56e0ae35716dfed8bbf41d5b1756c377e7e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 10 May 2026 13:34:00 +0000 Subject: [PATCH 1/4] SLH-DSA: align HashSLH-DSA API with HashML-DSA (caller-pre-hashed digest) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The four wc_SlhDsaKey_*Hash* entry points used to take the full message and hash it internally, while the ML-DSA counterparts (wc_dilithium_sign_ctx_hash / wc_dilithium_verify_ctx_hash) take the caller's pre-hashed digest. That divergence made HashSLH-DSA the outlier among wolfSSL's PQ signature schemes and against OpenSSL's HASH-ML-DSA, mldsa-native, leancrypto SLH-DSA, and NIST ACVP signatureInterface=external/preHash test vectors -- all of which take the digest at the pre-hash boundary. Refactor slhdsakey_prehash_msg to validate that msgSz equals the digest size for hashType (32 bytes for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2) and copy the digest in place of the previous wc_*Hash call. Returns BAD_LENGTH_E on size mismatch; BAD_FUNC_ARG / NOT_COMPILED_IN behaviour is unchanged. Sign-/verify-side M' construction is untouched. Also tighten the pre-existing wc_SlhDsaKey_SignMsg{Deterministic,WithRandom} input validation (NULL/sigSz/key-flag checks) so they front-match the *Hash* family. These are the FIPS 205 internal interface (Algorithm 19) and serve as the SLH-DSA analog of wc_dilithium_verify_mu for ACVP signatureInterface= internal testing -- documented as such. In-tree callers updated to pre-hash before calling: wolfcrypt self-test in slhdsa_test_param (SHA-256/SHAKE256/SHA-384 paths) and benchmark sign-pre/ vrfy-pre rows (SHA-256, hashed once outside the timed loop). API tests gain BAD_LENGTH_E coverage, SHA-512 and SHAKE256 round-trips, and a new test_wc_slhdsa_sign_msg covering the SignMsg* family plus a cross-check that proves an externally-built M' sign matches a SignHashDeterministic byte-for- byte. Doxygen and ChangeLog updated; entry flagged BREAKING. https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd --- ChangeLog.md | 14 ++ doc/dox_comments/header_files/wc_slhdsa.h | 107 +++++++---- tests/api/test_slhdsa.c | 182 ++++++++++++++++++- tests/api/test_slhdsa.h | 2 + wolfcrypt/benchmark/benchmark.c | 65 ++++--- wolfcrypt/src/wc_slhdsa.c | 211 +++++++++++++++------- wolfcrypt/test/test.c | 55 ++++-- wolfssl/wolfcrypt/wc_slhdsa.h | 5 +- 8 files changed, 491 insertions(+), 150 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 6e09b1e8924..1fc43404ea8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,20 @@ ## Enhancements +* **BREAKING (FIPS 205 SLH-DSA)**: `wc_SlhDsaKey_SignHash`, + `wc_SlhDsaKey_SignHashDeterministic`, `wc_SlhDsaKey_SignHashWithRandom`, and + `wc_SlhDsaKey_VerifyHash` now treat `msg`/`msgSz` as the **caller-pre-hashed + message digest**, matching `wc_dilithium_sign_ctx_hash` / + `wc_dilithium_verify_ctx_hash` and aligning with OpenSSL's HASH-ML-DSA, + mldsa-native, leancrypto SLH-DSA, and NIST ACVP `signatureInterface=external` + / `preHash=preHash` test vectors. `msgSz` must equal + `wc_HashGetDigestSize(hashType)` (32 bytes for SHAKE128, 64 bytes for + SHAKE256 per FIPS 205 §10.2.2); otherwise `BAD_LENGTH_E` is returned. + Migration: hash the message yourself before the call. The pre-existing + `wc_SlhDsaKey_SignMsgDeterministic`, `wc_SlhDsaKey_SignMsgWithRandom`, and + `wc_SlhDsaKey_VerifyMsg` (FIPS 205 internal interface, M' supplied directly) + are unaffected and gain stricter input validation and 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 (`aes->devCtx != NULL`). Clears `keys->{client,server}_write_key` diff --git a/doc/dox_comments/header_files/wc_slhdsa.h b/doc/dox_comments/header_files/wc_slhdsa.h index a520d153b2c..81b4eca9c6b 100644 --- a/doc/dox_comments/header_files/wc_slhdsa.h +++ b/doc/dox_comments/header_files/wc_slhdsa.h @@ -302,15 +302,19 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx, \brief Signs using the SLH-DSA internal interface with deterministic randomness. Unlike the external interface, M' is provided directly by - the caller — no 0x00||len(ctx)||ctx||M wrapping is performed. This - corresponds to FIPS 205 Algorithm 19 (slh_sign_internal) with opt_rand - set to PK.seed. + the caller — no wrapping is performed. This corresponds to FIPS 205 + Algorithm 19 (slh_sign_internal) with opt_rand set to PK.seed. - Use this when the CAVP test framework or protocol layer has already - constructed M'. + Use this when the ACVP signatureInterface=internal test framework or a + protocol layer has already constructed M'. For HashSLH-DSA the caller + builds M' as 0x01 || ctxSz || ctx || OID(hashType) || PHM and passes it + in here, where PHM is the hash of the application message under hashType. \return 0 on success. \return BAD_FUNC_ARG if key, mprime, sig, or sigSz is NULL. + \return BAD_LENGTH_E if sigSz is less than the parameter set's signature + length. + \return MISSING_KEY if the private key has not been set. \param [in] key Pointer to a private SlhDsaKey. \param [in] mprime Pointer to the pre-constructed M' message. @@ -344,10 +348,14 @@ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, \brief Signs using the SLH-DSA internal interface with caller-provided additional randomness. M' is provided directly — no wrapping is performed. This corresponds to FIPS 205 Algorithm 19 (slh_sign_internal) with an - explicit opt_rand value. + explicit opt_rand value. See wc_SlhDsaKey_SignMsgDeterministic for the M' + layout used by HashSLH-DSA. \return 0 on success. \return BAD_FUNC_ARG if key, mprime, sig, sigSz, or addRnd is NULL. + \return BAD_LENGTH_E if sigSz is less than the parameter set's signature + length. + \return MISSING_KEY if the private key has not been set. \param [in] key Pointer to a private SlhDsaKey. \param [in] mprime Pointer to the pre-constructed M' message. @@ -419,22 +427,28 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, /*! \ingroup SLH_DSA - \brief Signs a pre-hashed message using the SLH-DSA external (HashSLH-DSA) - interface with deterministic randomness. The message is hashed with the - specified hash algorithm, then signed per FIPS 205 Algorithm 22 with the - pre-hash domain separator (0x01). + \brief Signs a caller-pre-hashed message digest using the SLH-DSA external + (HashSLH-DSA) interface with deterministic randomness, per FIPS 205 + Algorithm 23 with the pre-hash domain separator (0x01). The caller must + hash the application message with hashType first and pass the digest as + msg; this function does NOT hash msg. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, or sigSz is NULL, or hashType - is unsupported. + \return BAD_FUNC_ARG if key, msg, sig, or sigSz is NULL. + \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType + (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + \return NOT_COMPILED_IN if hashType is not supported in this build. + \return MISSING_KEY if the private key has not been set. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the message to hash and sign. - \param [in] msgSz Length of the message. - \param [in] hashType Hash algorithm to use for pre-hashing. Supported: - WC_HASH_TYPE_SHA256, WC_HASH_TYPE_SHA384, WC_HASH_TYPE_SHA512, + \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal + the digest size for hashType. + \param [in] msgSz Length of the digest in bytes. + \param [in] hashType Hash algorithm used for pre-hashing (selects OID). + Supported: WC_HASH_TYPE_SHA224, WC_HASH_TYPE_SHA256, WC_HASH_TYPE_SHA384, + WC_HASH_TYPE_SHA512, WC_HASH_TYPE_SHA512_224, WC_HASH_TYPE_SHA512_256, WC_HASH_TYPE_SHAKE128, WC_HASH_TYPE_SHAKE256, WC_HASH_TYPE_SHA3_224, WC_HASH_TYPE_SHA3_256, WC_HASH_TYPE_SHA3_384, WC_HASH_TYPE_SHA3_512. \param [out] sig Buffer to receive the signature. @@ -447,10 +461,12 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, byte sig[WC_SLHDSA_MAX_SIG_LEN]; word32 sigSz = sizeof(sig); byte msg[] = "Hello World!"; + byte digest[WC_SHA256_DIGEST_SIZE]; int ret; + wc_Sha256Hash(msg, sizeof(msg), digest); ret = wc_SlhDsaKey_SignHashDeterministic(&key, NULL, 0, - msg, sizeof(msg), WC_HASH_TYPE_SHA256, sig, &sigSz); + digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig, &sigSz); \endcode \sa wc_SlhDsaKey_SignHashWithRandom @@ -464,18 +480,24 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, /*! \ingroup SLH_DSA - \brief Signs a pre-hashed message using the SLH-DSA external (HashSLH-DSA) - interface with caller-provided additional randomness. + \brief Signs a caller-pre-hashed message digest using the SLH-DSA external + (HashSLH-DSA) interface with caller-provided additional randomness. The + caller must hash the application message with hashType first and pass the + digest as msg; this function does NOT hash msg. \return 0 on success. \return BAD_FUNC_ARG if key, msg, sig, sigSz, or addRnd is NULL. + \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType + (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + \return NOT_COMPILED_IN if hashType is not supported in this build. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the message to hash and sign. - \param [in] msgSz Length of the message. - \param [in] hashType Hash algorithm to use for pre-hashing. + \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal + the digest size for hashType. + \param [in] msgSz Length of the digest in bytes. + \param [in] hashType Hash algorithm used for pre-hashing (selects OID). \param [out] sig Buffer to receive the signature. \param [in,out] sigSz On input, size of sig buffer. On output, actual signature length. @@ -491,18 +513,24 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, /*! \ingroup SLH_DSA - \brief Signs a pre-hashed message using the SLH-DSA external (HashSLH-DSA) - interface with RNG-provided randomness. + \brief Signs a caller-pre-hashed message digest using the SLH-DSA external + (HashSLH-DSA) interface with RNG-provided randomness. The caller must + hash the application message with hashType first and pass the digest as + msg; this function does NOT hash msg. \return 0 on success. \return BAD_FUNC_ARG if key, msg, sig, sigSz, or rng is NULL. + \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType + (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + \return NOT_COMPILED_IN if hashType is not supported in this build. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the message to hash and sign. - \param [in] msgSz Length of the message. - \param [in] hashType Hash algorithm to use for pre-hashing. + \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal + the digest size for hashType. + \param [in] msgSz Length of the digest in bytes. + \param [in] hashType Hash algorithm used for pre-hashing (selects OID). \param [out] sig Buffer to receive the signature. \param [in,out] sigSz On input, size of sig buffer. On output, actual signature length. @@ -518,21 +546,28 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, /*! \ingroup SLH_DSA - \brief Verifies an SLH-DSA signature over a pre-hashed message - (HashSLH-DSA). The message is hashed with the specified hash algorithm - before verification. + \brief Verifies an SLH-DSA signature using the external HashSLH-DSA + interface (FIPS 205 Algorithm 24). The caller must hash the application + message with hashType first and pass the digest as msg; this function + does NOT hash msg. \return 0 on success (signature valid). \return BAD_FUNC_ARG if key, msg, or sig is NULL. + \return BAD_LENGTH_E if sigSz does not match the parameter set, or if + msgSz does not equal the digest size for hashType (32 for SHAKE128, 64 + for SHAKE256 per FIPS 205 §10.2.2). + \return NOT_COMPILED_IN if hashType is not supported in this build. + \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. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the message to hash and verify. - \param [in] msgSz Length of the message. - \param [in] hashType Hash algorithm used for pre-hashing. Must match the - hash used during signing. + \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal + the digest size for hashType. + \param [in] msgSz Length of the digest in bytes. + \param [in] hashType Hash algorithm used for pre-hashing (selects OID). + Must match the hash used during signing. \param [in] sig Pointer to the signature to verify. \param [in] sigSz Length of the signature. @@ -542,10 +577,12 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte sig[...]; word32 sigSz; byte msg[] = "Hello World!"; + byte digest[WC_SHA256_DIGEST_SIZE]; int ret; + wc_Sha256Hash(msg, sizeof(msg), digest); ret = wc_SlhDsaKey_VerifyHash(&key, NULL, 0, - msg, sizeof(msg), WC_HASH_TYPE_SHA256, sig, sigSz); + digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig, sigSz); if (ret == 0) { // signature is valid } diff --git a/tests/api/test_slhdsa.c b/tests/api/test_slhdsa.c index ca32d2cf32d..d3c035cbfaa 100644 --- a/tests/api/test_slhdsa.c +++ b/tests/api/test_slhdsa.c @@ -1042,24 +1042,45 @@ int test_wc_slhdsa_sign_hash(void) expSigLen = TEST_SLHDSA_DEFAULT_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0); - /* Test SignHash NULL parameter handling. */ + /* Test SignHash NULL parameter handling. Use 32-byte hash length so the + * NULL check trips before the digest-length check (HashSLH-DSA expects + * SHA-256 digest = 32 bytes). */ sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_SignHash(NULL, ctx, sizeof(ctx), hash, - sizeof(hash), WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), + 32, WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), NULL, - sizeof(hash), WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), + 32, WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, - sizeof(hash), WC_HASH_TYPE_SHA256, NULL, &sigLen, &rng), + 32, WC_HASH_TYPE_SHA256, NULL, &sigLen, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, - sizeof(hash), WC_HASH_TYPE_SHA256, sig, NULL, &rng), + 32, WC_HASH_TYPE_SHA256, sig, NULL, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, - sizeof(hash), WC_HASH_TYPE_SHA256, sig, &sigLen, NULL), + 32, WC_HASH_TYPE_SHA256, sig, &sigLen, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* HashSLH-DSA must reject digest lengths that don't match hashType. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 31, + WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 33, + WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); + /* Generate a real signature first so VerifyHash gets to its length check + * rather than failing on signature size. */ + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32, + WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), 0); + ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 31, + WC_HASH_TYPE_SHA256, sig, sigLen), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); + ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 33, + WC_HASH_TYPE_SHA256, sig, sigLen), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); + /* Test SignHash with SHA-256. */ sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32, @@ -1110,6 +1131,31 @@ int test_wc_slhdsa_sign_hash(void) WC_HASH_TYPE_SHA256, sig, sigLen), 0); } +#ifdef WOLFSSL_SHA512 + /* SHA-512 round-trip exercises a 64-byte digest path. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64, + WC_HASH_TYPE_SHA512, sig, &sigLen, &rng), 0); + ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 64, + WC_HASH_TYPE_SHA512, sig, sigLen), 0); + /* SHA-512 must also reject the wrong digest length. */ + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32, + WC_HASH_TYPE_SHA512, sig, &sigLen, &rng), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); +#endif + +#ifdef WOLFSSL_SHAKE256 + /* SHAKE256 PHM is fixed at 512 bits per FIPS 205 §10.2.2. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64, + 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); + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32, + WC_HASH_TYPE_SHAKE256, sig, &sigLen, &rng), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); +#endif + wc_SlhDsaKey_Free(&key); wc_FreeRng(&rng); @@ -1118,6 +1164,130 @@ int test_wc_slhdsa_sign_hash(void) return EXPECT_RESULT(); } +/* + * Test the FIPS 205 internal interface (M' supplied directly) for SLH-DSA. + * Covers wc_SlhDsaKey_SignMsgDeterministic, wc_SlhDsaKey_SignMsgWithRandom, + * and wc_SlhDsaKey_VerifyMsg, plus a HashSLH-DSA equivalence cross-check + * that proves an externally-built M' matches the SignHash/VerifyHash path. + */ +int test_wc_slhdsa_sign_msg(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \ + !defined(NO_SHA256) + SlhDsaKey key; + WC_RNG rng; + byte mprime[64]; + byte* sig = NULL; + word32 sigLen; + byte addRnd[WC_SLHDSA_MAX_SEED]; + + sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(sig); + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + XMEMSET(mprime, 0xAA, sizeof(mprime)); + XMEMSET(addRnd, 0x55, sizeof(addRnd)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + + ExpectIntEQ(wc_SlhDsaKey_Init(&key, TEST_SLHDSA_DEFAULT_PARAM, NULL, + INVALID_DEVID), 0); + ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0); + + /* SignMsgDeterministic NULL-arg checks. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(NULL, mprime, sizeof(mprime), + sig, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, NULL, sizeof(mprime), + sig, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, sizeof(mprime), + NULL, &sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, sizeof(mprime), + sig, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Round-trip: Deterministic. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, + sizeof(mprime), sig, &sigLen), 0); + ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), sig, + sigLen), 0); + + /* SignMsgWithRandom NULL-arg checks. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(NULL, mprime, sizeof(mprime), + sig, &sigLen, addRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime), + sig, &sigLen, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Round-trip: WithRandom. */ + ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime), + sig, &sigLen, addRnd), 0); + ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), sig, + sigLen), 0); + + /* VerifyMsg NULL-arg checks. */ + ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(NULL, mprime, sizeof(mprime), sig, + sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, NULL, sizeof(mprime), sig, + sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), NULL, + sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Equivalence cross-check: build M' = 0x01 || ctxSz || OID(SHA-256) || + * SHA256(orig) externally, sign it via SignMsgDeterministic, and verify + * via VerifyHash with the same SHA-256 digest. Both paths must agree. */ + { + static const byte sha256_oid[] = { + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01 + }; + static const byte orig[] = "Hello World!"; + byte digest[WC_SHA256_DIGEST_SIZE]; + byte built_mprime[2 + sizeof(sha256_oid) + WC_SHA256_DIGEST_SIZE]; + word32 idx = 0; + word32 sigLen2; + + ExpectIntEQ(wc_Sha256Hash(orig, (word32)sizeof(orig) - 1, digest), 0); + + built_mprime[idx++] = 0x01; /* HashSLH-DSA domain separator */ + built_mprime[idx++] = 0; /* ctxSz = 0 */ + XMEMCPY(built_mprime + idx, sha256_oid, sizeof(sha256_oid)); + idx += (word32)sizeof(sha256_oid); + XMEMCPY(built_mprime + idx, digest, sizeof(digest)); + idx += (word32)sizeof(digest); + + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, built_mprime, + idx, sig, &sigLen), 0); + + /* The same signature must verify via the HashSLH-DSA external API. */ + ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, NULL, 0, digest, + sizeof(digest), WC_HASH_TYPE_SHA256, sig, sigLen), 0); + + /* And the deterministic HashSLH-DSA path must produce the SAME + * signature bytes (this is the strongest interop check). */ + sigLen2 = WC_SLHDSA_MAX_SIG_LEN; + { + byte* sig2 = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(sig2); + ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(&key, NULL, 0, + digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig2, + &sigLen2), 0); + ExpectIntEQ(sigLen2, sigLen); + ExpectIntEQ(XMEMCMP(sig2, sig, sigLen), 0); + XFREE(sig2, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + } + + wc_SlhDsaKey_Free(&key); + wc_FreeRng(&rng); + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + /* * Test export and import for SLH-DSA keys. */ diff --git a/tests/api/test_slhdsa.h b/tests/api/test_slhdsa.h index 8eed0b8669d..40d09ed8cb8 100644 --- a/tests/api/test_slhdsa.h +++ b/tests/api/test_slhdsa.h @@ -31,6 +31,7 @@ int test_wc_slhdsa_sign(void); int test_wc_slhdsa_verify(void); int test_wc_slhdsa_sign_vfy(void); int test_wc_slhdsa_sign_hash(void); +int test_wc_slhdsa_sign_msg(void); int test_wc_slhdsa_export_import(void); int test_wc_slhdsa_check_key(void); int test_wc_slhdsa_der_roundtrip(void); @@ -48,6 +49,7 @@ int test_wc_slhdsa_decoder_disabled_oid(void); TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_verify), \ TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_sign_vfy), \ TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_sign_hash), \ + TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_sign_msg), \ TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_export_import), \ TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_check_key), \ TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_der_roundtrip), \ diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 7fc62536a90..507aeb012cd 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -12726,41 +12726,52 @@ void bench_slhdsa(int param) ); bench_stats_asym_finish(name, len, "vrfy-msg", 0, count, start, ret); - /* Pre-hash interface: hash message, then sign the hash. */ - PRIVATE_KEY_UNLOCK(); - bench_stats_start(&count, &start); - do { - sigLen = WC_SLHDSA_MAX_SIG_LEN; - ret = wc_SlhDsaKey_SignHashDeterministic(key, ctx, 0, msg, - (word32)sizeof(msg), WC_HASH_TYPE_SHA256, sig, &sigLen); + /* Pre-hash interface: hash message ONCE outside the timed loop (the + * bench measures sign/verify, not the application-side hash), then sign + * and verify the digest. */ + { + byte digest[WC_SHA256_DIGEST_SIZE]; + + ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest); if (ret != 0) { goto exit; } - count++; - RECORD_MULTI_VALUE_STATS(); - } while (bench_stats_check(start) + + PRIVATE_KEY_UNLOCK(); + bench_stats_start(&count, &start); + do { + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ret = wc_SlhDsaKey_SignHashDeterministic(key, ctx, 0, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, sig, &sigLen); + if (ret != 0) { + goto exit; + } + count++; + RECORD_MULTI_VALUE_STATS(); + } while (bench_stats_check(start) #ifdef MULTI_VALUE_STATISTICS - || runs < minimum_runs + || runs < minimum_runs #endif - ); - PRIVATE_KEY_LOCK(); - bench_stats_asym_finish(name, len, "sign-pre", 0, count, start, ret); + ); + PRIVATE_KEY_LOCK(); + bench_stats_asym_finish(name, len, "sign-pre", 0, count, start, ret); - bench_stats_start(&count, &start); - do { - ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, msg, - (word32)sizeof(msg), WC_HASH_TYPE_SHA256, sig, sigLen); - if (ret != 0) { - goto exit; - } - count++; - RECORD_MULTI_VALUE_STATS(); - } while (bench_stats_check(start) + bench_stats_start(&count, &start); + do { + ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHA256, sig, sigLen); + if (ret != 0) { + goto exit; + } + count++; + RECORD_MULTI_VALUE_STATS(); + } while (bench_stats_check(start) #ifdef MULTI_VALUE_STATISTICS - || runs < minimum_runs + || runs < minimum_runs #endif - ); - bench_stats_asym_finish(name, len, "vrfy-pre", 0, count, start, ret); + ); + bench_stats_asym_finish(name, len, "vrfy-pre", 0, count, start, ret); + } exit: #ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index cf9c4a2cbe3..be0fde755c6 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -7266,9 +7266,11 @@ int wc_SlhDsaKey_Sign(SlhDsaKey* key, const byte* ctx, byte ctxSz, return ret; } -/* Sign using internal interface -- M' provided directly (deterministic). +/* Sign using the FIPS 205 internal interface (Algorithm 19) -- M' provided + * directly by the caller, deterministic variant (opt_rand = PK.seed). * - * opt_rand = PK.seed for the deterministic variant. + * Used for HashSLH-DSA implementations that build M' externally and for ACVP + * signatureInterface=internal test vectors. * * @param [in] key SLH-DSA key. * @param [in] mprime M' message (already in internal format). @@ -7276,16 +7278,29 @@ int wc_SlhDsaKey_Sign(SlhDsaKey* key, const byte* ctx, byte ctxSz, * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, buffer length. On out, signature length. * @return 0 on success. + * @return BAD_FUNC_ARG when key, key's parameters, mprime, sig or sigSz is + * NULL. + * @return BAD_LENGTH_E when sigSz is less than required signature length. + * @return MISSING_KEY when private key not set. + * @return MEMORY_E on dynamic memory allocation failure. + * @return SHAKE-256 error return code on digest failure. */ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz) { - int ret; + int ret = 0; - if ((key == NULL) || (key->params == NULL)) { + if ((key == NULL) || (key->params == NULL) || (mprime == NULL) || + (sig == NULL) || (sigSz == NULL)) { ret = BAD_FUNC_ARG; } - else { + else if (*sigSz < key->params->sigLen) { + ret = BAD_LENGTH_E; + } + else if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) { + ret = MISSING_KEY; + } + if (ret == 0) { ret = slhdsakey_sign_internal_msg(key, mprime, mprimeSz, sig, sigSz, key->sk + 2 * key->params->n); } @@ -7293,7 +7308,11 @@ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, const byte* mprime, return ret; } -/* Sign using internal interface -- M' provided directly (with explicit random). +/* Sign using the FIPS 205 internal interface (Algorithm 19) -- M' provided + * directly by the caller, with explicit randomness. + * + * Used for HashSLH-DSA implementations that build M' externally and for ACVP + * signatureInterface=internal test vectors. * * @param [in] key SLH-DSA key. * @param [in] mprime M' message (already in internal format). @@ -7302,12 +7321,34 @@ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, const byte* mprime, * @param [in, out] sigSz On in, buffer length. On out, signature length. * @param [in] addRnd opt_rand value. * @return 0 on success. + * @return BAD_FUNC_ARG when key, key's parameters, mprime, sig, sigSz or + * addRnd is NULL. + * @return BAD_LENGTH_E when sigSz is less than required signature length. + * @return MISSING_KEY when private key not set. + * @return MEMORY_E on dynamic memory allocation failure. + * @return SHAKE-256 error return code on digest failure. */ int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key, const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz, const byte* addRnd) { - return slhdsakey_sign_internal_msg(key, mprime, mprimeSz, sig, sigSz, - addRnd); + int ret = 0; + + if ((key == NULL) || (key->params == NULL) || (mprime == NULL) || + (sig == NULL) || (sigSz == NULL) || (addRnd == NULL)) { + ret = BAD_FUNC_ARG; + } + else if (*sigSz < key->params->sigLen) { + ret = BAD_LENGTH_E; + } + else if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) { + ret = MISSING_KEY; + } + if (ret == 0) { + ret = slhdsakey_sign_internal_msg(key, mprime, mprimeSz, sig, sigSz, + addRnd); + } + + return ret; } #endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */ @@ -7613,70 +7654,75 @@ static const byte slhdsakey_oid_sha3_512[] = { #endif #endif -/* Pre-hash the message with the hash specified. - * - * @param [in] msg Message to hash. - * @param [in] msgSz Length of message in bytes. - * @param [in] hashType Hash algorithm. - * @param [out] ph Prehash buffer. - * @param [out] phLen Length of prehash data. +/* Validate and copy the caller-supplied pre-hashed message digest. + * + * The HashSLH-DSA family takes the digest as input rather than the full + * message. This mirrors the wc_dilithium_*_ctx_hash interface and matches the + * convention used by NIST ACVP signatureInterface=external / preHash test + * vectors and other libraries (OpenSSL HASH-ML-DSA, leancrypto SLH-DSA, + * mldsa-native pre_hash_internal). The expected digest length is fixed by + * FIPS 205 §10.2.2 and equals wc_HashGetDigestSize(hashType) for the + * fixed-output hashes; for SHAKE128/256 the standard fixes the XOF output to + * 256/512 bits respectively. + * + * @param [in] msg Caller-supplied pre-hashed message digest. + * @param [in] msgSz Length of the digest in bytes. + * @param [in] hashType Hash algorithm identifier (selects OID and length). + * @param [out] ph Prehash buffer (filled by copy from msg). + * @param [out] phLen Length of prehash data written. * @param [out] oid OID data for hash algorithm. * @param [out] oidLen Length of OID data for hash algorithm. * @return 0 on success. + * @return BAD_LENGTH_E when msgSz does not equal the expected digest size. * @return NOT_COMPILED_IN when hash algorithm not supported. */ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, enum wc_HashType hashType, byte* ph, byte* phLen, const byte** oid, byte* oidLen) { - int ret; + int ret = 0; + word32 expectedLen = 0; switch ((int)hashType) { #ifdef WOLFSSL_SHA224 case WC_HASH_TYPE_SHA224: *oid = slhdsakey_oid_sha224; *oidLen = (byte)sizeof(slhdsakey_oid_sha224); - *phLen = WC_SHA224_DIGEST_SIZE; - ret = wc_Sha224Hash(msg, msgSz, ph); + expectedLen = WC_SHA224_DIGEST_SIZE; break; #endif #ifndef NO_SHA256 case WC_HASH_TYPE_SHA256: *oid = slhdsakey_oid_sha256; *oidLen = (byte)sizeof(slhdsakey_oid_sha256); - *phLen = WC_SHA256_DIGEST_SIZE; - ret = wc_Sha256Hash(msg, msgSz, ph); + expectedLen = WC_SHA256_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA384 case WC_HASH_TYPE_SHA384: *oid = slhdsakey_oid_sha384; *oidLen = (byte)sizeof(slhdsakey_oid_sha384); - *phLen = WC_SHA384_DIGEST_SIZE; - ret = wc_Sha384Hash(msg, msgSz, ph); + expectedLen = WC_SHA384_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA512 case WC_HASH_TYPE_SHA512: *oid = slhdsakey_oid_sha512; *oidLen = (byte)sizeof(slhdsakey_oid_sha512); - *phLen = WC_SHA512_DIGEST_SIZE; - ret = wc_Sha512Hash(msg, msgSz, ph); + expectedLen = WC_SHA512_DIGEST_SIZE; break; #ifndef WOLFSSL_NOSHA512_224 case WC_HASH_TYPE_SHA512_224: *oid = slhdsakey_oid_sha512_224; *oidLen = (byte)sizeof(slhdsakey_oid_sha512_224); - *phLen = WC_SHA512_224_DIGEST_SIZE; - ret = wc_Sha512_224Hash(msg, msgSz, ph); + expectedLen = WC_SHA512_224_DIGEST_SIZE; break; #endif #ifndef WOLFSSL_NOSHA512_256 case WC_HASH_TYPE_SHA512_256: *oid = slhdsakey_oid_sha512_256; *oidLen = (byte)sizeof(slhdsakey_oid_sha512_256); - *phLen = WC_SHA512_256_DIGEST_SIZE; - ret = wc_Sha512_256Hash(msg, msgSz, ph); + expectedLen = WC_SHA512_256_DIGEST_SIZE; break; #endif #endif @@ -7684,16 +7730,16 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, case WC_HASH_TYPE_SHAKE128: *oid = slhdsakey_oid_shake128; *oidLen = (byte)sizeof(slhdsakey_oid_shake128); - *phLen = WC_SHA3_256_DIGEST_SIZE; - ret = wc_Shake128Hash(msg, msgSz, ph, WC_SHA3_256_DIGEST_SIZE); + /* FIPS 205 §10.2.2 fixes SHAKE128 PHM length at 256 bits. */ + expectedLen = WC_SHA3_256_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHAKE256 case WC_HASH_TYPE_SHAKE256: *oid = slhdsakey_oid_shake256; *oidLen = (byte)sizeof(slhdsakey_oid_shake256); - *phLen = WC_SHA3_512_DIGEST_SIZE; - ret = wc_Shake256Hash(msg, msgSz, ph, WC_SHA3_512_DIGEST_SIZE); + /* FIPS 205 §10.2.2 fixes SHAKE256 PHM length at 512 bits. */ + expectedLen = WC_SHA3_512_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA3 @@ -7701,32 +7747,28 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, case WC_HASH_TYPE_SHA3_224: *oid = slhdsakey_oid_sha3_224; *oidLen = (byte)sizeof(slhdsakey_oid_sha3_224); - *phLen = WC_SHA3_224_DIGEST_SIZE; - ret = wc_Sha3_224Hash(msg, msgSz, ph); + expectedLen = WC_SHA3_224_DIGEST_SIZE; break; #endif #ifndef WOLFSSL_NOSHA3_256 case WC_HASH_TYPE_SHA3_256: *oid = slhdsakey_oid_sha3_256; *oidLen = (byte)sizeof(slhdsakey_oid_sha3_256); - *phLen = WC_SHA3_256_DIGEST_SIZE; - ret = wc_Sha3_256Hash(msg, msgSz, ph); + expectedLen = WC_SHA3_256_DIGEST_SIZE; break; #endif #ifndef WOLFSSL_NOSHA3_384 case WC_HASH_TYPE_SHA3_384: *oid = slhdsakey_oid_sha3_384; *oidLen = (byte)sizeof(slhdsakey_oid_sha3_384); - *phLen = WC_SHA3_384_DIGEST_SIZE; - ret = wc_Sha3_384Hash(msg, msgSz, ph); + expectedLen = WC_SHA3_384_DIGEST_SIZE; break; #endif #ifndef WOLFSSL_NOSHA3_512 case WC_HASH_TYPE_SHA3_512: *oid = slhdsakey_oid_sha3_512; *oidLen = (byte)sizeof(slhdsakey_oid_sha3_512); - *phLen = WC_SHA3_512_DIGEST_SIZE; - ret = wc_Sha3_512Hash(msg, msgSz, ph); + expectedLen = WC_SHA3_512_DIGEST_SIZE; break; #endif #endif @@ -7735,6 +7777,16 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, break; } + if (ret == 0) { + if (msgSz != expectedLen) { + ret = BAD_LENGTH_E; + } + else { + XMEMCPY(ph, msg, msgSz); + *phLen = (byte)msgSz; + } + } + return ret; } @@ -7790,12 +7842,16 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, * * Note: ctx length is of type byte which means it can never be more than 255. * + * The caller MUST pre-hash the application message with hashType before + * calling and pass the digest as msg. msgSz must equal the digest size of + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Message to sign. - * @param [in] msgSz Length of message in bytes. - * @param [in] hashType Hash algorithm to use in pre-hash. + * @param [in] msg Pre-hashed message digest to sign. + * @param [in] msgSz Length of digest in bytes. + * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. @@ -7803,7 +7859,8 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, * @return BAD_FUNC_ARG when key, key's parameters, msg, sig, sigSz or addRnd * is NULL. * @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. + * @return BAD_LENGTH_E when sigSz is less than required signature length, or + * when msgSz does not equal the digest size for hashType. * @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. @@ -7834,8 +7891,8 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, ret = BAD_FUNC_ARG; } if (ret == 0) { - /* Alg 23, Steps 8-23: Pre-hash message with hash algorithm specified. - */ + /* Alg 23, Steps 8-23: Validate caller-supplied pre-hashed digest and + * select OID for the chosen hash algorithm. */ ret = slhdsakey_prehash_msg(msg, msgSz, hashType, ph, &phLen, &oid, &oidLen); } @@ -7931,23 +7988,27 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, return ret; } -/* Generate a deterministic pre-hash SLH-DSA signature. +/* Generate a deterministic HashSLH-DSA signature. * - * addrnd is the public key seed. + * addrnd is the public key seed. The caller MUST pre-hash the application + * message with hashType before calling and pass the digest as msg; msgSz must + * equal the digest size of hashType (32 for SHAKE128, 64 for SHAKE256 per + * FIPS 205 §10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Message to sign. - * @param [in] msgSz Length of message in bytes. - * @param [in] hashType Hash algorithm to use in pre-hash. + * @param [in] msg Pre-hashed message digest to sign. + * @param [in] msgSz Length of digest in bytes. + * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. * @return 0 on success. * @return BAD_FUNC_ARG when key, key's parameters, msg, sig or sigSz is NULL. * @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. + * @return BAD_LENGTH_E when sigSz is less than required signature length, or + * when msgSz does not equal the digest size for hashType. * @return MISSING_KEY when private key not set. * @return MEMORY_E on dynamic memory allocation failure. * @return SHAKE-256 error return code on digest failure. @@ -7975,14 +8036,18 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, return ret; } -/* Generate a pre-hash SLH-DSA signature. +/* Generate a HashSLH-DSA signature with explicit randomness. + * + * The caller MUST pre-hash the application message with hashType before + * calling and pass the digest as msg; msgSz must equal the digest size of + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Message to sign. - * @param [in] msgSz Length of message in bytes. - * @param [in] hashType Hash algorithm to use in pre-hash. + * @param [in] msg Pre-hashed message digest to sign. + * @param [in] msgSz Length of digest in bytes. + * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. @@ -7991,7 +8056,8 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, * @return BAD_FUNC_ARG when key, key's parameters, msg, sig, sigSz or addrnd * is NULL. * @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. + * @return BAD_LENGTH_E when sigSz is less than required signature length, or + * when msgSz does not equal the digest size for hashType. * @return MISSING_KEY when private key not set. * @return NOT_COMPILED in when hash algorithm is not supported. * @return MEMORY_E on dynamic memory allocation failure. @@ -8006,14 +8072,18 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, sig, sigSz, addRnd); } -/* Generate a pre-hash SLH-DSA signature with a random number generator. +/* Generate a HashSLH-DSA signature using an RNG for added randomness. + * + * The caller MUST pre-hash the application message with hashType before + * calling and pass the digest as msg; msgSz must equal the digest size of + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Message to sign. - * @param [in] msgSz Length of message in bytes. - * @param [in] hashType Hash algorithm to use in pre-hash. + * @param [in] msg Pre-hashed message digest to sign. + * @param [in] msgSz Length of digest in bytes. + * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. @@ -8022,6 +8092,8 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, * @return BAD_FUNC_ARG when key, key's parameters, msg, sig, sigSz or rng is * NULL. * @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0. + * @return BAD_LENGTH_E when msgSz does not equal the digest size for + * hashType. * @return MISSING_KEY when private key not set. * @return NOT_COMPILED in when hash algorithm is not supported. * @return MEMORY_E on dynamic memory allocation failure. @@ -8106,18 +8178,23 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, * 20: M' <- toByte(1, 1) || toByte(|ctx|, 1) || ctx || OID || PHM * 21: return slh_verify_internal(M', SIG, PK) * + * The caller MUST pre-hash the application message with hashType before + * calling and pass the digest as msg; msgSz must equal the digest size of + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Message to sign. - * @param [in] msgSz Length of message in bytes. - * @param [in] hashType Hash algorithm to use in pre-hash. + * @param [in] msg Pre-hashed message digest to verify against. + * @param [in] msgSz Length of digest in bytes. + * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [in] sig Signature data. * @param [in] sigSz Length of signature in bytes. * @return 0 on success. * @return BAD_FUNC_ARG when key, key's parameters, msg or sig is NULL. * @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0. - * @return BAD_LENGTH_E when signature size does not match parameters. + * @return BAD_LENGTH_E when signature size does not match parameters, or + * when msgSz does not equal the digest size for hashType. * @return MISSING_KEY when public key not set. * @return NOT_COMPILED in when hash algorithm is not supported. * @return MEMORY_E on dynamic memory allocation failure. @@ -8148,8 +8225,8 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, ret = MISSING_KEY; } if (ret == 0) { - /* Alg 24, Steps 4-19: Pre-hash message with hash algorithm specified. - */ + /* Alg 24, Steps 4-19: Validate caller-supplied pre-hashed digest and + * select OID for the chosen hash algorithm. */ ret = slhdsakey_prehash_msg(msg, msgSz, hashType, ph, &phLen, &oid, &oidLen); } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5b0834019e0..99fd89f6623 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -54407,6 +54407,7 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } + /* HashSLH-DSA takes the caller's pre-hashed digest as input. */ { #ifdef WOLFSSL_SLHDSA_SHA2 enum wc_HashType phType = SLHDSA_IS_SHA2(param) ? @@ -54414,15 +54415,33 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param) #else enum wc_HashType phType = WC_HASH_TYPE_SHAKE256; #endif + byte digest[WC_SHA3_512_DIGEST_SIZE]; + word32 digestLen; + +#ifdef WOLFSSL_SLHDSA_SHA2 + if (phType == WC_HASH_TYPE_SHA256) { + ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest); + digestLen = WC_SHA256_DIGEST_SIZE; + } + else +#endif + { + ret = wc_Shake256Hash(msg, (word32)sizeof(msg), digest, + WC_SHA3_512_DIGEST_SIZE); + digestLen = WC_SHA3_512_DIGEST_SIZE; + } + if (ret != 0) { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } sigLen = WC_SLHDSA_MAX_SIG_LEN; PRIVATE_KEY_UNLOCK(); - ret = wc_SlhDsaKey_SignHash(key, ctx, 0, msg, (word32)sizeof(msg), + ret = wc_SlhDsaKey_SignHash(key, ctx, 0, digest, digestLen, phType, sig, &sigLen, &rng); PRIVATE_KEY_LOCK(); if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } - ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, msg, (word32)sizeof(msg), + ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, digest, digestLen, phType, sig, sigLen); } if (ret != 0) { @@ -54431,18 +54450,26 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param) /* Additional pre-hash test: SHA-384 exercises a different OID path */ #ifdef WOLFSSL_SHA384 - sigLen = WC_SLHDSA_MAX_SIG_LEN; - PRIVATE_KEY_UNLOCK(); - ret = wc_SlhDsaKey_SignHash(key, ctx, 0, msg, (word32)sizeof(msg), - WC_HASH_TYPE_SHA384, sig, &sigLen, &rng); - PRIVATE_KEY_LOCK(); - if (ret != 0) { - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - } - ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, msg, (word32)sizeof(msg), - WC_HASH_TYPE_SHA384, sig, sigLen); - if (ret != 0) { - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + { + byte digest384[WC_SHA384_DIGEST_SIZE]; + + ret = wc_Sha384Hash(msg, (word32)sizeof(msg), digest384); + if (ret != 0) { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } + sigLen = WC_SLHDSA_MAX_SIG_LEN; + PRIVATE_KEY_UNLOCK(); + ret = wc_SlhDsaKey_SignHash(key, ctx, 0, digest384, sizeof(digest384), + WC_HASH_TYPE_SHA384, sig, &sigLen, &rng); + PRIVATE_KEY_LOCK(); + if (ret != 0) { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } + ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, digest384, + sizeof(digest384), WC_HASH_TYPE_SHA384, sig, sigLen); + if (ret != 0) { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } } #endif diff --git a/wolfssl/wolfcrypt/wc_slhdsa.h b/wolfssl/wolfcrypt/wc_slhdsa.h index 15e32368b09..33bb7354128 100644 --- a/wolfssl/wolfcrypt/wc_slhdsa.h +++ b/wolfssl/wolfcrypt/wc_slhdsa.h @@ -666,7 +666,10 @@ WOLFSSL_API int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, const byte* sig, word32 sigSz); #ifndef WOLFSSL_SLHDSA_VERIFY_ONLY -/* Internal interface: M' provided directly (no M' construction). */ +/* External-M' / FIPS 205 internal interface (Algorithms 19/20): the caller + * supplies the fully-built M'. Use these for HashSLH-DSA implementations that + * construct M' externally, distributed signers that operate on a pre-built + * message representative, and ACVP signatureInterface=internal test groups. */ WOLFSSL_API int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz); WOLFSSL_API int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key, From 763a8d6aecff7cebe56e5c8ff5fef6e42af7c143 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 10 May 2026 14:08:37 +0000 Subject: [PATCH 2/4] SLH-DSA: address review feedback on prior HashSLH-DSA API alignment - benchmark.c: gate the new prehash bench block on NO_SHA256 with a SHAKE256 fallback so SHAKE-only SLH-DSA builds don't add a hard SHA-256 link-time dependency they didn't have before. - ChangeLog.md: narrow the "stricter input validation" claim to SignMsg{Deterministic,WithRandom}; VerifyMsg already had full validation and was not changed. - wc_slhdsa.c: rename the now-misnamed slhdsakey_prehash_msg helper to slhdsakey_validate_prehash to reflect that it validates and copies rather than hashing. - Replace UTF-8 paragraph signs with ASCII "Section" in comments to match the surrounding code's style and avoid encoding pitfalls in some toolchains. - doc/dox_comments: add \sa cross-references between the *Hash* and *Msg* doxygen entries so users discovering one path can find the other. - tests/api/test_slhdsa.c: add NOT_COMPILED_IN coverage for an unsupported hashType, BAD_LENGTH_E coverage for the new SignMsg{Deterministic,WithRandom} sigSz check, and reset sigLen explicitly before SignMsgWithRandom in the round-trip rather than silently relying on the prior call to have set it. https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd --- ChangeLog.md | 7 +-- doc/dox_comments/header_files/wc_slhdsa.h | 15 +++++-- tests/api/test_slhdsa.c | 23 +++++++++- wolfcrypt/benchmark/benchmark.c | 52 ++++++++++++++++++++++- wolfcrypt/src/wc_slhdsa.c | 22 +++++----- 5 files changed, 98 insertions(+), 21 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 1fc43404ea8..888e2d8dce8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,9 +12,10 @@ `wc_HashGetDigestSize(hashType)` (32 bytes for SHAKE128, 64 bytes for SHAKE256 per FIPS 205 §10.2.2); otherwise `BAD_LENGTH_E` is returned. Migration: hash the message yourself before the call. The pre-existing - `wc_SlhDsaKey_SignMsgDeterministic`, `wc_SlhDsaKey_SignMsgWithRandom`, and - `wc_SlhDsaKey_VerifyMsg` (FIPS 205 internal interface, M' supplied directly) - are unaffected and gain stricter input validation and doxygen coverage. + `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. * TLS 1.3: zero traffic key staging buffers in `SetKeysSide()` once a CryptoCB callback has imported the AES key into a Secure Element diff --git a/doc/dox_comments/header_files/wc_slhdsa.h b/doc/dox_comments/header_files/wc_slhdsa.h index 81b4eca9c6b..d3d531ca6c0 100644 --- a/doc/dox_comments/header_files/wc_slhdsa.h +++ b/doc/dox_comments/header_files/wc_slhdsa.h @@ -338,6 +338,7 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx, \sa wc_SlhDsaKey_SignMsgWithRandom \sa wc_SlhDsaKey_VerifyMsg \sa wc_SlhDsaKey_SignDeterministic + \sa wc_SlhDsaKey_SignHashDeterministic */ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz); @@ -381,6 +382,7 @@ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key, \sa wc_SlhDsaKey_SignMsgDeterministic \sa wc_SlhDsaKey_VerifyMsg + \sa wc_SlhDsaKey_SignHashWithRandom */ int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key, const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz, @@ -420,6 +422,7 @@ int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key, \sa wc_SlhDsaKey_SignMsgDeterministic \sa wc_SlhDsaKey_Verify + \sa wc_SlhDsaKey_VerifyHash */ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, word32 mprimeSz, const byte* sig, word32 sigSz); @@ -436,7 +439,7 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, \return 0 on success. \return BAD_FUNC_ARG if key, msg, sig, or sigSz is NULL. \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType - (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \return MISSING_KEY if the private key has not been set. @@ -472,6 +475,7 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, \sa wc_SlhDsaKey_SignHashWithRandom \sa wc_SlhDsaKey_SignHash \sa wc_SlhDsaKey_VerifyHash + \sa wc_SlhDsaKey_SignMsgDeterministic */ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, @@ -488,7 +492,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, \return 0 on success. \return BAD_FUNC_ARG if key, msg, sig, sigSz, or addRnd is NULL. \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType - (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \param [in] key Pointer to a private SlhDsaKey. @@ -505,6 +509,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, \sa wc_SlhDsaKey_SignHashDeterministic \sa wc_SlhDsaKey_VerifyHash + \sa wc_SlhDsaKey_SignMsgWithRandom */ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, @@ -521,7 +526,7 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, \return 0 on success. \return BAD_FUNC_ARG if key, msg, sig, sigSz, or rng is NULL. \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType - (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \param [in] key Pointer to a private SlhDsaKey. @@ -538,6 +543,7 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, \sa wc_SlhDsaKey_SignHashDeterministic \sa wc_SlhDsaKey_VerifyHash + \sa wc_SlhDsaKey_SignMsgDeterministic */ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, @@ -555,7 +561,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, \return BAD_FUNC_ARG if key, msg, or sig is NULL. \return BAD_LENGTH_E if sigSz does not match the parameter set, or if msgSz does not equal the digest size for hashType (32 for SHAKE128, 64 - for SHAKE256 per FIPS 205 §10.2.2). + for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \return MISSING_KEY if the public key has not been set. \return SIG_VERIFY_E if the signature is invalid. @@ -590,6 +596,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, \sa wc_SlhDsaKey_SignHashDeterministic \sa wc_SlhDsaKey_Verify + \sa wc_SlhDsaKey_VerifyMsg */ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, diff --git a/tests/api/test_slhdsa.c b/tests/api/test_slhdsa.c index d3c035cbfaa..a3f0abf40f7 100644 --- a/tests/api/test_slhdsa.c +++ b/tests/api/test_slhdsa.c @@ -1081,6 +1081,13 @@ int test_wc_slhdsa_sign_hash(void) WC_HASH_TYPE_SHA256, sig, sigLen), WC_NO_ERR_TRACE(BAD_LENGTH_E)); + /* Unsupported hashType (FIPS 205 doesn't list WC_HASH_TYPE_NONE) hits + * the default branch of slhdsakey_validate_prehash. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32, + WC_HASH_TYPE_NONE, sig, &sigLen, &rng), + WC_NO_ERR_TRACE(NOT_COMPILED_IN)); + /* Test SignHash with SHA-256. */ sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32, @@ -1145,7 +1152,7 @@ int test_wc_slhdsa_sign_hash(void) #endif #ifdef WOLFSSL_SHAKE256 - /* SHAKE256 PHM is fixed at 512 bits per FIPS 205 §10.2.2. */ + /* SHAKE256 PHM is fixed at 512 bits per FIPS 205 Section 10.2.2. */ sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 64, WC_HASH_TYPE_SHAKE256, sig, &sigLen, &rng), 0); @@ -1206,6 +1213,11 @@ int test_wc_slhdsa_sign_msg(void) ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, sizeof(mprime), sig, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* SignMsgDeterministic must reject sigSz smaller than params->sigLen. */ + sigLen = 1; + ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, + sizeof(mprime), sig, &sigLen), WC_NO_ERR_TRACE(BAD_LENGTH_E)); + /* Round-trip: Deterministic. */ sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_SignMsgDeterministic(&key, mprime, @@ -1220,7 +1232,14 @@ int test_wc_slhdsa_sign_msg(void) ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime), sig, &sigLen, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - /* Round-trip: WithRandom. */ + /* SignMsgWithRandom must reject sigSz smaller than params->sigLen. */ + sigLen = 1; + ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime), + sig, &sigLen, addRnd), WC_NO_ERR_TRACE(BAD_LENGTH_E)); + + /* Round-trip: WithRandom. Reset sigLen explicitly so the test doesn't + * silently rely on the previous call having set it to params->sigLen. */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_SignMsgWithRandom(&key, mprime, sizeof(mprime), sig, &sigLen, addRnd), 0); ExpectIntEQ(wc_SlhDsaKey_VerifyMsg(&key, mprime, sizeof(mprime), sig, diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 507aeb012cd..df3e13b358a 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -12726,9 +12726,12 @@ void bench_slhdsa(int param) ); bench_stats_asym_finish(name, len, "vrfy-msg", 0, count, start, ret); +#ifndef NO_SHA256 /* Pre-hash interface: hash message ONCE outside the timed loop (the * bench measures sign/verify, not the application-side hash), then sign - * and verify the digest. */ + * and verify the digest. SHA-256 path: only built when SHA-256 is + * available; HashSLH-DSA still works at runtime with any hashType the + * build supports, but the bench needs a compile-time choice. */ { byte digest[WC_SHA256_DIGEST_SIZE]; @@ -12772,6 +12775,53 @@ void bench_slhdsa(int param) ); bench_stats_asym_finish(name, len, "vrfy-pre", 0, count, start, ret); } +#elif defined(WOLFSSL_SHAKE256) + /* SHAKE-only build (NO_SHA256): use SHAKE256 prehash bench instead. */ + { + byte digest[WC_SHA3_512_DIGEST_SIZE]; + + ret = wc_Shake256Hash(msg, (word32)sizeof(msg), digest, + WC_SHA3_512_DIGEST_SIZE); + if (ret != 0) { + goto exit; + } + + PRIVATE_KEY_UNLOCK(); + bench_stats_start(&count, &start); + do { + sigLen = WC_SLHDSA_MAX_SIG_LEN; + ret = wc_SlhDsaKey_SignHashDeterministic(key, ctx, 0, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHAKE256, sig, &sigLen); + if (ret != 0) { + goto exit; + } + count++; + RECORD_MULTI_VALUE_STATS(); + } while (bench_stats_check(start) +#ifdef MULTI_VALUE_STATISTICS + || runs < minimum_runs +#endif + ); + PRIVATE_KEY_LOCK(); + bench_stats_asym_finish(name, len, "sign-pre", 0, count, start, ret); + + bench_stats_start(&count, &start); + do { + ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, digest, + (word32)sizeof(digest), WC_HASH_TYPE_SHAKE256, sig, sigLen); + if (ret != 0) { + goto exit; + } + count++; + RECORD_MULTI_VALUE_STATS(); + } while (bench_stats_check(start) +#ifdef MULTI_VALUE_STATISTICS + || runs < minimum_runs +#endif + ); + bench_stats_asym_finish(name, len, "vrfy-pre", 0, count, start, ret); + } +#endif /* NO_SHA256 / WOLFSSL_SHAKE256 */ exit: #ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index be0fde755c6..a964c0a0b36 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -7661,7 +7661,7 @@ static const byte slhdsakey_oid_sha3_512[] = { * convention used by NIST ACVP signatureInterface=external / preHash test * vectors and other libraries (OpenSSL HASH-ML-DSA, leancrypto SLH-DSA, * mldsa-native pre_hash_internal). The expected digest length is fixed by - * FIPS 205 §10.2.2 and equals wc_HashGetDigestSize(hashType) for the + * FIPS 205 Section 10.2.2 and equals wc_HashGetDigestSize(hashType) for the * fixed-output hashes; for SHAKE128/256 the standard fixes the XOF output to * 256/512 bits respectively. * @@ -7676,7 +7676,7 @@ static const byte slhdsakey_oid_sha3_512[] = { * @return BAD_LENGTH_E when msgSz does not equal the expected digest size. * @return NOT_COMPILED_IN when hash algorithm not supported. */ -static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, +static int slhdsakey_validate_prehash(const byte* msg, word32 msgSz, enum wc_HashType hashType, byte* ph, byte* phLen, const byte** oid, byte* oidLen) { @@ -7730,7 +7730,7 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, case WC_HASH_TYPE_SHAKE128: *oid = slhdsakey_oid_shake128; *oidLen = (byte)sizeof(slhdsakey_oid_shake128); - /* FIPS 205 §10.2.2 fixes SHAKE128 PHM length at 256 bits. */ + /* FIPS 205 Section 10.2.2 fixes SHAKE128 PHM length at 256 bits. */ expectedLen = WC_SHA3_256_DIGEST_SIZE; break; #endif @@ -7738,7 +7738,7 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, case WC_HASH_TYPE_SHAKE256: *oid = slhdsakey_oid_shake256; *oidLen = (byte)sizeof(slhdsakey_oid_shake256); - /* FIPS 205 §10.2.2 fixes SHAKE256 PHM length at 512 bits. */ + /* FIPS 205 Section 10.2.2 fixes SHAKE256 PHM length at 512 bits. */ expectedLen = WC_SHA3_512_DIGEST_SIZE; break; #endif @@ -7844,7 +7844,7 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz, * * The caller MUST pre-hash the application message with hashType before * calling and pass the digest as msg. msgSz must equal the digest size of - * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. @@ -7893,7 +7893,7 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, if (ret == 0) { /* Alg 23, Steps 8-23: Validate caller-supplied pre-hashed digest and * select OID for the chosen hash algorithm. */ - ret = slhdsakey_prehash_msg(msg, msgSz, hashType, ph, &phLen, &oid, + ret = slhdsakey_validate_prehash(msg, msgSz, hashType, ph, &phLen, &oid, &oidLen); } if (ret == 0) { @@ -7993,7 +7993,7 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, * addrnd is the public key seed. The caller MUST pre-hash the application * message with hashType before calling and pass the digest as msg; msgSz must * equal the digest size of hashType (32 for SHAKE128, 64 for SHAKE256 per - * FIPS 205 §10.2.2). + * FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. @@ -8040,7 +8040,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, * * The caller MUST pre-hash the application message with hashType before * calling and pass the digest as msg; msgSz must equal the digest size of - * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. @@ -8076,7 +8076,7 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, * * The caller MUST pre-hash the application message with hashType before * calling and pass the digest as msg; msgSz must equal the digest size of - * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. @@ -8180,7 +8180,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, * * The caller MUST pre-hash the application message with hashType before * calling and pass the digest as msg; msgSz must equal the digest size of - * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2). + * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. @@ -8227,7 +8227,7 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, if (ret == 0) { /* Alg 24, Steps 4-19: Validate caller-supplied pre-hashed digest and * select OID for the chosen hash algorithm. */ - ret = slhdsakey_prehash_msg(msg, msgSz, hashType, ph, &phLen, &oid, + ret = slhdsakey_validate_prehash(msg, msgSz, hashType, ph, &phLen, &oid, &oidLen); } if (ret == 0) { From 281d374bc45c7f9d1a5781de509c824d05211980 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 10 May 2026 15:58:37 +0000 Subject: [PATCH 3/4] SLH-DSA: rename Hash-API msg/msgSz to hash/hashSz; drop redundant ph buffer Address PR review feedback: - Rename the public *Hash* API parameters from msg/msgSz to hash/hashSz to match the ML-DSA wc_dilithium_sign_ctx_hash / wc_dilithium_verify_ctx_hash parameter naming exactly. Affects wc_SlhDsaKey_SignHash, wc_SlhDsaKey_SignHashDeterministic, wc_SlhDsaKey_SignHashWithRandom, and wc_SlhDsaKey_VerifyHash. Callers using positional arguments are source-compatible. - Refactor slhdsakey_validate_prehash to drop the now-redundant ph output buffer. Previously the helper validated msgSz against the expected digest size and copied the input into a separate ph buffer; the callers then concatenated oid || ph or streamed them. Since ph was just a copy of the input, the helper now returns only oid/oidLen plus the validation error code, and the callers feed the caller-supplied digest directly into the M' construction. Eliminates the misleading ph[WC_MAX_DIGEST_SIZE] locals in slhdsakey_signhash_external and wc_SlhDsaKey_VerifyHash and the pointless XMEMCPY hop. - Update doxygen (source and dox_comments) accordingly. - ChangeLog: clarify the rename and that callers using positional arguments are source-compatible. https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd --- ChangeLog.md | 26 ++-- doc/dox_comments/header_files/wc_slhdsa.h | 58 ++++----- wolfcrypt/src/wc_slhdsa.c | 142 ++++++++++------------ wolfssl/wolfcrypt/wc_slhdsa.h | 8 +- 4 files changed, 111 insertions(+), 123 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 888e2d8dce8..a8dd6f5b756 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,18 +4,20 @@ * **BREAKING (FIPS 205 SLH-DSA)**: `wc_SlhDsaKey_SignHash`, `wc_SlhDsaKey_SignHashDeterministic`, `wc_SlhDsaKey_SignHashWithRandom`, and - `wc_SlhDsaKey_VerifyHash` now treat `msg`/`msgSz` as the **caller-pre-hashed - message digest**, matching `wc_dilithium_sign_ctx_hash` / - `wc_dilithium_verify_ctx_hash` and aligning with OpenSSL's HASH-ML-DSA, - mldsa-native, leancrypto SLH-DSA, and NIST ACVP `signatureInterface=external` - / `preHash=preHash` test vectors. `msgSz` must equal - `wc_HashGetDigestSize(hashType)` (32 bytes for SHAKE128, 64 bytes for - SHAKE256 per FIPS 205 §10.2.2); otherwise `BAD_LENGTH_E` is returned. - Migration: hash the message yourself before the call. 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. + `wc_SlhDsaKey_VerifyHash` now take the **caller-pre-hashed message digest** + via `hash`/`hashSz` parameters (renamed from `msg`/`msgSz`), matching + `wc_dilithium_sign_ctx_hash` / `wc_dilithium_verify_ctx_hash` exactly and + aligning with OpenSSL's HASH-ML-DSA, mldsa-native, leancrypto SLH-DSA, and + NIST ACVP `signatureInterface=external` / `preHash=preHash` test vectors. + `hashSz` must equal `wc_HashGetDigestSize(hashType)` (32 bytes 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. * TLS 1.3: zero traffic key staging buffers in `SetKeysSide()` once a CryptoCB callback has imported the AES key into a Secure Element diff --git a/doc/dox_comments/header_files/wc_slhdsa.h b/doc/dox_comments/header_files/wc_slhdsa.h index d3d531ca6c0..dec6bae7908 100644 --- a/doc/dox_comments/header_files/wc_slhdsa.h +++ b/doc/dox_comments/header_files/wc_slhdsa.h @@ -434,11 +434,11 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, (HashSLH-DSA) interface with deterministic randomness, per FIPS 205 Algorithm 23 with the pre-hash domain separator (0x01). The caller must hash the application message with hashType first and pass the digest as - msg; this function does NOT hash msg. + hash; this function does NOT hash its input. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, or sigSz is NULL. - \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType + \return BAD_FUNC_ARG if key, hash, sig, or sigSz is NULL. + \return BAD_LENGTH_E if hashSz does not equal the digest size for hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \return MISSING_KEY if the private key has not been set. @@ -446,9 +446,9 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal - the digest size for hashType. - \param [in] msgSz Length of the digest in bytes. + \param [in] hash Pointer to the pre-hashed message digest. hashSz must + equal the digest size for hashType. + \param [in] hashSz Length of the digest in bytes. \param [in] hashType Hash algorithm used for pre-hashing (selects OID). Supported: WC_HASH_TYPE_SHA224, WC_HASH_TYPE_SHA256, WC_HASH_TYPE_SHA384, WC_HASH_TYPE_SHA512, WC_HASH_TYPE_SHA512_224, WC_HASH_TYPE_SHA512_256, @@ -478,7 +478,7 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, \sa wc_SlhDsaKey_SignMsgDeterministic */ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, - const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, + const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz); /*! @@ -487,20 +487,20 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, \brief Signs a caller-pre-hashed message digest using the SLH-DSA external (HashSLH-DSA) interface with caller-provided additional randomness. The caller must hash the application message with hashType first and pass the - digest as msg; this function does NOT hash msg. + digest as hash; this function does NOT hash its input. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, sigSz, or addRnd is NULL. - \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType + \return BAD_FUNC_ARG if key, hash, sig, sigSz, or addRnd is NULL. + \return BAD_LENGTH_E if hashSz does not equal the digest size for hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal - the digest size for hashType. - \param [in] msgSz Length of the digest in bytes. + \param [in] hash Pointer to the pre-hashed message digest. hashSz must + equal the digest size for hashType. + \param [in] hashSz Length of the digest in bytes. \param [in] hashType Hash algorithm used for pre-hashing (selects OID). \param [out] sig Buffer to receive the signature. \param [in,out] sigSz On input, size of sig buffer. On output, actual @@ -512,7 +512,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, \sa wc_SlhDsaKey_SignMsgWithRandom */ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, - const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, + const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd); /*! @@ -521,20 +521,20 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, \brief Signs a caller-pre-hashed message digest using the SLH-DSA external (HashSLH-DSA) interface with RNG-provided randomness. The caller must hash the application message with hashType first and pass the digest as - msg; this function does NOT hash msg. + hash; this function does NOT hash its input. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, sigSz, or rng is NULL. - \return BAD_LENGTH_E if msgSz does not equal the digest size for hashType + \return BAD_FUNC_ARG if key, hash, sig, sigSz, or rng is NULL. + \return BAD_LENGTH_E if hashSz does not equal the digest size for hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal - the digest size for hashType. - \param [in] msgSz Length of the digest in bytes. + \param [in] hash Pointer to the pre-hashed message digest. hashSz must + equal the digest size for hashType. + \param [in] hashSz Length of the digest in bytes. \param [in] hashType Hash algorithm used for pre-hashing (selects OID). \param [out] sig Buffer to receive the signature. \param [in,out] sigSz On input, size of sig buffer. On output, actual @@ -546,7 +546,7 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, \sa wc_SlhDsaKey_SignMsgDeterministic */ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, - byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, + byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, WC_RNG* rng); /*! @@ -554,13 +554,13 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, \brief Verifies an SLH-DSA signature using the external HashSLH-DSA interface (FIPS 205 Algorithm 24). The caller must hash the application - message with hashType first and pass the digest as msg; this function - does NOT hash msg. + message with hashType first and pass the digest as hash; this function + does NOT hash its input. \return 0 on success (signature valid). - \return BAD_FUNC_ARG if key, msg, or sig is NULL. + \return BAD_FUNC_ARG if key, hash, or sig is NULL. \return BAD_LENGTH_E if sigSz does not match the parameter set, or if - msgSz does not equal the digest size for hashType (32 for SHAKE128, 64 + hashSz does not equal the digest size for hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). \return NOT_COMPILED_IN if hashType is not supported in this build. \return MISSING_KEY if the public key has not been set. @@ -569,9 +569,9 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, \param [in] key Pointer to a public SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. \param [in] ctxSz Length of the context string (0-255). - \param [in] msg Pointer to the pre-hashed message digest. msgSz must equal - the digest size for hashType. - \param [in] msgSz Length of the digest in bytes. + \param [in] hash Pointer to the pre-hashed message digest. hashSz must + equal the digest size for hashType. + \param [in] hashSz Length of the digest in bytes. \param [in] hashType Hash algorithm used for pre-hashing (selects OID). Must match the hash used during signing. \param [in] sig Pointer to the signature to verify. @@ -599,7 +599,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, \sa wc_SlhDsaKey_VerifyMsg */ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, - byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, + byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, const byte* sig, word32 sigSz); /*! diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index a964c0a0b36..4fc39dba119 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -7654,7 +7654,8 @@ static const byte slhdsakey_oid_sha3_512[] = { #endif #endif -/* Validate and copy the caller-supplied pre-hashed message digest. +/* Validate the caller-supplied pre-hashed digest length and look up the + * corresponding OID for the chosen hash algorithm. * * The HashSLH-DSA family takes the digest as input rather than the full * message. This mirrors the wc_dilithium_*_ctx_hash interface and matches the @@ -7663,22 +7664,19 @@ static const byte slhdsakey_oid_sha3_512[] = { * mldsa-native pre_hash_internal). The expected digest length is fixed by * FIPS 205 Section 10.2.2 and equals wc_HashGetDigestSize(hashType) for the * fixed-output hashes; for SHAKE128/256 the standard fixes the XOF output to - * 256/512 bits respectively. + * 256/512 bits respectively. Callers feed the caller-supplied digest buffer + * directly into the M' construction -- there is no copy. * - * @param [in] msg Caller-supplied pre-hashed message digest. - * @param [in] msgSz Length of the digest in bytes. + * @param [in] hashSz Length of the caller-supplied digest in bytes. * @param [in] hashType Hash algorithm identifier (selects OID and length). - * @param [out] ph Prehash buffer (filled by copy from msg). - * @param [out] phLen Length of prehash data written. * @param [out] oid OID data for hash algorithm. * @param [out] oidLen Length of OID data for hash algorithm. * @return 0 on success. - * @return BAD_LENGTH_E when msgSz does not equal the expected digest size. + * @return BAD_LENGTH_E when hashSz does not equal the expected digest size. * @return NOT_COMPILED_IN when hash algorithm not supported. */ -static int slhdsakey_validate_prehash(const byte* msg, word32 msgSz, - enum wc_HashType hashType, byte* ph, byte* phLen, const byte** oid, - byte* oidLen) +static int slhdsakey_validate_prehash(word32 hashSz, + enum wc_HashType hashType, const byte** oid, byte* oidLen) { int ret = 0; word32 expectedLen = 0; @@ -7777,14 +7775,8 @@ static int slhdsakey_validate_prehash(const byte* msg, word32 msgSz, break; } - if (ret == 0) { - if (msgSz != expectedLen) { - ret = BAD_LENGTH_E; - } - else { - XMEMCPY(ph, msg, msgSz); - *phLen = (byte)msgSz; - } + if ((ret == 0) && (hashSz != expectedLen)) { + ret = BAD_LENGTH_E; } return ret; @@ -7843,41 +7835,39 @@ static int slhdsakey_validate_prehash(const byte* msg, word32 msgSz, * Note: ctx length is of type byte which means it can never be more than 255. * * The caller MUST pre-hash the application message with hashType before - * calling and pass the digest as msg. msgSz must equal the digest size of + * calling and pass the digest as hash. hashSz must equal the digest size of * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Pre-hashed message digest to sign. - * @param [in] msgSz Length of digest in bytes. + * @param [in] hash Pre-hashed message digest to sign. + * @param [in] hashSz Length of digest in bytes. * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. * @return 0 on success. - * @return BAD_FUNC_ARG when key, key's parameters, msg, sig, sigSz or addRnd + * @return BAD_FUNC_ARG when key, key's parameters, hash, sig, sigSz or addRnd * is NULL. * @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 msgSz does not equal the digest size for hashType. + * when hashSz does not equal the digest size for hashType. * @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* msg, word32 msgSz, enum wc_HashType hashType, + byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd) { int ret = 0; - byte ph[WC_MAX_DIGEST_SIZE]; - byte phLen = 0; const byte* oid = NULL; byte oidLen = 0; /* Validate parameters. */ if ((key == NULL) || (key->params == NULL) || - ((ctx == NULL) && (ctxSz > 0)) || (msg == NULL) || (sig == NULL) || + ((ctx == NULL) && (ctxSz > 0)) || (hash == NULL) || (sig == NULL) || (sigSz == NULL)) { ret = BAD_FUNC_ARG; } @@ -7891,10 +7881,9 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, ret = BAD_FUNC_ARG; } if (ret == 0) { - /* Alg 23, Steps 8-23: Validate caller-supplied pre-hashed digest and - * select OID for the chosen hash algorithm. */ - ret = slhdsakey_validate_prehash(msg, msgSz, hashType, ph, &phLen, &oid, - &oidLen); + /* Alg 23, Steps 8-23: Validate caller-supplied pre-hashed digest length + * and select OID for the chosen hash algorithm. */ + ret = slhdsakey_validate_prehash(hashSz, hashType, &oid, &oidLen); } if (ret == 0) { byte n = key->params->n; @@ -7907,12 +7896,12 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, #ifdef WOLFSSL_SLHDSA_SHA2 if (SLHDSA_IS_SHA2(key->params->param)) { - /* SHA2: Build oid||ph as message for PRF_msg/H_msg. */ + /* SHA2: Build oid||hash as message for PRF_msg/H_msg. */ byte phMsg[80]; /* Max: 11 byte OID + 64 byte hash */ - word32 phMsgLen = (word32)oidLen + phLen; + word32 phMsgLen = (word32)oidLen + hashSz; XMEMCPY(phMsg, oid, oidLen); - XMEMCPY(phMsg + oidLen, ph, phLen); + XMEMCPY(phMsg + oidLen, hash, hashSz); ret = slhdsakey_prf_msg_sha2(key, key->sk + n, addRnd, hdr, ctx, ctxSz, phMsg, phMsgLen, n, sig); @@ -7942,7 +7931,7 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, ret = slhdsakey_hash_update(&key->hash.shk.shake, oid, oidLen); } if (ret == 0) { - ret = slhdsakey_hash_update(&key->hash.shk.shake, ph, phLen); + ret = slhdsakey_hash_update(&key->hash.shk.shake, hash, hashSz); } if (ret == 0) { ret = slhdsakey_hash_final(&key->hash.shk.shake, sig, n); @@ -7967,7 +7956,7 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, ret = slhdsakey_hash_update(&key->hash.shk.shake, oid, oidLen); } if (ret == 0) { - ret = slhdsakey_hash_update(&key->hash.shk.shake, ph, phLen); + ret = slhdsakey_hash_update(&key->hash.shk.shake, hash, hashSz); } if (ret == 0) { ret = slhdsakey_hash_final(&key->hash.shk.shake, md, @@ -7991,30 +7980,30 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, /* Generate a deterministic HashSLH-DSA signature. * * addrnd is the public key seed. The caller MUST pre-hash the application - * message with hashType before calling and pass the digest as msg; msgSz must - * equal the digest size of hashType (32 for SHAKE128, 64 for SHAKE256 per - * FIPS 205 Section 10.2.2). + * message with hashType before calling and pass the digest as hash; hashSz + * must equal the digest size of hashType (32 for SHAKE128, 64 for SHAKE256 + * per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Pre-hashed message digest to sign. - * @param [in] msgSz Length of digest in bytes. + * @param [in] hash Pre-hashed message digest to sign. + * @param [in] hashSz Length of digest in bytes. * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. * @return 0 on success. - * @return BAD_FUNC_ARG when key, key's parameters, msg, sig or sigSz is NULL. + * @return BAD_FUNC_ARG when key, key's parameters, hash, sig or sigSz is NULL. * @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 msgSz does not equal the digest size for hashType. + * when hashSz does not equal the digest size for hashType. * @return MISSING_KEY when private key not set. * @return MEMORY_E on dynamic memory allocation failure. * @return SHAKE-256 error return code on digest failure. */ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, - byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, + byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz) { int ret; @@ -8029,8 +8018,8 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, } else { /* Pre-hash sign. */ - ret = slhdsakey_signhash_external(key, ctx, ctxSz, msg, msgSz, hashType, - sig, sigSz, key->sk + 2 * key->params->n); + ret = slhdsakey_signhash_external(key, ctx, ctxSz, hash, hashSz, + hashType, sig, sigSz, key->sk + 2 * key->params->n); } return ret; @@ -8039,60 +8028,60 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, /* Generate a HashSLH-DSA signature with explicit randomness. * * The caller MUST pre-hash the application message with hashType before - * calling and pass the digest as msg; msgSz must equal the digest size of + * calling and pass the digest as hash; hashSz must equal the digest size of * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Pre-hashed message digest to sign. - * @param [in] msgSz Length of digest in bytes. + * @param [in] hash Pre-hashed message digest to sign. + * @param [in] hashSz Length of digest in bytes. * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. * @param [in] addRnd Additional random for signature. * @return 0 on success. - * @return BAD_FUNC_ARG when key, key's parameters, msg, sig, sigSz or addrnd + * @return BAD_FUNC_ARG when key, key's parameters, hash, sig, sigSz or addrnd * is NULL. * @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 msgSz does not equal the digest size for hashType. + * when hashSz does not equal the digest size for hashType. * @return MISSING_KEY when private key not set. * @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. */ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, - const byte* msg, word32 msgSz, enum wc_HashType hashType, byte* sig, + const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd) { /* Pre-hash sign */ - return slhdsakey_signhash_external(key, ctx, ctxSz, msg, msgSz, hashType, + return slhdsakey_signhash_external(key, ctx, ctxSz, hash, hashSz, hashType, sig, sigSz, addRnd); } /* Generate a HashSLH-DSA signature using an RNG for added randomness. * * The caller MUST pre-hash the application message with hashType before - * calling and pass the digest as msg; msgSz must equal the digest size of + * calling and pass the digest as hash; hashSz must equal the digest size of * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Pre-hashed message digest to sign. - * @param [in] msgSz Length of digest in bytes. + * @param [in] hash Pre-hashed message digest to sign. + * @param [in] hashSz Length of digest in bytes. * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [out] sig Buffer to hold signature. * @param [in, out] sigSz On in, length of signature buffer. * On out, length of signature data. * @param [in] rng Random number generator. * @return 0 on success. - * @return BAD_FUNC_ARG when key, key's parameters, msg, sig, sigSz or rng is + * @return BAD_FUNC_ARG when key, key's parameters, hash, sig, sigSz or rng is * NULL. * @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0. - * @return BAD_LENGTH_E when msgSz does not equal the digest size for + * @return BAD_LENGTH_E when hashSz does not equal the digest size for * hashType. * @return MISSING_KEY when private key not set. * @return NOT_COMPILED in when hash algorithm is not supported. @@ -8100,7 +8089,7 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, * @return SHAKE-256 error return code on digest failure. */ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, - const byte* msg, word32 msgSz, enum wc_HashType hashType, byte* sig, + const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, WC_RNG* rng) { int ret = 0; @@ -8108,7 +8097,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, /* Validate parameters before generating random. */ if ((key == NULL) || (key->params == NULL) || - ((ctx == NULL) && (ctxSz > 0)) || (msg == NULL) || (sig == NULL) || + ((ctx == NULL) && (ctxSz > 0)) || (hash == NULL) || (sig == NULL) || (sigSz == NULL) || (rng == NULL)) { ret = BAD_FUNC_ARG; } @@ -8126,7 +8115,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, } if (ret == 0) { /* Pre-hash sign. */ - ret = wc_SlhDsaKey_SignHashWithRandom(key, ctx, ctxSz, msg, msgSz, + ret = wc_SlhDsaKey_SignHashWithRandom(key, ctx, ctxSz, hash, hashSz, hashType, sig, sigSz, addRnd); } @@ -8179,40 +8168,38 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, * 21: return slh_verify_internal(M', SIG, PK) * * The caller MUST pre-hash the application message with hashType before - * calling and pass the digest as msg; msgSz must equal the digest size of + * calling and pass the digest as hash; hashSz must equal the digest size of * hashType (32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2). * * @param [in] key SLH-DSA key. * @param [in] ctx Context of signing. * @param [in] ctxSz Length of context in bytes. - * @param [in] msg Pre-hashed message digest to verify against. - * @param [in] msgSz Length of digest in bytes. + * @param [in] hash Pre-hashed message digest to verify against. + * @param [in] hashSz Length of digest in bytes. * @param [in] hashType Hash algorithm used for pre-hash (selects OID). * @param [in] sig Signature data. * @param [in] sigSz Length of signature in bytes. * @return 0 on success. - * @return BAD_FUNC_ARG when key, key's parameters, msg or sig is NULL. + * @return BAD_FUNC_ARG when key, key's parameters, hash or sig is NULL. * @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0. * @return BAD_LENGTH_E when signature size does not match parameters, or - * when msgSz does not equal the digest size for hashType. + * when hashSz does not equal the digest size for hashType. * @return MISSING_KEY when public key not set. * @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. */ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, - const byte* msg, word32 msgSz, enum wc_HashType hashType, const byte* sig, + const byte* hash, word32 hashSz, enum wc_HashType hashType, const byte* sig, word32 sigSz) { int ret = 0; - byte ph[WC_MAX_DIGEST_SIZE]; - byte phLen = 0; const byte* oid = NULL; byte oidLen = 0; /* Validate parameters. */ if ((key == NULL) || (key->params == NULL) || - ((ctx == NULL) && (ctxSz > 0)) || (msg == NULL) || (sig == NULL)) { + ((ctx == NULL) && (ctxSz > 0)) || (hash == NULL) || (sig == NULL)) { ret = BAD_FUNC_ARG; } /* Alg 20, Step 1: Check signature length is the expect length. */ @@ -8225,10 +8212,9 @@ 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 and - * select OID for the chosen hash algorithm. */ - ret = slhdsakey_validate_prehash(msg, msgSz, hashType, ph, &phLen, &oid, - &oidLen); + /* Alg 24, 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); } if (ret == 0) { byte n = key->params->n; @@ -8241,12 +8227,12 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, #ifdef WOLFSSL_SLHDSA_SHA2 if (SLHDSA_IS_SHA2(key->params->param)) { - /* SHA2: Build oid||ph as message for H_msg. */ + /* SHA2: Build oid||hash as message for H_msg. */ byte phMsg[80]; /* Max: 11 byte OID + 64 byte hash */ - word32 phMsgLen = (word32)oidLen + phLen; + word32 phMsgLen = (word32)oidLen + hashSz; XMEMCPY(phMsg, oid, oidLen); - XMEMCPY(phMsg + oidLen, ph, phLen); + XMEMCPY(phMsg + oidLen, hash, hashSz); ret = slhdsakey_h_msg_sha2(key, sig, hdr, ctx, ctxSz, phMsg, phMsgLen, md, (word32)key->params->dl1 + key->params->dl2 + @@ -8272,7 +8258,7 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, ret = slhdsakey_hash_update(&key->hash.shk.shake, oid, oidLen); } if (ret == 0) { - ret = slhdsakey_hash_update(&key->hash.shk.shake, ph, phLen); + ret = slhdsakey_hash_update(&key->hash.shk.shake, hash, hashSz); } if (ret == 0) { ret = slhdsakey_hash_final(&key->hash.shk.shake, md, diff --git a/wolfssl/wolfcrypt/wc_slhdsa.h b/wolfssl/wolfcrypt/wc_slhdsa.h index 33bb7354128..323731334ba 100644 --- a/wolfssl/wolfcrypt/wc_slhdsa.h +++ b/wolfssl/wolfcrypt/wc_slhdsa.h @@ -681,17 +681,17 @@ WOLFSSL_API int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, #ifndef WOLFSSL_SLHDSA_VERIFY_ONLY WOLFSSL_API int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, - const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, + const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz); WOLFSSL_API int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key, - const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz, + const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd); WOLFSSL_API int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, - byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, + byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, byte* sig, word32* sigSz, WC_RNG* rng); #endif /* WOLFSSL_SLHDSA_VERIFY_ONLY */ WOLFSSL_API int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, - byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType, + byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType, const byte* sig, word32 sigSz); #ifndef WOLFSSL_SLHDSA_VERIFY_ONLY From 1ee118a64df00496960aec526805def27b3c922c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 10 May 2026 17:29:54 +0000 Subject: [PATCH 4/4] SLH-DSA: harden phMsg buffer size, fix stale comments, soften ChangeLog Address self-review feedback: - Replace the magic byte phMsg[80] /* Max: 11 byte OID + 64 byte hash */ in slhdsakey_signhash_external and wc_SlhDsaKey_VerifyHash with byte phMsg[SLHDSA_PHMSG_MAX_LEN] where SLHDSA_PHMSG_MAX_LEN = SLHDSA_OID_MAX_LEN + WC_MAX_DIGEST_SIZE. The derived bound now scales automatically with the project-wide max digest size, so adding a future hashType with a larger digest in slhdsakey_validate_prehash won't silently overflow these buffers. - Replace three stale "/* Pre-hash sign. */" inline comments with "/* HashSLH-DSA sign with caller-supplied digest. */" since the functions no longer pre-hash anything. - ChangeLog: soften the "matches wc_dilithium_*_ctx_hash exactly" claim to acknowledge that ML-DSA uses hashLen while SLH-DSA keeps the local Sz suffix convention. https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd --- ChangeLog.md | 8 +++++--- wolfcrypt/src/wc_slhdsa.c | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a8dd6f5b756..2b8157ad206 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,9 +5,11 @@ * **BREAKING (FIPS 205 SLH-DSA)**: `wc_SlhDsaKey_SignHash`, `wc_SlhDsaKey_SignHashDeterministic`, `wc_SlhDsaKey_SignHashWithRandom`, and `wc_SlhDsaKey_VerifyHash` now take the **caller-pre-hashed message digest** - via `hash`/`hashSz` parameters (renamed from `msg`/`msgSz`), matching - `wc_dilithium_sign_ctx_hash` / `wc_dilithium_verify_ctx_hash` exactly and - aligning with OpenSSL's HASH-ML-DSA, mldsa-native, leancrypto SLH-DSA, and + via `hash`/`hashSz` parameters (renamed from `msg`/`msgSz`), aligned with + ML-DSA's `wc_dilithium_sign_ctx_hash` / `wc_dilithium_verify_ctx_hash` + semantics (those use `hash` / `hashLen`; SLH-DSA keeps the local `Sz` + suffix convention) and with OpenSSL's HASH-ML-DSA, mldsa-native, + leancrypto SLH-DSA, and NIST ACVP `signatureInterface=external` / `preHash=preHash` test vectors. `hashSz` must equal `wc_HashGetDigestSize(hashType)` (32 bytes for SHAKE128, 64 bytes for SHAKE256 per FIPS 205 Section 10.2.2); otherwise diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 4fc39dba119..eb4a022113f 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -7579,6 +7579,16 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime, return ret; } +/* 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) + #ifdef WOLFSSL_SHA224 /* OID for SHA-224 for hash signing/verification. */ static const byte slhdsakey_oid_sha224[] = { @@ -7897,7 +7907,7 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, #ifdef WOLFSSL_SLHDSA_SHA2 if (SLHDSA_IS_SHA2(key->params->param)) { /* SHA2: Build oid||hash as message for PRF_msg/H_msg. */ - byte phMsg[80]; /* Max: 11 byte OID + 64 byte hash */ + byte phMsg[SLHDSA_PHMSG_MAX_LEN]; word32 phMsgLen = (word32)oidLen + hashSz; XMEMCPY(phMsg, oid, oidLen); @@ -8017,7 +8027,7 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx, ret = MISSING_KEY; } else { - /* Pre-hash sign. */ + /* HashSLH-DSA sign with caller-supplied digest. */ ret = slhdsakey_signhash_external(key, ctx, ctxSz, hash, hashSz, hashType, sig, sigSz, key->sk + 2 * key->params->n); } @@ -8056,7 +8066,7 @@ 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) { - /* Pre-hash sign */ + /* HashSLH-DSA sign with caller-supplied digest. */ return slhdsakey_signhash_external(key, ctx, ctxSz, hash, hashSz, hashType, sig, sigSz, addRnd); } @@ -8114,7 +8124,7 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, ret = wc_RNG_GenerateBlock(rng, addRnd, key->params->n); } if (ret == 0) { - /* Pre-hash sign. */ + /* HashSLH-DSA sign with caller-supplied digest. */ ret = wc_SlhDsaKey_SignHashWithRandom(key, ctx, ctxSz, hash, hashSz, hashType, sig, sigSz, addRnd); } @@ -8228,7 +8238,7 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz, #ifdef WOLFSSL_SLHDSA_SHA2 if (SLHDSA_IS_SHA2(key->params->param)) { /* SHA2: Build oid||hash as message for H_msg. */ - byte phMsg[80]; /* Max: 11 byte OID + 64 byte hash */ + byte phMsg[SLHDSA_PHMSG_MAX_LEN]; word32 phMsgLen = (word32)oidLen + hashSz; XMEMCPY(phMsg, oid, oidLen);