Skip to content

Commit f16216e

Browse files
committed
HashSLH-DSA APIs now take the pre-hashed digest, not the raw message
wc_SlhDsaKey_{Sign,Verify}Hash* previously accepted the raw message and performed the pre-hash internally. They now require the caller to hash the message first and pass the resulting digest -- the functions no longer call wc_*Hash() themselves and feed the supplied digest directly into the M' construction. Parameters are renamed from msg/msgSz to hash/hashSz to reflect this, and hashSz is validated against wc_HashGetDigestSize(hashType) per FIPS 205 Section 10.2.2 (32 for SHAKE128, 64 for SHAKE256), returning BAD_LENGTH_E on mismatch. This matches ML-DSA's wc_dilithium_{sign,verify}_ctx_hash, NIST ACVP signatureInterface=external / preHash=preHash vectors, and other libraries (OpenSSL HASH-ML-DSA, leancrypto, mldsa-native). It also enables distributed signers and HSM-style flows where the digest is computed separately from the signing operation. Migration: callers must now hash the message before invoking these APIs; passing the raw message will either fail length validation or produce signatures over the wrong input. The M'-supplied wc_SlhDsaKey_SignMsg* / VerifyMsg family (FIPS 205 internal interface, Algorithms 19/20) is unchanged but gains stricter input validation and doxygen coverage.
1 parent 9f759fa commit f16216e

8 files changed

Lines changed: 615 additions & 200 deletions

File tree

ChangeLog.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
## Enhancements
44

5+
* **BREAKING (FIPS 205 SLH-DSA)**: `wc_SlhDsaKey_SignHash`,
6+
`wc_SlhDsaKey_SignHashDeterministic`, `wc_SlhDsaKey_SignHashWithRandom`, and
7+
`wc_SlhDsaKey_VerifyHash` now take the **caller-pre-hashed message digest**
8+
via `hash`/`hashSz` parameters (renamed from `msg`/`msgSz`), aligned with
9+
ML-DSA's `wc_dilithium_sign_ctx_hash` / `wc_dilithium_verify_ctx_hash`
10+
semantics, and NIST ACVP `signatureInterface=external` / `preHash=preHash`
11+
test vectors. `hashSz` must equal `wc_HashGetDigestSize(hashType)` (32 bytes
12+
for SHAKE128, 64 bytes for SHAKE256 per FIPS 205 Section 10.2.2); otherwise
13+
`BAD_LENGTH_E` is returned. Migration: hash the message yourself before the
14+
call (callers using positional arguments are source-compatible; only the
15+
parameter names changed). The pre-existing `wc_SlhDsaKey_SignMsgDeterministic`
16+
and `wc_SlhDsaKey_SignMsgWithRandom` (FIPS 205 internal interface, M'
17+
supplied directly) are unaffected and gain stricter input validation
18+
matching the `*Hash*` family. `wc_SlhDsaKey_VerifyMsg` is unchanged. All
19+
three gain doxygen coverage.
20+
521
* TLS 1.3: zero traffic key staging buffers in `SetKeysSide()` once a
622
CryptoCB callback has imported the AES key into a Secure Element
723
(`aes->devCtx != NULL`). Clears `keys->{client,server}_write_key`

