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
63 changes: 63 additions & 0 deletions doc/dox_comments/header_files/wc_mldsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ int wc_MlDsaKey_SignCtxHashWithSeed(wc_MlDsaKey* key, const byte* ctx,
\param [in] seed 32-byte seed bytes.

\sa wc_MlDsaKey_VerifyMu
\sa wc_MlDsaKey_GenMu
*/
int wc_MlDsaKey_SignMuWithSeed(wc_MlDsaKey* key, byte* sig, word32* sigLen,
const byte* mu, word32 muLen, const byte* seed);
Expand Down Expand Up @@ -582,10 +583,72 @@ int wc_MlDsaKey_VerifyCtxHash(wc_MlDsaKey* key, const byte* sig, word32 sigLen,
when an error is returned.

\sa wc_MlDsaKey_SignMuWithSeed
\sa wc_MlDsaKey_GenMu
*/
int wc_MlDsaKey_VerifyMu(wc_MlDsaKey* key, const byte* sig, word32 sigLen,
const byte* mu, word32 muLen, int* res);

/*!
\ingroup ML_DSA

\brief Computes the 64-byte external mu for a message and context:
mu = SHAKE256(tr || 0 || ctxLen || ctx || msg, 64), where
tr = SHAKE256(pk, 64) (FIPS 204 Algorithm 2 step 10 and Algorithm 7
step 6). The result is the mu taken by wc_MlDsaKey_SignMuWithSeed() and
wc_MlDsaKey_VerifyMu(), so a message can be hashed apart from the device
that holds the private key and only mu sent to it.

tr is read from the private key when the key holds one, as signing
does, otherwise it is computed from the public key. Only the pure (non
pre-hash) FIPS 204 final ML-DSA form is produced. It matches the mu used
by wc_MlDsaKey_SignCtx(), wc_MlDsaKey_SignCtxWithSeed() and
wc_MlDsaKey_VerifyCtx(). It does not match the legacy no-context
wc_MlDsaKey_Sign(), wc_MlDsaKey_SignWithSeed() and wc_MlDsaKey_Verify()
(WOLFSSL_MLDSA_NO_CTX), which compute mu as SHAKE256(tr || msg, 64) with
no 0 || ctxLen prefix for every parameter set: a signature from
wc_MlDsaKey_Sign() does not verify against GenMu's mu, even with an
empty context. FIPS 204 draft keys, which sign through that legacy
form, are rejected.

\return 0 on success.
\return BAD_FUNC_ARG if key or mu is NULL, ctx is NULL with ctxLen
non-zero, msg is NULL with msgLen non-zero, muLen is not 64, the key
has neither a public nor a private key set, or the key is a FIPS 204
draft key.
\return NOT_COMPILED_IN when built with WOLF_CRYPTO_CB_ONLY_MLDSA.

\param [in,out] key Pointer to a wc_MlDsaKey with a public or private
key.
\param [in] ctx Optional context string (NULL when ctxLen=0).
\param [in] ctxLen Length of ctx; no greater than 255.
\param [in] msg Message (may be NULL when msgLen=0).
\param [in] msgLen Length of msg in bytes.
\param [out] mu Buffer to receive mu.
\param [in] muLen Length of mu; must be 64.

_Example_
\code
wc_MlDsaKey key;
byte mu[MLDSA_MU_SZ];
int res = 0;

wc_MlDsaKey_Init(&key, NULL, INVALID_DEVID);
wc_MlDsaKey_SetParams(&key, WC_ML_DSA_65);
wc_MlDsaKey_ImportPubRaw(&key, pub, pubLen);
if (wc_MlDsaKey_GenMu(&key, ctx, ctxLen, msg, msgLen, mu,
sizeof(mu)) != 0) {
// handle error
}
// send mu to the signer, which calls wc_MlDsaKey_SignMuWithSeed()
wc_MlDsaKey_VerifyMu(&key, sig, sigLen, mu, sizeof(mu), &res);
\endcode

\sa wc_MlDsaKey_SignMuWithSeed
\sa wc_MlDsaKey_VerifyMu
*/
int wc_MlDsaKey_GenMu(wc_MlDsaKey* key, const byte* ctx, byte ctxLen,
const byte* msg, word32 msgLen, byte* mu, word32 muLen);

/*!
\ingroup ML_DSA

Expand Down
200 changes: 200 additions & 0 deletions tests/api/test_mldsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -31340,6 +31340,203 @@ int test_mldsa_verify_mu_kats(void)
return EXPECT_RESULT();
}

/* wc_MlDsaKey_GenMu: mu computed apart from the signer must give the same
* signature as signing the message directly, must verify with VerifyMu, and
* must not depend on whether tr came from the public or the private key. */
int test_mldsa_gen_mu(void)
{
EXPECT_DECLS;
#if !defined(HAVE_FIPS) && defined(WC_MLDSA_HAVE_NATIVE) && \
defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
!defined(WOLFSSL_MLDSA_NO_VERIFY) && \
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
defined(WOLFSSL_MLDSA_PUBLIC_KEY) && defined(WOLFSSL_MLDSA_PRIVATE_KEY)
wc_MlDsaKey* key = NULL;
wc_MlDsaKey* pubKey = NULL;
wc_MlDsaKey* prvKey = NULL;
WC_RNG rng;
byte* pubBuf = NULL;
byte* prvBuf = NULL;
byte* sigMsg = NULL;
byte* sigMu = NULL;
word32 pubLen = MLDSA_MAX_PUB_KEY_SIZE;
word32 prvLen = MLDSA_MAX_KEY_SIZE;
word32 sigMsgLen = MLDSA_MAX_SIG_SIZE;
word32 sigMuLen = MLDSA_MAX_SIG_SIZE;
static const byte msg[] = "external mu message";
static const byte ctx[] = "external mu context";
static const byte empty[1] = { 0 };
byte seed[MLDSA_RND_SZ];
byte mu[MLDSA_MU_SZ];
byte muPub[MLDSA_MU_SZ];
byte muPrv[MLDSA_MU_SZ];
byte muOther[MLDSA_MU_SZ];
int res = 0;

XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(seed, 0x5a, sizeof(seed));
ExpectNotNull(key = (wc_MlDsaKey*)XMALLOC(sizeof(*key), NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(pubKey = (wc_MlDsaKey*)XMALLOC(sizeof(*pubKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(prvKey = (wc_MlDsaKey*)XMALLOC(sizeof(*prvKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(pubBuf = (byte*)XMALLOC(MLDSA_MAX_PUB_KEY_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(prvBuf = (byte*)XMALLOC(MLDSA_MAX_KEY_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(sigMsg = (byte*)XMALLOC(MLDSA_MAX_SIG_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(sigMu = (byte*)XMALLOC(MLDSA_MAX_SIG_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
if (key != NULL) {
XMEMSET(key, 0, sizeof(*key));
}
if (pubKey != NULL) {
XMEMSET(pubKey, 0, sizeof(*pubKey));
}
if (prvKey != NULL) {
XMEMSET(prvKey, 0, sizeof(*prvKey));
}

ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_Init(pubKey, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_MlDsaKey_Init(prvKey, NULL, INVALID_DEVID), 0);

/* No parameters or key material yet. */
ExpectIntEQ(wc_MlDsaKey_GenMu(key, NULL, 0, msg, sizeof(msg), mu,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

#ifndef WOLFSSL_NO_ML_DSA_44
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_44), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(pubKey, WC_ML_DSA_44), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(prvKey, WC_ML_DSA_44), 0);
#elif !defined(WOLFSSL_NO_ML_DSA_65)
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_65), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(pubKey, WC_ML_DSA_65), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(prvKey, WC_ML_DSA_65), 0);
#else
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_87), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(pubKey, WC_ML_DSA_87), 0);
ExpectIntEQ(wc_MlDsaKey_SetParams(prvKey, WC_ML_DSA_87), 0);
#endif

/* Parameters set but no key. */
ExpectIntEQ(wc_MlDsaKey_GenMu(key, NULL, 0, msg, sizeof(msg), mu,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

ExpectIntEQ(wc_MlDsaKey_MakeKey(key, &rng), 0);
ExpectIntEQ(wc_MlDsaKey_ExportPubRaw(key, pubBuf, &pubLen), 0);
ExpectIntEQ(wc_MlDsaKey_ExportPrivRaw(key, prvBuf, &prvLen), 0);
ExpectIntEQ(wc_MlDsaKey_ImportPubRaw(pubKey, pubBuf, pubLen), 0);
PRIVATE_KEY_UNLOCK();
ExpectIntEQ(wc_MlDsaKey_ImportPrivRaw(prvKey, prvBuf, prvLen), 0);

/* tr from the public key and from the private key give the same mu. */
ExpectIntEQ(wc_MlDsaKey_GenMu(key, ctx, sizeof(ctx), msg, sizeof(msg), mu,
sizeof(mu)), 0);
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, ctx, sizeof(ctx), msg, sizeof(msg),
muPub, sizeof(muPub)), 0);
ExpectIntEQ(wc_MlDsaKey_GenMu(prvKey, ctx, sizeof(ctx), msg, sizeof(msg),
muPrv, sizeof(muPrv)), 0);
ExpectBufEQ(muPub, mu, sizeof(mu));
ExpectBufEQ(muPrv, mu, sizeof(mu));

/* Signing mu matches signing the message with the same rnd. */
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(prvKey, ctx, sizeof(ctx), sigMsg,
&sigMsgLen, msg, sizeof(msg), seed), 0);
ExpectIntEQ(wc_MlDsaKey_SignMuWithSeed(prvKey, sigMu, &sigMuLen, mu,
sizeof(mu), seed), 0);
ExpectIntEQ(sigMuLen, sigMsgLen);
ExpectBufEQ(sigMu, sigMsg, sigMsgLen);

/* Both verify paths accept it. */
ExpectIntEQ(wc_MlDsaKey_VerifyMu(pubKey, sigMu, sigMuLen, mu, sizeof(mu),
&res), 0);
ExpectIntEQ(res, 1);
res = 0;
ExpectIntEQ(wc_MlDsaKey_VerifyCtx(pubKey, sigMu, sigMuLen, ctx,
sizeof(ctx), msg, sizeof(msg), &res), 0);
ExpectIntEQ(res, 1);

/* The context is bound into mu. */
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, msg, sizeof(msg), muOther,
sizeof(muOther)), 0);
ExpectBufNE(muOther, mu, sizeof(mu));

/* An empty message may be given as (NULL, 0). */
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, NULL, 0, muPub,
sizeof(muPub)), 0);
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, empty, 0, muOther,
sizeof(muOther)), 0);
ExpectBufEQ(muOther, muPub, sizeof(muPub));
sigMsgLen = MLDSA_MAX_SIG_SIZE;
ExpectIntEQ(wc_MlDsaKey_SignCtxWithSeed(prvKey, NULL, 0, sigMsg,
&sigMsgLen, NULL, 0, seed), 0);
res = 0;
ExpectIntEQ(wc_MlDsaKey_VerifyMu(pubKey, sigMsg, sigMsgLen, muPub,
sizeof(muPub), &res), 0);
ExpectIntEQ(res, 1);

/* With both halves set, tr comes from the private key as signing uses:
* pubKey keeps its old public key after a new private key is imported. */
ExpectIntEQ(wc_MlDsaKey_MakeKey(key, &rng), 0);
prvLen = MLDSA_MAX_KEY_SIZE;
ExpectIntEQ(wc_MlDsaKey_ExportPrivRaw(key, prvBuf, &prvLen), 0);
ExpectIntEQ(wc_MlDsaKey_ImportPrivRaw(pubKey, prvBuf, prvLen), 0);
ExpectIntEQ(wc_MlDsaKey_GenMu(key, ctx, sizeof(ctx), msg, sizeof(msg), mu,
sizeof(mu)), 0);
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, ctx, sizeof(ctx), msg, sizeof(msg),
muPrv, sizeof(muPrv)), 0);
ExpectBufEQ(muPrv, mu, sizeof(mu));
PRIVATE_KEY_LOCK();

/* Bad arguments. */
ExpectIntEQ(wc_MlDsaKey_GenMu(NULL, NULL, 0, msg, sizeof(msg), mu,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, msg, sizeof(msg), NULL,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 1, msg, sizeof(msg), mu,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, NULL, 1, mu,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, msg, sizeof(msg), mu,
sizeof(mu) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_MlDsaKey_GenMu(pubKey, NULL, 0, msg, sizeof(msg), mu,
sizeof(mu) + 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));

#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
/* Draft keys hash the message without the 0 || ctxLen prefix: rejected. */
wc_MlDsaKey_Free(key);
ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0);
#ifndef WOLFSSL_NO_ML_DSA_44
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_44_DRAFT), 0);
#elif !defined(WOLFSSL_NO_ML_DSA_65)
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_65_DRAFT), 0);
#else
ExpectIntEQ(wc_MlDsaKey_SetParams(key, WC_ML_DSA_87_DRAFT), 0);
#endif
ExpectIntEQ(wc_MlDsaKey_MakeKey(key, &rng), 0);
ExpectIntEQ(wc_MlDsaKey_GenMu(key, NULL, 0, msg, sizeof(msg), mu,
sizeof(mu)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#endif

DoExpectIntEQ(wc_FreeRng(&rng), 0);
wc_MlDsaKey_Free(prvKey);
wc_MlDsaKey_Free(pubKey);
wc_MlDsaKey_Free(key);
XFREE(sigMu, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(sigMsg, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(prvBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(pubBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(prvKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}

#if !defined(NO_ASN) && defined(HAVE_PKCS8) && \
defined(TEST_MLDSA_HAVE_IMPL) && \
defined(WOLFSSL_HAVE_MLDSA) && \
Expand Down Expand Up @@ -32170,6 +32367,9 @@ WOLFSSL_MLDSA_API_CHECK_INLINE void wc_mldsa_canonical_api_check(void)
(void)wc_MlDsaKey_VerifyCtx(&k, buf, sz, NULL, 0, buf, sz, &res);
(void)wc_MlDsaKey_VerifyCtxHash(&k, buf, sz, NULL, 0, buf, sz, 0, &res);
(void)wc_MlDsaKey_VerifyMu(&k, buf, sz, buf, sz, &res);
#if !defined(WOLFSSL_MLDSA_NO_SIGN) || !defined(WOLFSSL_MLDSA_NO_VERIFY)
(void)wc_MlDsaKey_GenMu(&k, NULL, 0, buf, sz, NULL, sz);
#endif

/* DER decode / encode. */
#ifndef WOLFSSL_MLDSA_NO_ASN1
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_mldsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ int test_mldsa_verify_ctx_kats(void);
int test_mldsa_verify_kats(void);
int test_mldsa_sign_mu_kats(void);
int test_mldsa_verify_mu_kats(void);
int test_mldsa_gen_mu(void);
int test_mldsa_PrivateKeyDecode_OpenSSL_form(void);
int test_mldsa_pkcs8_import_OpenSSL_form(void);
int test_mldsa_pkcs8_export_import_wolfSSL_form(void);
Expand Down Expand Up @@ -129,6 +130,7 @@ int test_mldsa_make_key_id_dev(void);
TEST_DECL_GROUP("mldsa", test_mldsa_verify_kats), \
TEST_DECL_GROUP("mldsa", test_mldsa_sign_mu_kats), \
TEST_DECL_GROUP("mldsa", test_mldsa_verify_mu_kats), \
TEST_DECL_GROUP("mldsa", test_mldsa_gen_mu), \
TEST_DECL_GROUP("mldsa", test_mldsa_PrivateKeyDecode_OpenSSL_form), \
TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8_import_OpenSSL_form), \
TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8_export_import_wolfSSL_form), \
Expand Down
Loading
Loading