From ff96497326e928fe51dd3cd4b7b5d940872fa6cf Mon Sep 17 00:00:00 2001 From: Arpan Sharma Date: Sat, 3 Oct 2026 09:17:48 -0500 Subject: [PATCH] SLH-DSA: accept (NULL, 0) message in SignWithRandom/SignDeterministic wc_SlhDsaKey_SignWithRandom() rejected msg == NULL for any length, so it and wc_SlhDsaKey_SignDeterministic() returned BAD_FUNC_ARG for an empty message passed as (NULL, 0), unlike wc_SlhDsaKey_Sign() and Verify(). Use the same check as Sign() and, like Sign(), replace a NULL message with a one-byte static stand-in before the crypto callback dispatch. Update the doxygen and source comments, and add tests for the (NULL, 0) case. --- doc/dox_comments/header_files/wc_slhdsa.h | 13 ++++++++----- tests/api/test_slhdsa.c | 4 ++++ wolfcrypt/src/wc_slhdsa.c | 16 +++++++++++++--- wolfcrypt/test/test.c | 15 +++++++++++++++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/doc/dox_comments/header_files/wc_slhdsa.h b/doc/dox_comments/header_files/wc_slhdsa.h index 519e2c0055c..fb48cebd122 100644 --- a/doc/dox_comments/header_files/wc_slhdsa.h +++ b/doc/dox_comments/header_files/wc_slhdsa.h @@ -238,7 +238,8 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, M' = 0x00 || len(ctx) || ctx || M before signing. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, or sigSz is NULL. + \return BAD_FUNC_ARG if key, sig, or sigSz is NULL, or msg is NULL but + msgSz is greater than 0. \return BUFFER_E if the output buffer is too small. \param [in] key Pointer to a private SlhDsaKey. @@ -279,7 +280,8 @@ int wc_SlhDsaKey_SignDeterministic(SlhDsaKey* key, const byte* ctx, an explicit opt_rand value. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, sigSz, or addRnd is NULL. + \return BAD_FUNC_ARG if key, sig, sigSz, or addRnd is NULL, or msg is + NULL but msgSz is greater than 0. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. @@ -321,7 +323,8 @@ int wc_SlhDsaKey_SignWithRandom(SlhDsaKey* key, const byte* ctx, that uses the WC_RNG for opt_rand. \return 0 on success. - \return BAD_FUNC_ARG if key, msg, sig, sigSz, or rng is NULL. + \return BAD_FUNC_ARG if key, sig, sigSz, or rng is NULL, or msg is NULL + but msgSz is greater than 0. \param [in] key Pointer to a private SlhDsaKey. \param [in] ctx Context string. May be NULL if ctxSz is 0. @@ -361,8 +364,8 @@ int wc_SlhDsaKey_Sign(SlhDsaKey* key, const byte* ctx, internally as M' = 0x00 || len(ctx) || ctx || M before verification. \return 0 on success (signature valid). - \return BAD_FUNC_ARG if key, msg, or sig is NULL, or ctx is NULL but - ctxSz is greater than 0. + \return BAD_FUNC_ARG if key or sig is NULL, or ctx is NULL but ctxSz is + greater than 0, or msg is NULL but msgSz is greater than 0. \return BAD_LENGTH_E if sigSz does not match the parameter set's signature length. \return MISSING_KEY if the public key has not been set. diff --git a/tests/api/test_slhdsa.c b/tests/api/test_slhdsa.c index 590eb026188..cee7b562937 100644 --- a/tests/api/test_slhdsa.c +++ b/tests/api/test_slhdsa.c @@ -3283,6 +3283,10 @@ int test_wc_SlhdsaDecisionCoverage(void) sizeof(dummyMsg), dummySig, &tinySigSz, dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* msg==NULL */ tinySigSz = 1; + ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, dummyMsg, 0, NULL, + 0, dummySig, &tinySigSz, dummyAddRnd), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); /* msg==NULL, msgSz==0 */ + tinySigSz = 1; ExpectIntEQ(wc_SlhDsaKey_SignWithRandom(&key, NULL, 0, dummyMsg, sizeof(dummyMsg), NULL, &tinySigSz, dummyAddRnd), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* sig==NULL */ diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index b84566db954..9ca4fdfc2cb 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -9399,8 +9399,9 @@ static int slhdsakey_sign_external(SlhDsaKey* key, const byte* ctx, byte ctxSz, * @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 or sig is NULL. + * @return BAD_FUNC_ARG when key, key's parameters, sig or sigSz is NULL. * @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0. + * @return BAD_FUNC_ARG when msg is NULL but msg length is greater than 0. * @return BAD_LENGTH_E when sigSz is less than required signature length. * @return MISSING_KEY when the public key seed is not set, or when * there is no device and no private key to sign with. @@ -9442,8 +9443,10 @@ int wc_SlhDsaKey_SignDeterministic(SlhDsaKey* key, const byte* ctx, byte ctxSz, * 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 or addrnd is NULL. + * @return BAD_FUNC_ARG when key, key's parameters, sig, sigSz or addRnd is + * NULL. * @return BAD_FUNC_ARG when ctx is NULL but ctx length is greater than 0. + * @return BAD_FUNC_ARG when msg is NULL but msg length is greater than 0. * @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. @@ -9456,7 +9459,8 @@ int wc_SlhDsaKey_SignWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, /* Validate parameters. */ if ((key == NULL) || (key->params == NULL) || - ((ctx == NULL) && (ctxSz > 0)) || (msg == NULL) || (sig == NULL) || + ((ctx == NULL) && (ctxSz > 0)) || + ((msg == NULL) && (msgSz != 0)) || (sig == NULL) || (sigSz == NULL)) { ret = BAD_FUNC_ARG; } @@ -9470,6 +9474,12 @@ int wc_SlhDsaKey_SignWithRandom(SlhDsaKey* key, const byte* ctx, byte ctxSz, ret = BAD_FUNC_ARG; } + /* Accept an empty message as (NULL, 0), as wc_SlhDsaKey_Sign() does. */ + if ((ret == 0) && (msg == NULL)) { + static const byte slhdsa_empty_msg[] = {0}; + msg = slhdsa_empty_msg; + } + #ifdef WOLF_CRYPTO_CB if (ret == 0) { #ifndef WOLF_CRYPTO_CB_FIND diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 260b6e94d22..eea11fef360 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -71511,6 +71511,21 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param) if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } + + /* Same for the deterministic variant, which signs through + * wc_SlhDsaKey_SignWithRandom(). */ + sigLen = WC_SLHDSA_MAX_SIG_LEN; + PRIVATE_KEY_UNLOCK(); + ret = wc_SlhDsaKey_SignDeterministic(key, NULL, 0, NULL, 0, sig, + &sigLen); + PRIVATE_KEY_LOCK(); + if (ret != 0) { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } + ret = wc_SlhDsaKey_Verify(key_vfy, NULL, 0, NULL, 0, sig, sigLen); + if (ret != 0) { + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } } /* HashSLH-DSA takes the caller's pre-hashed digest as input. */