doc/dox_comments/header_files/wc_slhdsa.h

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,19 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx,
302302
303303
\brief Signs using the SLH-DSA internal interface with deterministic
304304
randomness. Unlike the external interface, M' is provided directly by
305-
the caller — no 0x00||len(ctx)||ctx||M wrapping is performed. This
306-
corresponds to FIPS 205 Algorithm 19 (slh_sign_internal) with opt_rand
307-
set to PK.seed.
305+
the caller — no wrapping is performed. This corresponds to FIPS 205
306+
Algorithm 19 (slh_sign_internal) with opt_rand set to PK.seed.
308307
309-
Use this when the CAVP test framework or protocol layer has already
310-
constructed M'.
308+
Use this when the ACVP signatureInterface=internal test framework or a
309+
protocol layer has already constructed M'. For HashSLH-DSA the caller
310+
builds M' as 0x01 || ctxSz || ctx || OID(hashType) || PHM and passes it
311+
in here, where PHM is the hash of the application message under hashType.
311312
312313
\return 0 on success.
313314
\return BAD_FUNC_ARG if key, mprime, sig, or sigSz is NULL.
315+
\return BAD_LENGTH_E if sigSz is less than the parameter set's signature
316+
length.
317+
\return MISSING_KEY if the private key has not been set.
314318
315319
\param [in] key Pointer to a private SlhDsaKey.
316320
\param [in] mprime Pointer to the pre-constructed M' message.
@@ -334,6 +338,7 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx,
334338
\sa wc_SlhDsaKey_SignMsgWithRandom
335339
\sa wc_SlhDsaKey_VerifyMsg
336340
\sa wc_SlhDsaKey_SignDeterministic
341+
\sa wc_SlhDsaKey_SignHashDeterministic
337342
*/
338343
int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key,
339344
const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz);
@@ -344,10 +349,14 @@ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key,
344349
\brief Signs using the SLH-DSA internal interface with caller-provided
345350
additional randomness. M' is provided directly — no wrapping is performed.
346351
This corresponds to FIPS 205 Algorithm 19 (slh_sign_internal) with an
347-
explicit opt_rand value.
352+
explicit opt_rand value. See wc_SlhDsaKey_SignMsgDeterministic for the M'
353+
layout used by HashSLH-DSA.
348354
349355
\return 0 on success.
350356
\return BAD_FUNC_ARG if key, mprime, sig, sigSz, or addRnd is NULL.
357+
\return BAD_LENGTH_E if sigSz is less than the parameter set's signature
358+
length.
359+
\return MISSING_KEY if the private key has not been set.
351360
352361
\param [in] key Pointer to a private SlhDsaKey.
353362
\param [in] mprime Pointer to the pre-constructed M' message.
@@ -373,6 +382,7 @@ int wc_SlhDsaKey_SignMsgDeterministic(SlhDsaKey* key,
373382
374383
\sa wc_SlhDsaKey_SignMsgDeterministic
375384
\sa wc_SlhDsaKey_VerifyMsg
385+
\sa wc_SlhDsaKey_SignHashWithRandom
376386
*/
377387
int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key,
378388
const byte* mprime, word32 mprimeSz, byte* sig, word32* sigSz,
@@ -412,29 +422,36 @@ int wc_SlhDsaKey_SignMsgWithRandom(SlhDsaKey* key,
412422
413423
\sa wc_SlhDsaKey_SignMsgDeterministic
414424
\sa wc_SlhDsaKey_Verify
425+
\sa wc_SlhDsaKey_VerifyHash
415426
*/
416427
int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime,
417428
word32 mprimeSz, const byte* sig, word32 sigSz);
418429

419430
/*!
420431
\ingroup SLH_DSA
421432
422-
\brief Signs a pre-hashed message using the SLH-DSA external (HashSLH-DSA)
423-
interface with deterministic randomness. The message is hashed with the
424-
specified hash algorithm, then signed per FIPS 205 Algorithm 22 with the
425-
pre-hash domain separator (0x01).
433+
\brief Signs a caller-pre-hashed message digest using the SLH-DSA external
434+
(HashSLH-DSA) interface with deterministic randomness, per FIPS 205
435+
Algorithm 23 with the pre-hash domain separator (0x01). The caller must
436+
hash the application message with hashType first and pass the digest as
437+
hash; this function does NOT hash its input.
426438
427439
\return 0 on success.
428-
\return BAD_FUNC_ARG if key, msg, sig, or sigSz is NULL, or hashType
429-
is unsupported.
440+
\return BAD_FUNC_ARG if key, hash, sig, or sigSz is NULL.
441+
\return BAD_LENGTH_E if hashSz does not equal the digest size for hashType
442+
(32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2).
443+
\return NOT_COMPILED_IN if hashType is not supported in this build.
444+
\return MISSING_KEY if the private key has not been set.
430445
431446
\param [in] key Pointer to a private SlhDsaKey.
432447
\param [in] ctx Context string. May be NULL if ctxSz is 0.
433448
\param [in] ctxSz Length of the context string (0-255).
434-
\param [in] msg Pointer to the message to hash and sign.
435-
\param [in] msgSz Length of the message.
436-
\param [in] hashType Hash algorithm to use for pre-hashing. Supported:
437-
WC_HASH_TYPE_SHA256, WC_HASH_TYPE_SHA384, WC_HASH_TYPE_SHA512,
449+
\param [in] hash Pointer to the pre-hashed message digest. hashSz must
450+
equal the digest size for hashType.
451+
\param [in] hashSz Length of the digest in bytes.
452+
\param [in] hashType Hash algorithm used for pre-hashing (selects OID).
453+
Supported: WC_HASH_TYPE_SHA224, WC_HASH_TYPE_SHA256, WC_HASH_TYPE_SHA384,
454+
WC_HASH_TYPE_SHA512, WC_HASH_TYPE_SHA512_224, WC_HASH_TYPE_SHA512_256,
438455
WC_HASH_TYPE_SHAKE128, WC_HASH_TYPE_SHAKE256, WC_HASH_TYPE_SHA3_224,
439456
WC_HASH_TYPE_SHA3_256, WC_HASH_TYPE_SHA3_384, WC_HASH_TYPE_SHA3_512.
440457
\param [out] sig Buffer to receive the signature.
@@ -447,92 +464,116 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime,
447464
byte sig[WC_SLHDSA_MAX_SIG_LEN];
448465
word32 sigSz = sizeof(sig);
449466
byte msg[] = "Hello World!";
467+
byte digest[WC_SHA256_DIGEST_SIZE];
450468
int ret;
451469
470+
wc_Sha256Hash(msg, sizeof(msg), digest);
452471
ret = wc_SlhDsaKey_SignHashDeterministic(&key, NULL, 0,
453-
msg, sizeof(msg), WC_HASH_TYPE_SHA256, sig, &sigSz);
472+
digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig, &sigSz);
454473
\endcode
455474
456475
\sa wc_SlhDsaKey_SignHashWithRandom
457476
\sa wc_SlhDsaKey_SignHash
458477
\sa wc_SlhDsaKey_VerifyHash
478+
\sa wc_SlhDsaKey_SignMsgDeterministic
459479
*/
460480
int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key,
461-
const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz,
481+
const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz,
462482
enum wc_HashType hashType, byte* sig, word32* sigSz);
463483

