Merge pull request #10450 from Frauschi/slhdsa_pre_hash

HashSLH-DSA APIs take the pre-hashed digest, not the raw message
This commit is contained in:
Daniel Pouzzner
2026-05-11 16:29:32 -05:00
committed by GitHub
8 changed files with 619 additions and 204 deletions
+90 -29
View File
@@ -12737,41 +12737,102 @@ 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);
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);
#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. 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];
bench_stats_start(&count, &start);
do {
ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, msg,
(word32)sizeof(msg), WC_HASH_TYPE_SHA256, sig, sigLen);
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
);
bench_stats_asym_finish(name, len, "vrfy-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, 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
#endif
);
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
+181 -108
View File
@@ -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 */
@@ -7538,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[] = {
@@ -7613,70 +7664,73 @@ static const byte slhdsakey_oid_sha3_512[] = {
#endif
#endif
/* Pre-hash the message with the hash specified.
/* Validate the caller-supplied pre-hashed digest length and look up the
* corresponding OID for the chosen hash algorithm.
*
* @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.
* 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 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. Callers feed the caller-supplied digest buffer
* directly into the M' construction -- there is no copy.
*
* @param [in] hashSz Length of the caller-supplied digest in bytes.
* @param [in] hashType Hash algorithm identifier (selects OID and length).
* @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 hashSz 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)
static int slhdsakey_validate_prehash(word32 hashSz,
enum wc_HashType hashType, 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 +7738,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 Section 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 Section 10.2.2 fixes SHAKE256 PHM length at 512 bits. */
expectedLen = WC_SHA3_512_DIGEST_SIZE;
break;
#endif
#ifdef WOLFSSL_SHA3
@@ -7701,32 +7755,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 +7785,10 @@ static int slhdsakey_prehash_msg(const byte* msg, word32 msgSz,
break;
}
if ((ret == 0) && (hashSz != expectedLen)) {
ret = BAD_LENGTH_E;
}
return ret;
}
@@ -7790,37 +7844,40 @@ 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 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 Message to sign.
* @param [in] msgSz Length of message in bytes.
* @param [in] hashType Hash algorithm to use in pre-hash.
* @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.
* @return BAD_LENGTH_E when sigSz is less than required signature length, or
* when hashSz does not equal the digest size for hashType.
* @return NOT_COMPILED in when hash algorithm is not supported.
* @return 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;
}
@@ -7834,10 +7891,9 @@ 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.
*/
ret = slhdsakey_prehash_msg(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;
@@ -7850,12 +7906,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. */
byte phMsg[80]; /* Max: 11 byte OID + 64 byte hash */
word32 phMsgLen = (word32)oidLen + phLen;
/* SHA2: Build oid||hash as message for PRF_msg/H_msg. */
byte phMsg[SLHDSA_PHMSG_MAX_LEN];
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);
@@ -7885,7 +7941,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);
@@ -7910,7 +7966,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,
@@ -7931,29 +7987,33 @@ 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 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 Message to sign.
* @param [in] msgSz Length of message in bytes.
* @param [in] hashType Hash algorithm to use in pre-hash.
* @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.
* @return BAD_LENGTH_E when sigSz is less than required signature length, or
* 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;
@@ -7967,68 +8027,79 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key, const byte* ctx,
ret = MISSING_KEY;
}
else {
/* Pre-hash sign. */
ret = slhdsakey_signhash_external(key, ctx, ctxSz, msg, msgSz, hashType,
sig, sigSz, key->sk + 2 * key->params->n);
/* 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);
}
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 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 Message to sign.
* @param [in] msgSz Length of message in bytes.
* @param [in] hashType Hash algorithm to use in pre-hash.
* @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.
* @return BAD_LENGTH_E when sigSz is less than required signature length, or
* 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,
/* HashSLH-DSA sign with caller-supplied digest. */
return slhdsakey_signhash_external(key, ctx, ctxSz, hash, hashSz, hashType,
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 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 Message to sign.
* @param [in] msgSz Length of message in bytes.
* @param [in] hashType Hash algorithm to use in pre-hash.
* @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 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_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;
@@ -8036,7 +8107,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;
}
@@ -8053,8 +8124,8 @@ 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. */
ret = wc_SlhDsaKey_SignHashWithRandom(key, ctx, ctxSz, msg, msgSz,
/* HashSLH-DSA sign with caller-supplied digest. */
ret = wc_SlhDsaKey_SignHashWithRandom(key, ctx, ctxSz, hash, hashSz,
hashType, sig, sigSz, addRnd);
}
@@ -8106,36 +8177,39 @@ 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 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 Message to sign.
* @param [in] msgSz Length of message in bytes.
* @param [in] hashType Hash algorithm to use in pre-hash.
* @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.
* @return BAD_LENGTH_E when signature size does not match parameters, or
* 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. */
@@ -8148,10 +8222,9 @@ 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.
*/
ret = slhdsakey_prehash_msg(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;
@@ -8164,12 +8237,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. */
byte phMsg[80]; /* Max: 11 byte OID + 64 byte hash */
word32 phMsgLen = (word32)oidLen + phLen;
/* SHA2: Build oid||hash as message for H_msg. */
byte phMsg[SLHDSA_PHMSG_MAX_LEN];
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 +
@@ -8195,7 +8268,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,
+41 -14
View File
@@ -54542,6 +54542,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) ?
@@ -54549,15 +54550,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) {
@@ -54566,18 +54585,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