Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/wolfprovider/alg_funcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ void* wp_mldsa_get_key(wp_MlDsa* mldsa);
int wp_mldsa_get_sig_size(const wp_MlDsa* mldsa);
int wp_mldsa_has_private(const wp_MlDsa* mldsa);
int wp_mldsa_get_level(wp_MlDsa* mldsa);
int wp_mldsa_get_security_bits(const wp_MlDsa* mldsa);

/* Internal SLH-DSA types and functions. */
typedef struct wp_SlhDsa wp_SlhDsa;
Expand Down
16 changes: 16 additions & 0 deletions src/wp_mldsa_kmgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,22 @@ int wp_mldsa_get_level(wp_MlDsa* mldsa)
return level;
}

/**
* Get the security strength in bits of the key's parameter set.
*
* @param [in] mldsa ML-DSA key object.
* @return Security bits, or 0 when not available.
*/
int wp_mldsa_get_security_bits(const wp_MlDsa* mldsa)
{
int bits = 0;

if ((mldsa != NULL) && (mldsa->data != NULL)) {
bits = mldsa->data->securityBits;
}
return bits;
}

/**
* Get the maximum signature size for the key.
*
Expand Down
13 changes: 11 additions & 2 deletions src/wp_mldsa_sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ static int wp_mldsa_stream_verify_final(wp_MlDsaSigCtx* ctx,
/* Digests permitted for FIPS 204 HashML-DSA. Restricting to the SHA2/SHA3
* families keeps the generic name mapper from admitting legacy/undersized
* digests (MD5, SHA-1, SHA-512/224) as a HashML-DSA pre-hash. */
static int wp_mldsa_hash_allowed(enum wc_HashType hashType)
static int wp_mldsa_hash_allowed(enum wc_HashType hashType, int secBits)
{
int allowed = 0;

Expand All @@ -783,6 +783,14 @@ static int wp_mldsa_hash_allowed(enum wc_HashType hashType)
(hashType == WC_HASH_TYPE_SHA3_512)) {
allowed = 1;
}
#ifndef WOLFSSL_MLDSA_ALLOW_WEAK_PREHASH
/* FIPS 204 sec 5.4: collision strength must cover the parameter set. */
if (allowed && (wc_HashGetDigestSize(hashType) * 8 / 2 < secBits)) {
allowed = 0;
}
#else
(void)secBits;
#endif /* !WOLFSSL_MLDSA_ALLOW_WEAK_PREHASH */
return allowed;
}

Expand All @@ -796,7 +804,8 @@ static int wp_mldsa_setup_prehash(wp_MlDsaSigCtx* ctx, const char* mdName)
OSSL_LIB_CTX* libCtx = (ctx->provCtx != NULL) ? ctx->provCtx->libCtx : NULL;

ctx->hashType = wp_name_to_wc_hash_type(libCtx, mdName, NULL);
if (!wp_mldsa_hash_allowed(ctx->hashType)) {
if (!wp_mldsa_hash_allowed(ctx->hashType,
wp_mldsa_get_security_bits(ctx->mldsa))) {
ctx->hashType = WC_HASH_TYPE_NONE;
ok = 0;
}
Expand Down
121 changes: 71 additions & 50 deletions test/test_mldsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -743,66 +743,87 @@ int test_mldsa_get_params(void* data)
return err;
}

/* DigestSignInit with non-empty mdName must fail (ML-DSA is pure). */
/* HashML-DSA digest choices and whether init must accept them. */
typedef struct mldsa_prehash_case {
const char* keyName;
const char* mdName;
int ok;
} mldsa_prehash_case;

static const mldsa_prehash_case mldsa_prehash_cases[] = {
{ "ML-DSA-44", "SHA-256", 1 },
#ifdef WP_HAVE_SHA384
{ "ML-DSA-65", "SHA-384", 1 },
#endif /* WP_HAVE_SHA384 */
#ifdef WP_HAVE_SHA512
{ "ML-DSA-87", "SHA-512", 1 },
#endif /* WP_HAVE_SHA512 */
{ "ML-DSA-65", "SHA-1", 0 },
#ifndef WOLFSSL_MLDSA_ALLOW_WEAK_PREHASH
/* Digest weaker than the parameter set (FIPS 204 sec 5.4). */
{ "ML-DSA-65", "SHA-256", 0 },
{ "ML-DSA-87", "SHA-384", 0 },
#endif /* !WOLFSSL_MLDSA_ALLOW_WEAK_PREHASH */
};
#define MLDSA_PREHASH_CASE_COUNT \
(sizeof(mldsa_prehash_cases) / sizeof(mldsa_prehash_cases[0]))