464484
/*!
465485
\ingroup SLH_DSA
466486
467-
\brief Signs a pre-hashed message using the SLH-DSA external (HashSLH-DSA)
468-
interface with caller-provided additional randomness.
487+
\brief Signs a caller-pre-hashed message digest using the SLH-DSA external
488+
(HashSLH-DSA) interface with caller-provided additional randomness. The
489+
caller must hash the application message with hashType first and pass the
490+
digest as hash; this function does NOT hash its input.
469491
470492
\return 0 on success.
471-
\return BAD_FUNC_ARG if key, msg, sig, sigSz, or addRnd is NULL.
493+
\return BAD_FUNC_ARG if key, hash, sig, sigSz, or addRnd is NULL.
494+
\return BAD_LENGTH_E if hashSz does not equal the digest size for hashType
495+
(32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2).
496+
\return NOT_COMPILED_IN if hashType is not supported in this build.
472497
473498
\param [in] key Pointer to a private SlhDsaKey.
474499
\param [in] ctx Context string. May be NULL if ctxSz is 0.
475500
\param [in] ctxSz Length of the context string (0-255).
476-
\param [in] msg Pointer to the message to hash and sign.
477-
\param [in] msgSz Length of the message.
478-
\param [in] hashType Hash algorithm to use for pre-hashing.
501+
\param [in] hash Pointer to the pre-hashed message digest. hashSz must
502+
equal the digest size for hashType.
503+
\param [in] hashSz Length of the digest in bytes.
504+
\param [in] hashType Hash algorithm used for pre-hashing (selects OID).
479505
\param [out] sig Buffer to receive the signature.
480506
\param [in,out] sigSz On input, size of sig buffer. On output, actual
481507
signature length.
482508
\param [in] addRnd Additional randomness (n bytes).
483509
484510
\sa wc_SlhDsaKey_SignHashDeterministic
485511
\sa wc_SlhDsaKey_VerifyHash
512+
\sa wc_SlhDsaKey_SignMsgWithRandom
486513
*/
487514
int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key,
488-
const byte* ctx, byte ctxSz, const byte* msg, word32 msgSz,
515+
const byte* ctx, byte ctxSz, const byte* hash, word32 hashSz,
489516
enum wc_HashType hashType, byte* sig, word32* sigSz, byte* addRnd);
490517

