SLH-DSA pre-hash enforcement per FIPS 205 and FIPS v7 test fixes

pull/11265/head
kaleb-himes 2026-08-29 20:16:25 -06:00
parent b273b7d11b
commit e8e0630c76
7 changed files with 461 additions and 110 deletions

View File

@ -763,6 +763,7 @@ WC_DISABLE_RADIX_ZERO_PAD
WC_DRBG_BANKREF
WC_FIPS_AESGCM_NO_SHORT_NONCES
WC_FIPS_AESGCM_ONE_SHOT_EXT_IV_ALLOWED
WC_FIPS_ED25519CTX_NOT_APPROVED
WC_FLAG_DONT_USE_AESNI
WC_FORCE_LINUXKM_FORTIFY_SOURCE
WC_HASH_CUSTOM_MAX_BLOCK_SIZE

View File

@ -542,6 +542,9 @@ int wc_SlhDsaKey_VerifyMsg(SlhDsaKey* key, const byte* mprime,
\return 0 on success.
\return BAD_FUNC_ARG if key, hash, sig, or sigSz is NULL.
\return BAD_FUNC_ARG if hashType is WC_HASH_TYPE_NONE, or if the
pre-hash gives less collision strength than the parameter set's
security category (FIPS 205 sec. 10.2).
\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.
@ -595,6 +598,9 @@ int wc_SlhDsaKey_SignHashDeterministic(SlhDsaKey* key,
\return 0 on success.
\return BAD_FUNC_ARG if key, hash, sig, sigSz, or addRnd is NULL.
\return BAD_FUNC_ARG if hashType is WC_HASH_TYPE_NONE, or if the
pre-hash gives less collision strength than the parameter set's
security category (FIPS 205 sec. 10.2).
\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.
@ -629,6 +635,9 @@ int wc_SlhDsaKey_SignHashWithRandom(SlhDsaKey* key,
\return 0 on success.
\return BAD_FUNC_ARG if key, hash, sig, sigSz, or rng is NULL.
\return BAD_FUNC_ARG if hashType is WC_HASH_TYPE_NONE, or if the
pre-hash gives less collision strength than the parameter set's
security category (FIPS 205 sec. 10.2).
\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.
@ -663,6 +672,9 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx,
\return 0 on success (signature valid).
\return BAD_FUNC_ARG if key, hash, or sig is NULL.
\return BAD_FUNC_ARG if hashType is WC_HASH_TYPE_NONE, or if the
pre-hash gives less collision strength than the parameter set's
security category (FIPS 205 sec. 10.2).
\return BAD_LENGTH_E if sigSz does not match the parameter set, or if
hashSz does not equal the digest size for hashType (32 for SHAKE128, 64
for SHAKE256 per FIPS 205 Section 10.2.2).

View File

@ -3849,24 +3849,38 @@ int test_wc_PKCS7_DecodeAuthEnvelopedData_truncated(void)
static long pkcs7_leak_live; /* outstanding allocations */
static void* pkcs7_leak_malloc_cb(size_t size)
/* These callback types take (func, line) too under WOLFSSL_DEBUG_MEMORY. */
#ifdef WOLFSSL_DEBUG_MEMORY
#define PKCS7_LEAK_CB_TAIL , const char* func, unsigned int line
#define PKCS7_LEAK_CB_UNUSED (void)func; (void)line;
#else
#define PKCS7_LEAK_CB_TAIL
#define PKCS7_LEAK_CB_UNUSED
#endif
static void* pkcs7_leak_malloc_cb(size_t size PKCS7_LEAK_CB_TAIL)
{
void* p = malloc(size);
void* p;
PKCS7_LEAK_CB_UNUSED
p = malloc(size);
if (p != NULL)
pkcs7_leak_live++;
return p;
}
static void pkcs7_leak_free_cb(void* ptr)
static void pkcs7_leak_free_cb(void* ptr PKCS7_LEAK_CB_TAIL)
{
PKCS7_LEAK_CB_UNUSED
if (ptr != NULL)
pkcs7_leak_live--;
free(ptr);
}
static void* pkcs7_leak_realloc_cb(void* ptr, size_t size)
static void* pkcs7_leak_realloc_cb(void* ptr, size_t size PKCS7_LEAK_CB_TAIL)
{
void* p = realloc(ptr, size);
void* p;
PKCS7_LEAK_CB_UNUSED
p = realloc(ptr, size);
/* realloc(NULL, n) is an allocation; realloc(p, n) replaces one. */
if (ptr == NULL && p != NULL)
pkcs7_leak_live++;

View File

@ -49,12 +49,14 @@
* variants in the same order. */
#if defined(WOLFSSL_SLHDSA_PARAM_128S)
#define TEST_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE128S
#define TEST_SLHDSA_DEFAULT_CAT1
#define TEST_SLHDSA_DEFAULT_SIG_LEN WC_SLHDSA_SHAKE128S_SIG_LEN
#define TEST_SLHDSA_DEFAULT_PRIV_LEN WC_SLHDSA_SHAKE128S_PRIV_LEN
#define TEST_SLHDSA_DEFAULT_PUB_LEN WC_SLHDSA_SHAKE128S_PUB_LEN
#define TEST_SLHDSA_DEFAULT_SEED_LEN WC_SLHDSA_SHAKE128S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_128F)
#define TEST_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE128F
#define TEST_SLHDSA_DEFAULT_CAT1
#define TEST_SLHDSA_DEFAULT_SIG_LEN WC_SLHDSA_SHAKE128F_SIG_LEN
#define TEST_SLHDSA_DEFAULT_PRIV_LEN WC_SLHDSA_SHAKE128F_PRIV_LEN
#define TEST_SLHDSA_DEFAULT_PUB_LEN WC_SLHDSA_SHAKE128F_PUB_LEN
@ -85,12 +87,14 @@
#define TEST_SLHDSA_DEFAULT_SEED_LEN WC_SLHDSA_SHAKE256F_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
#define TEST_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_128S
#define TEST_SLHDSA_DEFAULT_CAT1
#define TEST_SLHDSA_DEFAULT_SIG_LEN WC_SLHDSA_SHA2_128S_SIG_LEN
#define TEST_SLHDSA_DEFAULT_PRIV_LEN WC_SLHDSA_SHA2_128S_PRIV_LEN
#define TEST_SLHDSA_DEFAULT_PUB_LEN WC_SLHDSA_SHA2_128S_PUB_LEN
#define TEST_SLHDSA_DEFAULT_SEED_LEN WC_SLHDSA_SHA2_128S_SEED_LEN
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
#define TEST_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_128F
#define TEST_SLHDSA_DEFAULT_CAT1
#define TEST_SLHDSA_DEFAULT_SIG_LEN WC_SLHDSA_SHA2_128F_SIG_LEN
#define TEST_SLHDSA_DEFAULT_PRIV_LEN WC_SLHDSA_SHA2_128F_PRIV_LEN
#define TEST_SLHDSA_DEFAULT_PUB_LEN WC_SLHDSA_SHA2_128F_PUB_LEN
@ -120,6 +124,31 @@
#define TEST_SLHDSA_DEFAULT_PUB_LEN WC_SLHDSA_SHA2_256F_PUB_LEN
#define TEST_SLHDSA_DEFAULT_SEED_LEN WC_SLHDSA_SHA2_256F_SEED_LEN
#endif
/* FIPS 205 sec. 10.2.2 allows SHA-256 and SHAKE128 only at category 1, so a
* build whose default parameter set is larger needs a stronger pre-hash. */
/* The hash, its digest size, the last byte of its FIPS 205 Algorithm 23 OID
* and the call that produces the digest are chosen together here, so they
* cannot disagree. */
#ifdef TEST_SLHDSA_DEFAULT_CAT1
#define TEST_SLHDSA_PH WC_HASH_TYPE_SHA256
#define TEST_SLHDSA_PH_SZ 32
#define TEST_SLHDSA_PH_OID 0x01
#define TEST_SLHDSA_PH_HASH(in, inSz, out) \
wc_Sha256Hash((in), (inSz), (out))
#elif defined(WOLFSSL_SHA512)
#define TEST_SLHDSA_PH WC_HASH_TYPE_SHA512
#define TEST_SLHDSA_PH_SZ 64
#define TEST_SLHDSA_PH_OID 0x03
#define TEST_SLHDSA_PH_HASH(in, inSz, out) \
wc_Sha512Hash((in), (inSz), (out))
#else
#define TEST_SLHDSA_PH WC_HASH_TYPE_SHAKE256
#define TEST_SLHDSA_PH_SZ 64
#define TEST_SLHDSA_PH_OID 0x0C
#define TEST_SLHDSA_PH_HASH(in, inSz, out) \
wc_Shake256Hash((in), (inSz), (out), TEST_SLHDSA_PH_SZ)
#endif
#endif /* WOLFSSL_HAVE_SLHDSA */
@ -1026,7 +1055,9 @@ int test_wc_slhdsa_sign_hash(void)
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
SlhDsaKey key;
WC_RNG rng;
byte hash[64];
/* 64 for the SHA-512 and SHAKE256 cases, plus one for the deliberate
* too-long case. */
byte hash[65];
byte* sig = NULL;
word32 sigLen;
word32 expSigLen;
@ -1046,83 +1077,89 @@ 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. Use 32-byte hash length so the
* NULL check trips before the digest-length check (HashSLH-DSA expects
* SHA-256 digest = 32 bytes). */
/* Test SignHash NULL parameter handling. Use the right digest length so
* the NULL check trips before the length check. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHash(NULL, ctx, sizeof(ctx), hash,
32, WC_HASH_TYPE_SHA256, sig, &sigLen, &rng),
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, &rng),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), NULL,
32, WC_HASH_TYPE_SHA256, sig, &sigLen, &rng),
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, &rng),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
32, WC_HASH_TYPE_SHA256, NULL, &sigLen, &rng),
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, NULL, &sigLen, &rng),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
32, WC_HASH_TYPE_SHA256, sig, NULL, &rng),
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, NULL, &rng),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
32, WC_HASH_TYPE_SHA256, sig, &sigLen, NULL),
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, 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),
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ - 1, TEST_SLHDSA_PH, 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),
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ + 1, TEST_SLHDSA_PH, 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),
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, &rng), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ - 1, TEST_SLHDSA_PH, 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),
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ + 1, TEST_SLHDSA_PH, sig, sigLen),
WC_NO_ERR_TRACE(BAD_LENGTH_E));
/* Verify rejects the no-pre-hash sentinel too. Done while sigLen is
* still a real length, or the length check fires first and hides it. */
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, WC_HASH_TYPE_NONE, sig, sigLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* Unsupported hashType (FIPS 205 doesn't list WC_HASH_TYPE_NONE) hits
* the default branch of slhdsakey_validate_prehash. */
/* WC_HASH_TYPE_NONE means "no pre-hash", so it is never valid here. */
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));
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, WC_HASH_TYPE_NONE, sig, &sigLen, &rng),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* Test SignHash with SHA-256. */
/* Round trip with the parameter set's own pre-hash. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, sig, &sigLen, &rng), 0);
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, &rng), 0);
ExpectIntEQ(sigLen, expSigLen);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, sig, sigLen), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, sigLen), 0);
/* Test VerifyHash NULL parameter handling. */
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(NULL, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), NULL, 32,
WC_HASH_TYPE_SHA256, sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, NULL, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(NULL, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ,
TEST_SLHDSA_PH, sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), NULL,
TEST_SLHDSA_PH_SZ,
TEST_SLHDSA_PH, sig, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ,
TEST_SLHDSA_PH, NULL, sigLen), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* Test VerifyHash with wrong hash. */
hash[0] ^= 0xFF;
ExpectIntNE(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, sig, sigLen), 0);
ExpectIntNE(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, sigLen), 0);
hash[0] ^= 0xFF;
/* Test SignHashDeterministic. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(NULL, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen),
hash, TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(&key, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, sig, sigLen), 0);
hash, TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, sigLen), 0);
/* Test SignHashWithRandom. */
{
@ -1131,15 +1168,15 @@ int test_wc_slhdsa_sign_hash(void)
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(NULL, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, addRnd),
hash, TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, addRnd),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, NULL),
hash, TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, NULL),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_SlhDsaKey_SignHashWithRandom(&key, ctx, sizeof(ctx),
hash, 32, WC_HASH_TYPE_SHA256, sig, &sigLen, addRnd), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHA256, sig, sigLen), 0);
hash, TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, &sigLen, addRnd), 0);
ExpectIntEQ(wc_SlhDsaKey_VerifyHash(&key, ctx, sizeof(ctx), hash,
TEST_SLHDSA_PH_SZ, TEST_SLHDSA_PH, sig, sigLen), 0);
}
#ifdef WOLFSSL_SHA512
@ -1155,8 +1192,8 @@ int test_wc_slhdsa_sign_hash(void)
WC_NO_ERR_TRACE(BAD_LENGTH_E));
#endif
#ifdef WOLFSSL_SHAKE128
/* SHAKE128 PHM is fixed at 256 bits per FIPS 205 Section 10.2.2. */
#if defined(WOLFSSL_SHAKE128) && defined(TEST_SLHDSA_DEFAULT_CAT1)
/* SHAKE128 digest is fixed at 256 bits, and is category 1 only. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ExpectIntEQ(wc_SlhDsaKey_SignHash(&key, ctx, sizeof(ctx), hash, 32,
WC_HASH_TYPE_SHAKE128, sig, &sigLen, &rng), 0);
@ -1277,26 +1314,29 @@ int test_wc_slhdsa_sign_msg(void)
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. */
/* Equivalence cross-check: build M' = 0x01 || ctxSz || OID || digest
* externally, sign it via SignMsgDeterministic, and verify via VerifyHash
* with the same digest. Both paths must agree. The OID must match the
* pre-hash the parameter set allows. */
{
static const byte sha256_oid[] = {
/* FIPS 205 Algorithm 23 OID for TEST_SLHDSA_PH. */
static const byte ph_oid[] = {
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01
0x04, 0x02, TEST_SLHDSA_PH_OID
};
static const byte orig[] = "Hello World!";
byte digest[WC_SHA256_DIGEST_SIZE];
byte built_mprime[2 + sizeof(sha256_oid) + WC_SHA256_DIGEST_SIZE];
byte digest[TEST_SLHDSA_PH_SZ];
byte built_mprime[2 + sizeof(ph_oid) + TEST_SLHDSA_PH_SZ];
word32 idx = 0;
word32 sigLen2;
ExpectIntEQ(wc_Sha256Hash(orig, (word32)sizeof(orig) - 1, digest), 0);
ExpectIntEQ(TEST_SLHDSA_PH_HASH(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, ph_oid, sizeof(ph_oid));
idx += (word32)sizeof(ph_oid);
XMEMCPY(built_mprime + idx, digest, sizeof(digest));
idx += (word32)sizeof(digest);
@ -1306,7 +1346,7 @@ int test_wc_slhdsa_sign_msg(void)
/* 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);
sizeof(digest), TEST_SLHDSA_PH, sig, sigLen), 0);
/* And the deterministic HashSLH-DSA path must produce the SAME
* signature bytes (this is the strongest interop check). */
@ -1316,7 +1356,7 @@ int test_wc_slhdsa_sign_msg(void)
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(sig2);
ExpectIntEQ(wc_SlhDsaKey_SignHashDeterministic(&key, NULL, 0,
digest, sizeof(digest), WC_HASH_TYPE_SHA256, sig2,
digest, sizeof(digest), TEST_SLHDSA_PH, sig2,
&sigLen2), 0);
ExpectIntEQ(sigLen2, sigLen);
ExpectIntEQ(XMEMCMP(sig2, sig, sigLen), 0);

View File

@ -14214,16 +14214,27 @@ void bench_slhdsa(int param)
);
bench_stats_asym_finish(name, len, "vrfy-msg", 0, count, start, ret);
#ifndef NO_SHA256
#if !defined(NO_SHA256) && defined(WOLFSSL_SHA512)
/* 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. */
* and verify the digest. SHA-256 is only strong enough for category 1,
* so larger parameter sets pre-hash with SHA-512, which is why both are
* required here. */
{
byte digest[WC_SHA256_DIGEST_SIZE];
byte digest[WC_SHA512_DIGEST_SIZE];
word32 digestSz;
enum wc_HashType phType;
ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest);
if (key->params->n == WC_SLHDSA_N_128) {
phType = WC_HASH_TYPE_SHA256;
digestSz = WC_SHA256_DIGEST_SIZE;
ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest);
}
else {
phType = WC_HASH_TYPE_SHA512;
digestSz = WC_SHA512_DIGEST_SIZE;
ret = wc_Sha512Hash(msg, (word32)sizeof(msg), digest);
}
if (ret != 0) {
goto exit;
}
@ -14233,7 +14244,7 @@ void bench_slhdsa(int param)
do {
sigLen = WC_SLHDSA_MAX_SIG_LEN;
ret = wc_SlhDsaKey_SignHashDeterministic(key, ctx, 0, digest,
(word32)sizeof(digest), WC_HASH_TYPE_SHA256, sig, &sigLen);
digestSz, phType, sig, &sigLen);
if (ret != 0) {
goto exit;
}
@ -14250,7 +14261,7 @@ void bench_slhdsa(int param)
bench_stats_start(&count, &start);
do {
ret = wc_SlhDsaKey_VerifyHash(key_vfy, ctx, 0, digest,
(word32)sizeof(digest), WC_HASH_TYPE_SHA256, sig, sigLen);
digestSz, phType, sig, sigLen);
if (ret != 0) {
goto exit;
}
@ -14264,7 +14275,8 @@ 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. */
/* Reached without SHA-256, or without the SHA-512 the larger parameter
* sets need. SHAKE256 is strong enough for every parameter set. */
{
byte digest[WC_SHA3_512_DIGEST_SIZE];

View File

@ -7997,7 +7997,46 @@ static const byte slhdsakey_oid_sha3_512[] = {
#endif
#endif
/* Validate the caller-supplied pre-hashed digest length and look up the
/* The CMVP PQC validation worksheet requires the pre-hash to provide classical
* security strength at least equal to the parameter set that uses it (item 2.5,
* citing FIPS 205 sec. 10.2). A hash gives half its digest size in collision
* strength, and sec. 11 ties the category to n, so both are compared in bytes:
* the pre-hash strength must be >= n. This also gives the sec. 10.2.2 rule
* that SHA-256 and SHAKE128 are category 1 only.
* Returns 0 if allowed, else BAD_FUNC_ARG. */
static int slhdsa_check_hash_for_n(enum wc_HashType hashType, byte n)
{
byte strength;
switch ((int)hashType) {
case WC_HASH_TYPE_SHA256:
case WC_HASH_TYPE_SHA512_256:
case WC_HASH_TYPE_SHA3_256:
case WC_HASH_TYPE_SHAKE128:
strength = WC_SLHDSA_N_128;
break;
case WC_HASH_TYPE_SHA384:
case WC_HASH_TYPE_SHA3_384:
strength = WC_SLHDSA_N_192;
break;
case WC_HASH_TYPE_SHA512:
case WC_HASH_TYPE_SHA3_512:
case WC_HASH_TYPE_SHAKE256:
strength = WC_SLHDSA_N_256;
break;
default:
/* Includes SHA-224 and friends, which are below every category. */
return BAD_FUNC_ARG;
}
return (strength >= n) ? 0 : BAD_FUNC_ARG;
}
/* Both callers run slhdsa_check_hash_for_n() first, so the entries below that
* are too weak for any parameter set are already rejected by the time this
* runs. The table stays complete so the two concerns remain separate.
*
* 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
@ -8223,6 +8262,11 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx,
/* Alg 23, Step 6: Return error. */
ret = BAD_FUNC_ARG;
}
/* Covers SignHashDeterministic and SignHashWithRandom, which reach here
* without passing the check in wc_SlhDsaKey_SignHash. */
if (ret == 0) {
ret = slhdsa_check_hash_for_n(hashType, key->params->n);
}
if (ret == 0) {
/* Alg 23, Steps 8-23: Validate caller-supplied pre-hashed digest length
* and select OID for the chosen hash algorithm. */
@ -8438,11 +8482,9 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
int ret = 0;
byte addRnd[SLHDSA_MAX_N];
/* Validate parameters before generating random.
* hashSz / hashType validation lives in the internal worker and therefore
* runs after wc_RNG_GenerateBlock. A call with a bad hashSz/hashType will
* waste n bytes of DRBG output before the error is reported (similar to
* ML-DSA pre-hash handling). */
/* Validate parameters before generating random. hashType is checked
* here; only hashSz is left to the internal worker, so a bad hashSz still
* costs n bytes of DRBG output before the error comes back. */
if ((key == NULL) || (key->params == NULL) ||
((ctx == NULL) && (ctxSz > 0)) || (hash == NULL) || (sig == NULL) ||
(sigSz == NULL) || (rng == NULL)) {
@ -8456,10 +8498,11 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
else if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) {
ret = MISSING_KEY;
}
/* First sanity check on hashType; the downstream prehash validator does
* the detailed check for the actual type. */
else if ((word32)hashType > (word32)WC_HASH_TYPE_MAX) {
ret = BAD_FUNC_ARG;
/* Before the callback dispatch, so a device cannot sign a combination
* this build rejects on verify. Also rejects WC_HASH_TYPE_NONE and any
* out-of-range value, which the gate does not list. */
else {
ret = slhdsa_check_hash_for_n(hashType, key->params->n);
}
#ifdef WOLF_CRYPTO_CB
@ -8588,10 +8631,8 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
else if ((key->flags & WC_SLHDSA_FLAG_PUBLIC) == 0) {
ret = MISSING_KEY;
}
/* First sanity check on hashType; the downstream prehash validator does
* the detailed check for the actual type. */
else if ((word32)hashType > (word32)WC_HASH_TYPE_MAX) {
ret = BAD_FUNC_ARG;
else {
ret = slhdsa_check_hash_for_n(hashType, key->params->n);
}
#ifdef WOLF_CRYPTO_CB

View File

@ -30142,7 +30142,9 @@ static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key)
enum wc_HashType hash[] = { WC_HASH_TYPE_SHA256 };
#else
int mgf[] = {
#ifndef NO_SHA
/* FIPS 186-5 sec 5.4 disallows SHA-1 for RSA signature generation; the module
* rejects it at the RSA-PSS signing service. */
#if !defined(NO_SHA) && FIPS_VERSION3_LT(7,0,0)
WC_MGF1SHA1,
#endif
#ifdef WOLFSSL_SHA224
@ -30159,7 +30161,7 @@ static wc_test_ret_t rsa_pss_test(WC_RNG* rng, RsaKey* key)
#endif
};
enum wc_HashType hash[] = {
#ifndef NO_SHA
#if !defined(NO_SHA) && FIPS_VERSION3_LT(7,0,0)
WC_HASH_TYPE_SHA,
#endif
#ifdef WOLFSSL_SHA224
@ -49601,6 +49603,24 @@ static wc_test_ret_t ed25519ctx_test(void)
ret = wc_ed25519_import_private_key(sKeyCtx, ED25519_KEY_SIZE, pKeyCtx,
sizeof(pKeyCtx), &key);
#ifdef WC_FIPS_ED25519CTX_NOT_APPROVED
/* The FIPS module does not approve Ed25519ctx, so it must refuse to
* sign. Keyed off the module's own macro because some v7.0.0 builds
* report the same version but still sign. */
if (ret == 0) {
ret = wc_ed25519ctx_sign_msg(msgCtx, sizeof(msgCtx), out, &outlen, &key,
contextCtx, sizeof(contextCtx));
if (ret == WC_NO_ERR_TRACE(SIG_TYPE_E))
ret = 0;
else if (ret == 0)
ret = WC_TEST_RET_ENC_NC;
}
(void)sigCtx1;
(void)sigCtx2;
#ifdef HAVE_ED25519_VERIFY
(void)verify;
#endif
#else
if (ret == 0)
ret = wc_ed25519ctx_sign_msg(msgCtx, sizeof(msgCtx), out, &outlen, &key,
contextCtx, sizeof(contextCtx));
@ -49631,6 +49651,7 @@ static wc_test_ret_t ed25519ctx_test(void)
if (ret == 0 && verify != 1)
ret = WC_TEST_RET_ENC_NC;
#endif
#endif /* WC_FIPS_ED25519CTX_NOT_APPROVED */
wc_ed25519_free(&key);
@ -52520,8 +52541,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed448_test(void)
}
#endif /* HAVE_ED448 */
/* KATs that feed in fixed NIST seeds. A FIPS build must make its own
* randomness, so those calls are rejected there. Non-FIPS only. */
#if !defined(HAVE_FIPS)
#define WOLFSSL_TEST_PQC_SEED_KAT
#endif
#ifdef WOLFSSL_HAVE_MLKEM
#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512)
#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512) && \
defined(WOLFSSL_TEST_PQC_SEED_KAT)
static wc_test_ret_t mlkem512_kat(void)
{
wc_test_ret_t ret;
@ -53615,7 +53643,8 @@ out:
}
#endif /* !WOLFSSL_NO_KYBER512 && !WOLFSSL_NO_ML_KEM_512 */
#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768)
#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768) && \
defined(WOLFSSL_TEST_PQC_SEED_KAT)
static wc_test_ret_t mlkem768_kat(void)
{
wc_test_ret_t ret;
@ -55055,7 +55084,8 @@ out:
}
#endif /* !WOLFSSL_NO_KYBER768 && !WOLFSSL_NO_ML_KEM_768 */
#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024)
#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024) && \
defined(WOLFSSL_TEST_PQC_SEED_KAT)
static wc_test_ret_t mlkem1024_kat(void)
{
wc_test_ret_t ret;
@ -57155,17 +57185,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mlkem_test(void)
#endif
}
#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512)
#if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512) && \
defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = mlkem512_kat();
if (ret != 0)
goto out;
#endif
#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768)
#if !defined(WOLFSSL_NO_KYBER768) && !defined(WOLFSSL_NO_ML_KEM_768) && \
defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = mlkem768_kat();
if (ret != 0)
goto out;
#endif
#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024)
#if !defined(WOLFSSL_NO_KYBER1024) && !defined(WOLFSSL_NO_ML_KEM_1024) && \
defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = mlkem1024_kat();
if (ret != 0)
goto out;
@ -63105,7 +63138,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test_verify_only(void)
#define WOLFSSL_SLHDSA_VERIFY_ONLY
#endif
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
/* KeyGen KAT: deterministic key generation cross-validated against NIST CAVP
* vectors. Verifies that MakeKeyWithRandom produces the expected sk and pk
* for a given parameter set. */
@ -63173,7 +63206,7 @@ out:
WC_FREE_VAR_EX(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY && WOLFSSL_TEST_PQC_SEED_KAT */
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
@ -63251,9 +63284,16 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
/* HashSLH-DSA takes the caller's pre-hashed digest as input. */
{
/* FIPS 205 sec. 10.2.2 approves SHA-256 only for category 1, so the
* larger SHA-2 parameter sets pre-hash with SHA-512. */
#ifdef WOLFSSL_SLHDSA_SHA2
enum wc_HashType phType = SLHDSA_IS_SHA2(param) ?
WC_HASH_TYPE_SHA256 : WC_HASH_TYPE_SHAKE256;
enum wc_HashType phType = !SLHDSA_IS_SHA2(param) ? WC_HASH_TYPE_SHAKE256
: (key->params->n == WC_SLHDSA_N_128) ? WC_HASH_TYPE_SHA256
#ifdef WOLFSSL_SHA512
: WC_HASH_TYPE_SHA512;
#else
: WC_HASH_TYPE_SHAKE256;
#endif
#else
enum wc_HashType phType = WC_HASH_TYPE_SHAKE256;
#endif
@ -63265,6 +63305,12 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest);
digestLen = WC_SHA256_DIGEST_SIZE;
}
#ifdef WOLFSSL_SHA512
else if (phType == WC_HASH_TYPE_SHA512) {
ret = wc_Sha512Hash(msg, (word32)sizeof(msg), digest);
digestLen = WC_SHA512_DIGEST_SIZE;
}
#endif
else
#endif
{
@ -63290,9 +63336,10 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
/* Additional pre-hash test: SHA-384 exercises a different OID path */
/* SHA-384 exercises a different OID path. Its 192-bit strength covers
* categories 1 and 3 only. */
#ifdef WOLFSSL_SHA384
{
if (key->params->n <= WC_SLHDSA_N_192) {
byte digest384[WC_SHA384_DIGEST_SIZE];
ret = wc_Sha384Hash(msg, (word32)sizeof(msg), digest384);
@ -63352,6 +63399,145 @@ out:
return ret;
}
/* Must list exactly the parameter sets the call sites below use, or the
* build either fails to compile or warns about an unused function. */
#if defined(WOLFSSL_SLHDSA_PARAM_192S) || \
defined(WOLFSSL_SLHDSA_PARAM_192F) || \
defined(WOLFSSL_SLHDSA_PARAM_256S) || \
defined(WOLFSSL_SLHDSA_PARAM_256F) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
/* Negative test: HashSLH-DSA must reject a pre-hash whose collision resistance
* is below the parameter set's claimed security strength (FIPS 205 sec.
* 10.2.2 with sec. 11). Asserts sigGen and sigVer both
* reject. */
static wc_test_ret_t slhdsa_hash_paramset_rejection_test(enum SlhDsaParam param)
{
int ret = 0;
wc_test_ret_t testRet = 0;
WC_RNG rng;
WC_DECLARE_VAR(key, SlhDsaKey, 1, HEAP_HINT);
/* Signature is about 49KB, too big for a small stack. */
WC_DECLARE_VAR(sig, byte, WC_SLHDSA_MAX_SIG_LEN, HEAP_HINT);
word32 sigLen;
/* Content does not matter, only that the length matches the hash. */
byte msg[48];
byte addRnd[WC_SLHDSA_MAX_SEED];
/* Rejected by every parameter set above category 1. */
enum wc_HashType badHash = WC_HASH_TYPE_SHA256;
int rngInited = 0;
int keyInited = 0;
WC_ALLOC_VAR_EX(key, SlhDsaKey, 1, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER, return WC_TEST_RET_ENC_EC(MEMORY_E));
/* Goes to out, not return, so a failure here still frees key. */
WC_ALLOC_VAR_EX(sig, byte, WC_SLHDSA_MAX_SIG_LEN, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER,
testRet = WC_TEST_RET_ENC_EC(MEMORY_E); goto out);
XMEMSET(key, 0, sizeof(*key));
XMEMSET(msg, 0xAB, sizeof(msg));
XMEMSET(addRnd, 0x55, sizeof(addRnd));
#ifndef HAVE_FIPS
ret = wc_InitRng_ex(&rng, HEAP_HINT, devId);
#else
ret = wc_InitRng(&rng);
#endif
if (ret != 0) {
testRet = WC_TEST_RET_ENC_EC(ret);
goto out;
}
rngInited = 1;
ret = wc_SlhDsaKey_Init(key, param, NULL, devId);
if (ret != 0) {
testRet = WC_TEST_RET_ENC_EC(ret);
goto out;
}
keyInited = 1;
ret = wc_SlhDsaKey_MakeKey(key, &rng);
if (ret != 0) {
testRet = WC_TEST_RET_ENC_EC(ret);
goto out;
}
/* SHA-256 is allowed at category 1, so nothing to reject. */
if (key->params->n == WC_SLHDSA_N_128) {
goto out;
}
/* sigGen with too-weak PH must be REJECTED. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_SignHash(key, NULL, 0, msg, 32,
badHash, sig, &sigLen, &rng);
PRIVATE_KEY_LOCK();
/* Must be this rejection, not some earlier error. */
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
testRet = WC_TEST_RET_ENC_NC;
goto out;
}
/* These two reach the check by a different route, through
* slhdsakey_signhash_external, so assert them separately. */
sigLen = WC_SLHDSA_MAX_SIG_LEN;
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_SignHashDeterministic(key, NULL, 0, msg, 32,
badHash, sig, &sigLen);
PRIVATE_KEY_LOCK();
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
testRet = WC_TEST_RET_ENC_NC;
goto out;
}
sigLen = WC_SLHDSA_MAX_SIG_LEN;
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_SignHashWithRandom(key, NULL, 0, msg, 32,
badHash, sig, &sigLen, addRnd);
PRIVATE_KEY_LOCK();
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
testRet = WC_TEST_RET_ENC_NC;
goto out;
}
/* Verify must reject it too. Use the real signature length, or the
* length check fires first and hides the result. */
sigLen = key->params->sigLen;
ret = wc_SlhDsaKey_VerifyHash(key, NULL, 0, msg, 32,
badHash, sig, sigLen);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
testRet = WC_TEST_RET_ENC_NC;
goto out;
}
#ifdef WOLFSSL_SHA384
/* SHA-384 is 192-bit, so category 5 must reject it as well. */
if (key->params->n == WC_SLHDSA_N_256) {
ret = wc_SlhDsaKey_VerifyHash(key, NULL, 0, msg, 48,
WC_HASH_TYPE_SHA384, sig, sigLen);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
testRet = WC_TEST_RET_ENC_NC;
goto out;
}
}
#endif
out:
if (keyInited) {
wc_SlhDsaKey_Free(key);
}
if (rngInited) {
wc_FreeRng(&rng);
}
WC_FREE_VAR_EX(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
WC_FREE_VAR_EX(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
return testRet;
}
#endif /* any category 3/5 param set */
#endif
/* True iff slhdsa_test() actually emits at least one `goto out;` /
@ -63538,7 +63724,7 @@ wc_test_ret_t slhdsa_test(void)
int ret = 0;
#ifdef WOLFSSL_SLHDSA_PARAM_128S
WC_DECLARE_VAR(key_vfy, SlhDsaKey, 1, HEAP_HINT);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_DECLARE_VAR(key, SlhDsaKey, 1, HEAP_HINT);
static const byte sk_seed_shake128s[] = {
0x17, 0x3D, 0x04, 0xC9, 0x38, 0xC1, 0xC3, 0x6B,
@ -63569,7 +63755,7 @@ wc_test_ret_t slhdsa_test(void)
0x82, 0x6e, 0x97, 0xbc, 0xb0, 0x1b, 0x78, 0x7b,
0xc6, 0xb5, 0xa7, 0xbb, 0xe3, 0x7e, 0xb4, 0xa8
};
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_DECLARE_VAR(sk, byte, WC_SLHDSA_MAX_PRIV_LEN, HEAP_HINT);
WC_DECLARE_VAR(pk, byte, WC_SLHDSA_MAX_PUB_LEN, HEAP_HINT);
word32 outLen;
@ -64563,14 +64749,14 @@ wc_test_ret_t slhdsa_test(void)
0xc0, 0x48, 0xd0, 0x63, 0x82, 0x20, 0x2b, 0x06,
0xe8, 0x21, 0xf8, 0xcd, 0x56, 0xc5, 0xc7, 0x31,
};
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_DECLARE_VAR(sig, byte, sizeof(sig_shake128s), HEAP_HINT);
#endif
WC_ALLOC_VAR_EX(key_vfy, SlhDsaKey, 1, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER, ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out));
XMEMSET(key_vfy, 0, sizeof(*key_vfy));
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
WC_ALLOC_VAR_EX(key, SlhDsaKey, 1, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER, ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out));
XMEMSET(key, 0, sizeof(*key));
@ -64584,7 +64770,7 @@ wc_test_ret_t slhdsa_test(void)
/* // NOLINTEND(bugprone-sizeof-expression) */
#endif
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
ret = wc_SlhDsaKey_Init(key, SLHDSA_SHAKE128S, NULL, devId);
if (ret != 0) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
@ -64644,7 +64830,7 @@ wc_test_ret_t slhdsa_test(void)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
outLen = (word32)sizeof(sig_shake128s);
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_SignWithRandom(key, ctx, 0, msg, (word32)sizeof(msg),
@ -64670,7 +64856,7 @@ wc_test_ret_t slhdsa_test(void)
* These verify that deterministic key generation produces the exact pk/sk
* that NIST expects. Covers both SHA-256 (cat 1, n=16) and SHA-512
* (cat 3 n=24, cat 5 n=32) code paths. */
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128S
{
/* NIST CAVP SLH-DSA-SHA2-128s keyGen vector (tgId=1, tcId=1).
@ -65260,6 +65446,12 @@ wc_test_ret_t slhdsa_test(void)
}
#endif
/* Below here nothing feeds in a seed, so it must stay outside the gate
* above. Putting it inside dropped these tests from every FIPS build. */
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY && WOLFSSL_TEST_PQC_SEED_KAT */
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#ifdef WOLFSSL_SLHDSA_PARAM_128S
ret = slhdsa_test_param(SLHDSA_SHAKE128S);
if (ret != 0) {
@ -65345,6 +65537,45 @@ wc_test_ret_t slhdsa_test(void)
}
#endif
/* FIPS 205 sec. 10.2.2 allows SHA-256 only at category 1, so every larger
* parameter set must reject it. Each is tested on its own; an #elif chain
* would stop at the first one the build enables. */
#define SLHDSA_REJECT_CASE(param) \
if (ret == 0) { \
ret = slhdsa_hash_paramset_rejection_test(param); \
if (ret != 0) { \
wc_test_render_error_message(#param " (hash-paramset reject)", \
0); \
goto out; \
} \
}
#ifdef WOLFSSL_SLHDSA_PARAM_192S
SLHDSA_REJECT_CASE(SLHDSA_SHAKE192S)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
SLHDSA_REJECT_CASE(SLHDSA_SHAKE192F)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
SLHDSA_REJECT_CASE(SLHDSA_SHAKE256S)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
SLHDSA_REJECT_CASE(SLHDSA_SHAKE256F)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S
SLHDSA_REJECT_CASE(SLHDSA_SHA2_192S)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192F
SLHDSA_REJECT_CASE(SLHDSA_SHA2_192F)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256S
SLHDSA_REJECT_CASE(SLHDSA_SHA2_256S)
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_256F
SLHDSA_REJECT_CASE(SLHDSA_SHA2_256F)
#endif
#undef SLHDSA_REJECT_CASE
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
#if defined(WOLF_PRIVATE_KEY_ID) && \
@ -65378,7 +65609,7 @@ out:
/* key, sig, sk, pk are declared inside #ifdef WOLFSSL_SLHDSA_PARAM_128S
* (alongside the SHAKE-128s test data) so they only exist when 128S is
* built. Their cleanup must match. */
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_TEST_PQC_SEED_KAT)
#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC
if (key)
#endif