/* A digest name selects HashML-DSA: allowed digests round-trip, others fail
* at init. */
int test_mldsa_digest_sign_prehash(void* data)
{
int err = 0;
size_t i;
EVP_PKEY* k = NULL;
EVP_MD_CTX* mdctx = NULL;
unsigned char sig[5000];
size_t sigLen = sizeof(sig);
const unsigned char msg[] = "wolfProvider ML-DSA pre-hash message";
unsigned char sig[ML_DSA_LEVEL5_SIG_SIZE];
size_t sigLen = 0;
int rc;

(void)data;
PRINT_MSG("DigestSign/Verify pre-hash (HashML-DSA) round-trip");
for (i = 0; (err == 0) && (i < MLDSA_PREHASH_CASE_COUNT); i++) {
const mldsa_prehash_case* c = &mldsa_prehash_cases[i];
PRINT_MSG("HashML-DSA %s with %s", c->keyName, c->mdName);

err = mldsa_keygen("ML-DSA-65", &k);
if (err == 0) {
mdctx = EVP_MD_CTX_new();
err = (mdctx == NULL);
}
/* A digest name selects FIPS 204 HashML-DSA; signing must succeed. */
if (err == 0) {
rc = EVP_DigestSignInit_ex(mdctx, NULL, "SHA-256", wpLibCtx, NULL, k,
NULL);
err = (rc != 1);
if (err) PRINT_ERR_MSG("DigestSignInit with mdName failed");
}
if (err == 0) {
err = EVP_DigestSign(mdctx, sig, &sigLen, msg, sizeof(msg)) != 1;
if (err) PRINT_ERR_MSG("EVP_DigestSign (pre-hash) failed");
}
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
if (err == 0) {
mdctx = EVP_MD_CTX_new();
err = (mdctx == NULL);
}
if (err == 0) {
rc = EVP_DigestVerifyInit_ex(mdctx, NULL, "SHA-256", wpLibCtx, NULL, k,
NULL);
err = (rc != 1);
}
if (err == 0) {
err = EVP_DigestVerify(mdctx, sig, sigLen, msg, sizeof(msg)) != 1;
if (err) PRINT_ERR_MSG("EVP_DigestVerify (pre-hash) failed");
}
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
/* A weak/legacy digest is not allowed for HashML-DSA. */
if (err == 0) {
mdctx = EVP_MD_CTX_new();
err = (mdctx == NULL);
}
if (err == 0) {
rc = EVP_DigestSignInit_ex(mdctx, NULL, "SHA-1", wpLibCtx, NULL, k,
NULL);
err = (rc == 1);
if (err) PRINT_ERR_MSG("DigestSignInit accepted SHA-1 for ML-DSA");
err = mldsa_keygen(c->keyName, &k);
if (err == 0) {
mdctx = EVP_MD_CTX_new();
err = (mdctx == NULL);
}
if (err == 0) {
rc = EVP_DigestSignInit_ex(mdctx, NULL, c->mdName, wpLibCtx, NULL,
k, NULL);
err = ((rc == 1) != c->ok);
if (err) PRINT_ERR_MSG("DigestSignInit result unexpected");
}
if ((err == 0) && c->ok) {
sigLen = sizeof(sig);
err = EVP_DigestSign(mdctx, sig, &sigLen, mldsa_test_msg,
MLDSA_TEST_MSG_LEN) != 1;
if (err) PRINT_ERR_MSG("EVP_DigestSign (pre-hash) failed");
}
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
if (err == 0) {
mdctx = EVP_MD_CTX_new();
err = (mdctx == NULL);
}
if (err == 0) {
rc = EVP_DigestVerifyInit_ex(mdctx, NULL, c->mdName, wpLibCtx,
NULL, k, NULL);
err = ((rc == 1) != c->ok);
if (err) PRINT_ERR_MSG("DigestVerifyInit result unexpected");
}
if ((err == 0) && c->ok) {
err = EVP_DigestVerify(mdctx, sig, sigLen, mldsa_test_msg,
MLDSA_TEST_MSG_LEN) != 1;
if (err) PRINT_ERR_MSG("EVP_DigestVerify (pre-hash) failed");
}
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
EVP_PKEY_free(k);
k = NULL;
}
EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(k);
return err;
}

Expand Down
Loading