491518
/*!
492519
\ingroup SLH_DSA
493520
494-
\brief Signs a pre-hashed message using the SLH-DSA external (HashSLH-DSA)
495-
interface with RNG-provided randomness.
521+
\brief Signs a caller-pre-hashed message digest using the SLH-DSA external
522+
(HashSLH-DSA) interface with RNG-provided randomness. The caller must
523+
hash the application message with hashType first and pass the digest as
524+
hash; this function does NOT hash its input.
496525
497526
\return 0 on success.
498-
\return BAD_FUNC_ARG if key, msg, sig, sigSz, or rng is NULL.
527+
\return BAD_FUNC_ARG if key, hash, sig, sigSz, or rng is NULL.
528+
\return BAD_LENGTH_E if hashSz does not equal the digest size for hashType
529+
(32 for SHAKE128, 64 for SHAKE256 per FIPS 205 Section 10.2.2).
530+
\return NOT_COMPILED_IN if hashType is not supported in this build.
499531
500532
\param [in] key Pointer to a private SlhDsaKey.
501533
\param [in] ctx Context string. May be NULL if ctxSz is 0.
502534
\param [in] ctxSz Length of the context string (0-255).
503-
\param [in] msg Pointer to the message to hash and sign.
504-
\param [in] msgSz Length of the message.
505-
\param [in] hashType Hash algorithm to use for pre-hashing.
535+
\param [in] hash Pointer to the pre-hashed message digest. hashSz must
536+
equal the digest size for hashType.
537+
\param [in] hashSz Length of the digest in bytes.
538+
\param [in] hashType Hash algorithm used for pre-hashing (selects OID).
506539
\param [out] sig Buffer to receive the signature.
507540
\param [in,out] sigSz On input, size of sig buffer. On output, actual
508541
signature length.
509542
\param [in] rng Pointer to an initialized WC_RNG.
510543
511544
\sa wc_SlhDsaKey_SignHashDeterministic
512545
\sa wc_SlhDsaKey_VerifyHash
546+
\sa wc_SlhDsaKey_SignMsgDeterministic
513547
*/
514548
int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx,
515-
byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType,
549+
byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType,
516550
byte* sig, word32* sigSz, WC_RNG* rng);
517551

518552
/*!
519553
\ingroup SLH_DSA
520554
521-
\brief Verifies an SLH-DSA signature over a pre-hashed message
522-
(HashSLH-DSA). The message is hashed with the specified hash algorithm
523-
before verification.
555+
\brief Verifies an SLH-DSA signature using the external HashSLH-DSA
556+
interface (FIPS 205 Algorithm 24). The caller must hash the application
557+
message with hashType first and pass the digest as hash; this function
558+
does NOT hash its input.
524559
525560
\return 0 on success (signature valid).
526-
\return BAD_FUNC_ARG if key, msg, or sig is NULL.
561+
\return BAD_FUNC_ARG if key, hash, or sig is NULL.
562+
\return BAD_LENGTH_E if sigSz does not match the parameter set, or if
563+
hashSz does not equal the digest size for hashType (32 for SHAKE128, 64
564+
for SHAKE256 per FIPS 205 Section 10.2.2).
565+
\return NOT_COMPILED_IN if hashType is not supported in this build.
566+
\return MISSING_KEY if the public key has not been set.
527567
\return SIG_VERIFY_E if the signature is invalid.
528568
529569
\param [in] key Pointer to a public SlhDsaKey.
530570
\param [in] ctx Context string. May be NULL if ctxSz is 0.
531571
\param [in] ctxSz Length of the context string (0-255).
532-
\param [in] msg Pointer to the message to hash and verify.
533-
\param [in] msgSz Length of the message.
534-
\param [in] hashType Hash algorithm used for pre-hashing. Must match the
535-
hash used during signing.
572+
\param [in] hash Pointer to the pre-hashed message digest. hashSz must
573+
equal the digest size for hashType.
574+
\param [in] hashSz Length of the digest in bytes.
575+
\param [in] hashType Hash algorithm used for pre-hashing (selects OID).
576+
Must match the hash used during signing.
536577
\param [in] sig Pointer to the signature to verify.
537578
\param [in] sigSz Length of the signature.
538579
@@ -542,20 +583,23 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx,
542583
byte sig[...];
543584
word32 sigSz;
544585
byte msg[] = "Hello World!";
586+
byte digest[WC_SHA256_DIGEST_SIZE];
545587
int ret;
546588
589+
wc_Sha256Hash(msg, sizeof(msg), digest);
547590
ret = wc_SlhDsaKey_VerifyHash(&key, NULL, 0,
548-
msg, sizeof(msg), WC_HASH_TYPE_SHA256, sig, sigSz);
591+
digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig, sigSz);
549592
if (ret == 0) {
550593
// signature is valid
551594
}
552595
\endcode
553596
554597
\sa wc_SlhDsaKey_SignHashDeterministic
555598
\sa wc_SlhDsaKey_Verify
599+
\sa wc_SlhDsaKey_VerifyMsg
556600
*/
557601
int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx,
558-
byte ctxSz, const byte* msg, word32 msgSz, enum wc_HashType hashType,
602+
byte ctxSz, const byte* hash, word32 hashSz, enum wc_HashType hashType,
559603
const byte* sig, word32 sigSz);
560604

561605
/*!

0 commit comments

Comments
 (0)