Commit 175cda569d for openssl.org
commit 175cda569df9a0188f388ee3afe87c26eaa77f25
Author: slontis <shane.lontis@oracle.com>
Date: Wed Nov 26 17:42:43 2025 +1100
ML-DSA: Add a digest that can calculate external mu.
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/29223)
diff --git a/.gitignore b/.gitignore
index c8e9abece0..c5dcdcdf7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -161,6 +161,7 @@ providers/implementations/digests/digestcommon.inc
providers/implementations/digests/mdc2_prov.inc
providers/implementations/digests/sha2_prov.inc
providers/implementations/digests/sha3_prov.inc
+providers/implementations/digests/ml_dsa_mu_prov.inc
providers/implementations/include/prov/blake2_params.inc
providers/implementations/kdfs/snmpkdf.inc
providers/implementations/macs/cmac_prov.inc
diff --git a/CHANGES.md b/CHANGES.md
index afd7f71010..9b46ca53aa 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -32,6 +32,10 @@ OpenSSL 4.0
### Changes between 3.6 and 4.0 [xx XXX xxxx]
+ * Added "ML-DSA-MU" digest algorithm support.
+
+ *Shane Lontis*
+
* Removed configure options can now only be disabled. You may continue to use
`disable-<feature>`, which will remain supported. Using `enable-<feature>`
for a removed feature is no longer permitted.
diff --git a/build.info b/build.info
index abf27ae39a..c7a833710b 100644
--- a/build.info
+++ b/build.info
@@ -117,6 +117,7 @@ DEPEND[]=include/openssl/asn1.h \
providers/implementations/digests/mdc2_prov.inc \
providers/implementations/digests/sha2_prov.inc \
providers/implementations/digests/sha3_prov.inc \
+ providers/implementations/digests/ml_dsa_mu_prov.inc \
providers/implementations/include/prov/blake2_params.inc \
providers/implementations/macs/cmac_prov.inc \
providers/implementations/macs/gmac_prov.inc \
@@ -232,6 +233,7 @@ DEPEND[providers/implementations/asymciphers/rsa_enc.inc \
providers/implementations/ciphers/cipher_rc4_hmac_md5.inc \
providers/implementations/ciphers/cipher_sm4_xts.inc \
providers/implementations/digests/blake2_prov.inc \
+ providers/implementations/digests/ml_dsa_mu_prov.inc \
providers/implementations/digests/digestcommon.inc \
providers/implementations/digests/mdc2_prov.inc \
providers/implementations/digests/sha2_prov.inc \
@@ -397,6 +399,8 @@ GENERATE[providers/implementations/digests/sha3_prov.inc]=\
providers/implementations/digests/sha3_prov.inc.in
GENERATE[providers/implementations/include/prov/blake2_params.inc]=\
providers/implementations/include/prov/blake2_params.inc.in
+GENERATE[providers/implementations/digests/ml_dsa_mu_prov.inc]=\
+ providers/implementations/digests/ml_dsa_mu_prov.inc.in
GENERATE[providers/implementations/macs/cmac_prov.inc]=\
providers/implementations/macs/cmac_prov.inc.in
GENERATE[providers/implementations/macs/gmac_prov.inc]=\
diff --git a/crypto/ml_dsa/ml_dsa_sign.c b/crypto/ml_dsa/ml_dsa_sign.c
index 21946ab2be..5be4e69a31 100644
--- a/crypto/ml_dsa/ml_dsa_sign.c
+++ b/crypto/ml_dsa/ml_dsa_sign.c
@@ -65,32 +65,29 @@ static void signature_init(ML_DSA_SIG *sig,
* @param ctx_len The size of |ctx|. It must be in the range 0..255
* @returns an EVP_MD_CTX if the operation is successful, NULL otherwise.
*/
-
-EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
+EVP_MD_CTX *ossl_ml_dsa_mu_init_int(EVP_MD *shake256_md,
+ const uint8_t *tr, size_t tr_len, int encode, int prehash,
const uint8_t *ctx, size_t ctx_len)
{
EVP_MD_CTX *md_ctx;
uint8_t itb[2];
- if (key == NULL)
- return NULL;
-
md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL)
return NULL;
/* H(.. */
- if (!EVP_DigestInit_ex2(md_ctx, key->shake256_md, NULL))
+ if (!EVP_DigestInit_ex2(md_ctx, shake256_md, NULL))
goto err;
/* ..pk (= key->tr) */
- if (!EVP_DigestUpdate(md_ctx, key->tr, sizeof(key->tr)))
+ if (!EVP_DigestUpdate(md_ctx, tr, tr_len))
goto err;
/* M' = .. */
if (encode) {
if (ctx_len > ML_DSA_MAX_CONTEXT_STRING_LEN)
goto err;
/* IntegerToBytes(0, 1) .. */
- itb[0] = 0;
+ itb[0] = prehash ? 1 : 0;
/* || IntegerToBytes(|ctx|, 1) || .. */
itb[1] = (uint8_t)ctx_len;
if (!EVP_DigestUpdate(md_ctx, itb, 2))
@@ -108,6 +105,15 @@ err:
return NULL;
}
+EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
+ const uint8_t *ctx, size_t ctx_len)
+{
+ if (key == NULL)
+ return NULL;
+ return ossl_ml_dsa_mu_init_int(key->shake256_md, key->tr, sizeof(key->tr),
+ encode, 0, ctx, ctx_len);
+}
+
/*
* @brief: updates the internal ML-DSA hash with an additional message chunk.
*
@@ -153,8 +159,7 @@ int ossl_ml_dsa_mu_finalize(EVP_MD_CTX *md_ctx, uint8_t *mu, size_t mu_len)
* @returns 1 on success, 0 on error
*/
static int ml_dsa_sign_internal(const ML_DSA_KEY *priv,
- const uint8_t *mu, size_t mu_len,
- const uint8_t *rnd, size_t rnd_len,
+ const uint8_t *mu, size_t mu_len, const uint8_t *rnd, size_t rnd_len,
uint8_t *out_sig)
{
int ret = 0;
@@ -315,8 +320,7 @@ err:
*/
static int ml_dsa_verify_internal(const ML_DSA_KEY *pub,
const uint8_t *mu, size_t mu_len,
- const uint8_t *sig_enc,
- size_t sig_enc_len)
+ const uint8_t *sig_enc, size_t sig_enc_len)
{
int ret = 0;
uint8_t *alloc = NULL, *w1_encoded;
@@ -412,8 +416,8 @@ err:
*
* @returns 1 on success, or 0 on error.
*/
-int ossl_ml_dsa_sign(const ML_DSA_KEY *priv, int msg_is_mu,
- const uint8_t *msg, size_t msg_len,
+int ossl_ml_dsa_sign(const ML_DSA_KEY *priv,
+ int msg_is_mu, const uint8_t *msg, size_t msg_len,
const uint8_t *context, size_t context_len,
const uint8_t *rand, size_t rand_len, int encode,
unsigned char *sig, size_t *sig_len, size_t sig_size)
@@ -462,8 +466,8 @@ err:
* See FIPS 203 Section 5.3 Algorithm 3 ML-DSA.Verify()
* @returns 1 on success, or 0 on error.
*/
-int ossl_ml_dsa_verify(const ML_DSA_KEY *pub, int msg_is_mu,
- const uint8_t *msg, size_t msg_len,
+int ossl_ml_dsa_verify(const ML_DSA_KEY *pub,
+ int msg_is_mu, const uint8_t *msg, size_t msg_len,
const uint8_t *context, size_t context_len, int encode,
const uint8_t *sig, size_t sig_len)
{
diff --git a/doc/build.info b/doc/build.info
index b71e1e41d6..589516632a 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -4741,6 +4741,10 @@ DEPEND[html/man7/EVP_MD-MDC2.html]=man7/EVP_MD-MDC2.pod
GENERATE[html/man7/EVP_MD-MDC2.html]=man7/EVP_MD-MDC2.pod
DEPEND[man/man7/EVP_MD-MDC2.7]=man7/EVP_MD-MDC2.pod
GENERATE[man/man7/EVP_MD-MDC2.7]=man7/EVP_MD-MDC2.pod
+DEPEND[html/man7/EVP_MD-ML-DSA-MU.html]=man7/EVP_MD-ML-DSA-MU.pod
+GENERATE[html/man7/EVP_MD-ML-DSA-MU.html]=man7/EVP_MD-ML-DSA-MU.pod
+DEPEND[man/man7/EVP_MD-ML-DSA-MU.7]=man7/EVP_MD-ML-DSA-MU.pod
+GENERATE[man/man7/EVP_MD-ML-DSA-MU.7]=man7/EVP_MD-ML-DSA-MU.pod
DEPEND[html/man7/EVP_MD-NULL.html]=man7/EVP_MD-NULL.pod
GENERATE[html/man7/EVP_MD-NULL.html]=man7/EVP_MD-NULL.pod
DEPEND[man/man7/EVP_MD-NULL.7]=man7/EVP_MD-NULL.pod
@@ -5228,6 +5232,7 @@ html/man7/EVP_MD-MD4.html \
html/man7/EVP_MD-MD5-SHA1.html \
html/man7/EVP_MD-MD5.html \
html/man7/EVP_MD-MDC2.html \
+html/man7/EVP_MD-ML-DSA-MU.html \
html/man7/EVP_MD-NULL.html \
html/man7/EVP_MD-RIPEMD160.html \
html/man7/EVP_MD-SHA1.html \
@@ -5389,6 +5394,7 @@ man/man7/EVP_MD-MD4.7 \
man/man7/EVP_MD-MD5-SHA1.7 \
man/man7/EVP_MD-MD5.7 \
man/man7/EVP_MD-MDC2.7 \
+man/man7/EVP_MD-ML-DSA-MU.7 \
man/man7/EVP_MD-NULL.7 \
man/man7/EVP_MD-RIPEMD160.7 \
man/man7/EVP_MD-SHA1.7 \
diff --git a/doc/man7/EVP_MD-ML-DSA-MU.pod b/doc/man7/EVP_MD-ML-DSA-MU.pod
new file mode 100644
index 0000000000..ae351770db
--- /dev/null
+++ b/doc/man7/EVP_MD-ML-DSA-MU.pod
@@ -0,0 +1,150 @@
+=pod
+
+=head1 NAME
+
+EVP_MD-ML-DSA-MU - The ML-DSA-MU EVP_MD implementation
+
+=head1 DESCRIPTION
+
+Support for computing the value of external mu for ML-DSA using the B<EVP_MD> API.
+
+Normally the value of C<mu> is calculated internally as part of an ML-DSA
+sign or verify operation. C<mu> is defined as:
+ mu = SHAKE256(tr || M', 64)
+
+Where B<tr> is the hash of the encoded public key, and, for Pure (ML-DSA):
+ M' = 0x00 || ctx_len || ctx || message
+
+In cases where prehashing the message is required, FIPS 204 allows
+the C<mu> calculation to be done externally and then C<mu> can be passed to
+ML-DSA sign or verify operations.
+
+PreHash (HASH-ML-DSA) is also supported and uses:
+ M' = 0x01 || ctx_len || ctx || OID || HashedMessage
+
+The output C<mu> value can then be supplied as an input to L<EVP_SIGNATURE-ML-DSA(7)>
+using the ML-DSA Signature Parameter B<OSSL_SIGNATURE_PARAM_MU> (i.e. C<mu>).
+This allows larger messages to be hashed (or hidden) before they are passed to
+pure ML-DSA sign or verify operations.
+
+=head2 Identities
+
+This implementation is available with the FIPS provider as well as the
+default provider, and is identified with the name "ML-DSA-MU".
+
+=head2 Parameters
+
+This implementation supports the following settable L<OSSL_PARAM(3)> parameters:
+
+=over 4
+
+=item "pub" (B<OSSL_DIGEST_PARAM_MU_PUB_KEY>) <octet string>
+
+A B<ML-DSA> encoded public key value of size 1312, 1952 or 2592 bytes
+depending on the respective key type of B<ML-DSA-44>, B<ML-DSA-65> or B<ML-DSA-87>.
+This can be retrieved from a L<EVP_PKEY-ML-DSA(7)> key by calling
+EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PUB_KEY, pub, sizeof(pub), &publen)
+This parameter MUST be set or an error will occur.
+
+=item "context-string" (B<OSSL_DIGEST_PARAM_MU_CONTEXT_STRING>) <octet string>
+
+An optional string of octets with length at most 255. By default it is the empty string.
+
+=item "digest" (B<OSSL_DIGEST_PARAM_MU_DIGEST>) <utf8 string>
+
+An optional parameter related to "HASH-ML-DSA". If used it determines the OID in
+the definition of PreHash M' above.
+
+When this parameter is not specified, pure ML-DSA C<mu> is computed, and the
+input data is expected to be the full message, otherwise the input data must
+be the result of prehashing the message with the corresponding digest algorithm.
+
+The HASH-ML-DSA variant is available to enable specialised use-cases,
+in which signing the full message with pure ML-DSA is not practical, and
+the external-mu API is a viable alternative.
+HASH-ML-DSA is not used in protocols such as X509 & CMS (See RFC 9981 and 9982),
+and is not presently implemented as an independent OpenSSL signature algorithm.
+
+OpenSSL accepts the following digest names: "SHAKE-256", "SHAKE-128", "SHA-224",
+"SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384" and "SHA3-512".
+The total size of the C<HashedMessage> passed to EVP_DigestUpdate() MUST match
+the size of the digest. For SHAKE-128 and SHAKE-256 the expected XOF digest
+lengths are 32 and 64 respectively.
+
+=item "properties" (B<OSSL_DIGEST_PARAM_MU_PROPERTIES>) <utf8 string>
+
+Sets the properties to be queried when trying to fetch the underlying digest.
+
+=back
+
+=head2 Gettable Parameters
+
+This implementation supports the common gettable parameters described
+in L<EVP_MD-common(7)>.
+
+=head1 CONFORMING TO
+
+FIPS 204 and
+https://csrc.nist.gov/csrc/media/Projects/post-quantum-cryptography/documents/faq/fips204-sec6-03192025.pdf
+
+=head1 EXAMPLES
+
+To generate external 'mu' given an existing ML-DSA key and a large message:
+
+ calculate_mu(EVP_PKEY *pkey, const unsigned char *msg, size_t msglen,
+ const unsigned char *ctx, size_t ctxlen, unsigned char mu[64])
+ {
+ unsigned char pub[2592];
+ size_t publen = 0, chunk;
+ OSSL_PARAM params[4], *p = params;
+
+ /* Retrieve the ML-DSA encoded public key */
+ EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+ pub, sizeof(pub), &publen);
+
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_PUB_KEY, pub, publen);
+ /* This is an optional parameter */
+ if (ctx != NULL && ctxlen != 0)
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_CONTEXT_STRING, ctx, ctxlen);
+ /*
+ * Optionally we could also set the digest name for HASH-ML-DSA
+ * *p++ = OSSL_PARAM_construct_utf8_string(OSSL_DIGEST_PARAM_MU_DIGEST, "SHA-512", 0);
+ */
+ *p = OSSL_PARAM_construct_end();
+
+ mdctx = EVP_MD_CTX_new();
+ md = EVP_MD_fetch(libctx, "ML-DSA-MU", NULL);
+ EVP_DigestInit_ex2(mdctx, md, params);
+ /* Call EVP_DigestUpdate() multiple times to stream the message */
+ while (msglen != 0) {
+ /* Account for the last chunk being less than 64 */
+ chunk = (msglen >= 64) ? 64 : msglen;
+ EVP_DigestUpdate(mdctx, msg, chunk);
+ msg += chunk;
+ msglen -= chunk;
+ }
+ EVP_DigestFinalXOF(mdctx, mu, sizeof(mu))
+ }
+
+=head1 SEE ALSO
+
+L<EVP_SIGNATURE-ML-DSA(7)>,
+L<EVP_PKEY-ML-DSA(7)>,
+L<provider-digest(7)>,
+L<OSSL_PROVIDER-FIPS(7)>,
+L<OSSL_PROVIDER-default(7)>
+
+=head1 HISTORY
+
+EVP_MD-ML-DSA-MU was added in OpenSSL 4.0.0.
+
+=head1 COPYRIGHT
+
+Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man7/EVP_SIGNATURE-ML-DSA.pod b/doc/man7/EVP_SIGNATURE-ML-DSA.pod
index c9ccf1aafb..bad325f06a 100644
--- a/doc/man7/EVP_SIGNATURE-ML-DSA.pod
+++ b/doc/man7/EVP_SIGNATURE-ML-DSA.pod
@@ -67,19 +67,25 @@ If set the size must be 32 bytes.
=item "deterministic" (B<OSSL_SIGNATURE_PARAM_DETERMINISTIC>) <integer>
The default value of 0 causes the per message randomness to be randomly
-generated using a DRBG. Setting this to 1 causes the per message randomness
-to be set to 32 bytes of zeros. This value is ignored if "test-entropy" is set.
+generated using a DRBG. Setting this to a nonzero value causes the per message
+randomness to be set to 32 bytes of zeros. This value is ignored
+if "test-entropy" is provided.
=item "mu" (B<OSSL_SIGNATURE_PARAM_MU>) <integer>
The default value of 0 causes sign and verify operations to process a raw message.
-Setting this to 1 causes those operations to assume the input is the C<mu> value
-from L<FIPS 204|https://csrc.nist.gov/pubs/fips/204/final> Algorithm 7 step 6 and
-Algorithm 8 step 7.
+Setting this to a nonzero value causes those operations to assume the input is
+the C<mu> value from
+L<FIPS 204|https://csrc.nist.gov/pubs/fips/204/final> Algorithm 7 step 6 and Algorithm 8 step 7.
Note that the message encoding steps from
L<FIPS 204|https://csrc.nist.gov/pubs/fips/204/final> Algorithm 2 step 10 and
-Algorithm 3 step 5 are omitted when this setting is 1.
+Algorithm 3 step 5 are omitted when this setting is nonzero.
+
+See L<EVP_MD-ML-DSA-MU(7)> for more information on generating an
+external-mu value.
+
+The "context-string" is ignored if this value is nonzero.
=back
@@ -95,17 +101,17 @@ passed in I<mdname> must be NULL.
To sign a message using an ML-DSA EVP_PKEY structure:
- void do_sign(EVP_PKEY *key, unsigned char *msg, size_t msg_len)
+ void do_sign(EVP_PKEY *key, const unsigned char *msg, size_t msg_len)
{
size_t sig_len;
unsigned char *sig = NULL;
- const OSSL_PARAM params[] = {
- OSSL_PARAM_octet_string("context-string", (unsigned char *)"A context string", 16),
- OSSL_PARAM_END
- };
+ OSSL_PARAM params[2];
EVP_PKEY_CTX *sctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
EVP_SIGNATURE *sig_alg = EVP_SIGNATURE_fetch(NULL, "ML-DSA-65", NULL);
+ /* The context string is an optional parameter */
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (unsigned char *)"A context string", 16),
+ params[1] = OSSL_PARAM_construct_end();
EVP_PKEY_sign_message_init(sctx, sig_alg, params);
/* Calculate the required size for the signature by passing a NULL buffer. */
EVP_PKEY_sign(sctx, NULL, &sig_len, msg, msg_len);
@@ -117,9 +123,35 @@ To sign a message using an ML-DSA EVP_PKEY structure:
EVP_PKEY_CTX_free(sctx);
}
+To sign a message using an ML-DSA EVP_PKEY structure and an external mu:
+
+ void do_sign(EVP_PKEY *key, const unsigned char mu[64])
+ {
+ int use_mu_instead_of_msg = 1;
+ size_t sig_len;
+ unsigned char *sig = NULL;
+ OSSL_PARAM params[2];
+ EVP_PKEY_CTX *sctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
+ EVP_SIGNATURE *sig_alg = EVP_SIGNATURE_fetch(NULL, "ML-DSA-65", NULL);
+
+ params[0] = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_MU, &use_mu_instead_of_msg);
+ params[1] = OSSL_PARAM_construct_end();
+
+ EVP_PKEY_sign_message_init(sctx, sig_alg, params);
+ /* Calculate the required size for the signature by passing a NULL buffer. */
+ EVP_PKEY_sign(sctx, NULL, &sig_len, mu, 64);
+ sig = OPENSSL_malloc(sig_len);
+ EVP_PKEY_sign(sctx, sig, &sig_len, mu, 64);
+ ...
+ OPENSSL_free(sig);
+ EVP_SIGNATURE_free(sig_alg);
+ EVP_PKEY_CTX_free(sctx);
+ }
+
=head1 SEE ALSO
-L<EVP_PKEY-ML-DSA(7)>
+L<EVP_PKEY-ML-DSA(7)>,
+L<EVP_MD-ML-DSA-MU(7)>,
L<provider-signature(7)>,
L<EVP_PKEY_sign(3)>,
L<EVP_PKEY_verify(3)>,
diff --git a/doc/man7/OSSL_PROVIDER-FIPS.pod b/doc/man7/OSSL_PROVIDER-FIPS.pod
index b18140f554..29ffe1592b 100644
--- a/doc/man7/OSSL_PROVIDER-FIPS.pod
+++ b/doc/man7/OSSL_PROVIDER-FIPS.pod
@@ -73,6 +73,8 @@ The OpenSSL FIPS provider supports these operations and algorithms:
KECCAK-KMAC is only used internally as a sub algorithm of KMAC.
+=item ML-DSA-MU, see L<EVP_MD-ML-DSA-MU(7)>
+
=back
=head2 Symmetric Ciphers
diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod
index f0743d99d7..5cc5f490ec 100644
--- a/doc/man7/OSSL_PROVIDER-default.pod
+++ b/doc/man7/OSSL_PROVIDER-default.pod
@@ -75,6 +75,8 @@ The OpenSSL default provider supports these operations and algorithms:
=item NULL, see L<EVP_MD-NULL(7)>
+=item ML-DSA-MU, see L<EVP_MD-ML-DSA-MU(7)>
+
=back
=head2 Symmetric Ciphers
diff --git a/doc/man7/provider-digest.pod b/doc/man7/provider-digest.pod
index 5620812e3f..0b4319217c 100644
--- a/doc/man7/provider-digest.pod
+++ b/doc/man7/provider-digest.pod
@@ -307,7 +307,7 @@ L<EVP_MD-MD4(7)>, L<EVP_MD-MD5(7)>, L<EVP_MD-MD5-SHA1(7)>,
L<EVP_MD-MDC2(7)>, L<EVP_MD-RIPEMD160(7)>, L<EVP_MD-SHA1(7)>,
L<EVP_MD-SHA2(7)>, L<EVP_MD-SHA3(7)>, L<EVP_MD-KECCAK(7)>
L<EVP_MD-SHAKE(7)>, L<EVP_MD-SM3(7)>, L<EVP_MD-WHIRLPOOL(7)>,
-L<EVP_MD-NULL(7)>,
+L<EVP_MD-NULL(7)>, L<EVP_MD-ML-DSA-MU(7)>,
L<life_cycle-digest(7)>, L<EVP_DigestInit(3)>
=head1 HISTORY
diff --git a/include/crypto/ml_dsa.h b/include/crypto/ml_dsa.h
index 41a882c76d..0bda071404 100644
--- a/include/crypto/ml_dsa.h
+++ b/include/crypto/ml_dsa.h
@@ -109,7 +109,11 @@ __owur int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key);
__owur int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len);
__owur int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len);
-EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
+__owur EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
+ const uint8_t *ctx, size_t ctx_len);
+
+__owur EVP_MD_CTX *ossl_ml_dsa_mu_init_int(EVP_MD *shake256_md,
+ const uint8_t *tr, size_t tr_len, int encode, int prehash,
const uint8_t *ctx, size_t ctx_len);
__owur int ossl_ml_dsa_mu_update(EVP_MD_CTX *md_ctx, const uint8_t *msg, size_t msg_len);
__owur int ossl_ml_dsa_mu_finalize(EVP_MD_CTX *md_ctx, uint8_t *mu, size_t mu_len);
diff --git a/providers/common/der/der_ml_dsa_key.c b/providers/common/der/der_ml_dsa_key.c
index a042f634e7..36021c6f20 100644
--- a/providers/common/der/der_ml_dsa_key.c
+++ b/providers/common/der/der_ml_dsa_key.c
@@ -12,30 +12,67 @@
* internal use.
*/
#include "internal/deprecated.h"
-
+#include <openssl/obj_mac.h>
+#include <openssl/evp.h>
#include "internal/packet.h"
#include "prov/der_ml_dsa.h"
+#include "prov/der_pq_dsa.h"
+#include "prov/der_digests.h"
+
+#define SET_OID(oid, oidlen, oidname) \
+ (oid) = ossl_der_oid_id_##oidname; \
+ (oidlen) = sizeof(ossl_der_oid_id_##oidname)
+
+#define SET_DIGEST_OID(oidname, digestsz) \
+ SET_OID(*oid, *oidlen, oidname); \
+ *sz = digestsz
int ossl_DER_w_algorithmIdentifier_ML_DSA(WPACKET *pkt, int tag, ML_DSA_KEY *key)
{
- const uint8_t *alg;
- size_t len;
+ const uint8_t *oid;
+ size_t oidlen;
const char *name = ossl_ml_dsa_key_get_name(key);
if (OPENSSL_strcasecmp(name, "ML-DSA-44") == 0) {
- alg = ossl_der_oid_id_ml_dsa_44;
- len = sizeof(ossl_der_oid_id_ml_dsa_44);
+ SET_OID(oid, oidlen, ml_dsa_44);
} else if (OPENSSL_strcasecmp(name, "ML-DSA-65") == 0) {
- alg = ossl_der_oid_id_ml_dsa_65;
- len = sizeof(ossl_der_oid_id_ml_dsa_65);
+ SET_OID(oid, oidlen, ml_dsa_65);
} else if (OPENSSL_strcasecmp(name, "ML-DSA-87") == 0) {
- alg = ossl_der_oid_id_ml_dsa_87;
- len = sizeof(ossl_der_oid_id_ml_dsa_87);
+ SET_OID(oid, oidlen, ml_dsa_87);
} else {
return 0;
}
return ossl_DER_w_begin_sequence(pkt, tag)
/* No parameters */
- && ossl_DER_w_precompiled(pkt, -1, alg, len)
+ && ossl_DER_w_precompiled(pkt, -1, oid, oidlen)
&& ossl_DER_w_end_sequence(pkt, tag);
}
+
+int ossl_der_oid_pq_dsa_prehash_digest(const char *oid_digest_name,
+ const uint8_t **oid, size_t *oidlen, size_t *sz)
+{
+ if (OPENSSL_strcasecmp(oid_digest_name, "SHAKE-256") == 0) {
+ SET_DIGEST_OID(shake256, 64);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHAKE-128") == 0) {
+ SET_DIGEST_OID(shake128, 32);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-224") == 0) {
+ SET_DIGEST_OID(sha224, 28);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-256") == 0) {
+ SET_DIGEST_OID(sha256, 32);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-384") == 0) {
+ SET_DIGEST_OID(sha384, 48);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-512") == 0) {
+ SET_DIGEST_OID(sha512, 64);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-224") == 0) {
+ SET_DIGEST_OID(sha3_224, 28);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-256") == 0) {
+ SET_DIGEST_OID(sha3_256, 32);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-384") == 0) {
+ SET_DIGEST_OID(sha3_384, 48);
+ } else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-512") == 0) {
+ SET_DIGEST_OID(sha3_512, 64);
+ } else {
+ return 0;
+ }
+ return 1;
+}
diff --git a/providers/common/include/prov/der_pq_dsa.h b/providers/common/include/prov/der_pq_dsa.h
new file mode 100644
index 0000000000..adc6f839be
--- /dev/null
+++ b/providers/common/include/prov/der_pq_dsa.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+int ossl_der_oid_pq_dsa_prehash_digest(const char *oid_digest_name,
+ const uint8_t **oid, size_t *oidlen, size_t *sz);
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 50397f2189..aa673f7c7f 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -159,6 +159,9 @@ static const OSSL_ALGORITHM deflt_digests[] = {
#endif /* OPENSSL_NO_RMD160 */
{ PROV_NAMES_NULL, "provider=default", ossl_nullmd_functions },
+#ifndef OPENSSL_NO_ML_DSA
+ { PROV_NAMES_ML_DSA_MU, "provider=default", ossl_ml_dsa_mu_functions },
+#endif
{ NULL, NULL, NULL }
};
diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index 8967a21e9a..9905fa404f 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -49,8 +49,10 @@ static OSSL_FUNC_provider_query_operation_fn fips_query;
static OSSL_FUNC_provider_query_operation_fn fips_query_internal;
static OSSL_FUNC_provider_random_bytes_fn fips_random_bytes;
-#define ALGC(NAMES, FUNC, CHECK) \
- { { NAMES, FIPS_DEFAULT_PROPERTIES, FUNC }, CHECK }
+#define ALGC(NAMES, FUNC, CHECK) \
+ { \
+ { NAMES, FIPS_DEFAULT_PROPERTIES, FUNC }, CHECK \
+ }
#define ALG(NAMES, FUNC) ALGC(NAMES, FUNC, NULL)
extern OSSL_FUNC_core_thread_start_fn *c_thread_start;
@@ -300,6 +302,9 @@ static int fips_self_test(void *provctx)
static const OSSL_ALGORITHM fips_digests[] = {
FIPS_DIGESTS_COMMON(),
+#ifndef OPENSSL_NO_ML_DSA
+ { PROV_NAMES_ML_DSA_MU, FIPS_DEFAULT_PROPERTIES, ossl_ml_dsa_mu_functions },
+#endif
{ NULL, NULL, NULL }
};
static const OSSL_ALGORITHM fips_digests_internal[] = {
diff --git a/providers/implementations/digests/build.info b/providers/implementations/digests/build.info
index d30975028e..ceaac9816d 100644
--- a/providers/implementations/digests/build.info
+++ b/providers/implementations/digests/build.info
@@ -10,6 +10,7 @@ $BLAKE2_GOAL=../../libdefault.a
$SM3_GOAL=../../libdefault.a
$MD5_GOAL=../../libdefault.a
$NULL_GOAL=../../libdefault.a
+$ML_DSA_MU_GOAL=../../libdefault.a ../../libfips.a
$MD2_GOAL=../../liblegacy.a
$MD4_GOAL=../../liblegacy.a
@@ -60,3 +61,7 @@ ENDIF
IF[{- !$disabled{rmd160} -}]
SOURCE[$RIPEMD_GOAL]=ripemd_prov.c
ENDIF
+
+IF[{- !$disabled{'ml-dsa'} -}]
+ SOURCE[$ML_DSA_MU_GOAL]=ml_dsa_mu_prov.c
+ENDIF
diff --git a/providers/implementations/digests/ml_dsa_mu_prov.c b/providers/implementations/digests/ml_dsa_mu_prov.c
new file mode 100644
index 0000000000..725cd450aa
--- /dev/null
+++ b/providers/implementations/digests/ml_dsa_mu_prov.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * mu is the value:
+ * mu = SHAKE256(tr || M', 64)
+ *
+ * where tr is the hash of the public key
+ * And M' is one of the following:
+ * (1) Pure: M' = 00 || ctx_len || ctx || in (where in = message)
+ * (2) PreHash: M' = 01 || ctx_len || ctx || OID || in (where in = hashed(msg))
+ */
+
+#include "internal/deprecated.h" /* including crypto/sha.h requires this */
+
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/core_names.h>
+#include "crypto/ml_dsa.h"
+#include "prov/provider_ctx.h"
+#include "prov/digestcommon.h"
+#include "prov/der_pq_dsa.h"
+#include "prov/implementations.h"
+#include "internal/common.h"
+#include "internal/sha3.h"
+#include "providers/implementations/digests/ml_dsa_mu_prov.inc"
+
+#define SHAKE256_SIZE 64
+#define SHAKE_FLAGS (PROV_DIGEST_FLAG_ALGID_ABSENT)
+#define ML_DSA_MAX_CONTEXT_STRING_LEN 255
+
+typedef struct mu_ctx_st {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ EVP_MD_CTX *mdctx;
+ EVP_MD *md;
+ uint8_t context[ML_DSA_MAX_CONTEXT_STRING_LEN];
+ size_t context_len;
+ uint8_t tr[SHAKE256_SIZE]; /* Pre-cached public key Hash */
+ size_t keylen;
+ const uint8_t *oid;
+ size_t oid_len;
+ size_t digest_len;
+ size_t remaining;
+} MU_CTX;
+
+static OSSL_FUNC_digest_newctx_fn mu_newctx;
+static OSSL_FUNC_digest_freectx_fn mu_freectx;
+static OSSL_FUNC_digest_get_params_fn mu_get_params;
+static OSSL_FUNC_digest_dupctx_fn mu_dupctx;
+static OSSL_FUNC_digest_init_fn mu_init;
+static OSSL_FUNC_digest_update_fn mu_update;
+static OSSL_FUNC_digest_final_fn mu_final;
+static OSSL_FUNC_digest_set_ctx_params_fn mu_set_ctx_params;
+static OSSL_FUNC_digest_settable_ctx_params_fn mu_settable_ctx_params;
+static OSSL_FUNC_digest_get_ctx_params_fn mu_get_ctx_params;
+static OSSL_FUNC_digest_gettable_ctx_params_fn mu_gettable_ctx_params;
+
+static void *mu_newctx(void *provctx)
+{
+ MU_CTX *ctx;
+
+ if (ossl_unlikely(!ossl_prov_is_running()))
+ return NULL;
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ return ctx;
+}
+
+static void mu_freectx(void *vctx)
+{
+ MU_CTX *ctx = (MU_CTX *)vctx;
+
+ OPENSSL_free(ctx->propq);
+ EVP_MD_free(ctx->md);
+ EVP_MD_CTX_free(ctx->mdctx);
+ OPENSSL_free(ctx);
+}
+
+static void *mu_dupctx(void *ctx)
+{
+ MU_CTX *src = (MU_CTX *)ctx;
+ MU_CTX *dst = ossl_prov_is_running() ? OPENSSL_malloc(sizeof(*dst)) : NULL;
+
+ if (dst == NULL)
+ return NULL;
+ *dst = *src;
+ dst->mdctx = NULL;
+ dst->propq = NULL;
+ dst->md = NULL;
+ if (src->md != NULL) {
+ if (!EVP_MD_up_ref(src->md))
+ goto err;
+ dst->md = src->md;
+ }
+ if (src->mdctx != NULL) {
+ dst->mdctx = EVP_MD_CTX_new();
+ if (dst->mdctx == NULL
+ || !EVP_MD_CTX_copy_ex(dst->mdctx, src->mdctx))
+ goto err;
+ }
+ if (src->propq != NULL) {
+ dst->propq = OPENSSL_strdup(src->propq);
+ if (dst->propq == NULL)
+ goto err;
+ }
+ return dst;
+err:
+ mu_freectx(dst);
+ return NULL;
+}
+
+static int mu_init(void *vctx, const OSSL_PARAM params[])
+{
+ MU_CTX *ctx = (MU_CTX *)vctx;
+
+ if (ossl_unlikely(!ossl_prov_is_running()))
+ return 0;
+
+ if (ctx->mdctx != NULL && !EVP_MD_CTX_reset(ctx->mdctx))
+ return 0;
+ ctx->remaining = ctx->digest_len;
+ return mu_set_ctx_params(vctx, params);
+}
+
+static int mu_get_params(OSSL_PARAM params[])
+{
+ return ossl_digest_default_get_params(params, SHA3_BLOCKSIZE(256),
+ SHAKE256_SIZE, SHAKE_FLAGS);
+}
+
+static const OSSL_PARAM *mu_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return ml_dsa_mu_set_ctx_params_list;
+}
+
+static int set_property_query(MU_CTX *ctx, const char *propq)
+{
+ OPENSSL_free(ctx->propq);
+ ctx->propq = NULL;
+ if (propq != NULL) {
+ ctx->propq = OPENSSL_strdup(propq);
+ if (ctx->propq == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static EVP_MD *shake_digest(MU_CTX *ctx)
+{
+ if (ctx->md == NULL)
+ ctx->md = EVP_MD_fetch(ctx->libctx, "SHAKE256", ctx->propq);
+ return ctx->md;
+}
+
+static int digest_public_key(MU_CTX *ctx, const uint8_t *pub, size_t publen)
+{
+ int ret;
+ EVP_MD *md;
+ EVP_MD_CTX *mdctx;
+
+ if (publen != ML_DSA_44_PUB_LEN
+ && publen != ML_DSA_65_PUB_LEN
+ && publen != ML_DSA_87_PUB_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+ md = shake_digest(ctx);
+ if (md == NULL)
+ return 0;
+ mdctx = EVP_MD_CTX_new();
+ if (mdctx == NULL)
+ return 0;
+ ret = EVP_DigestInit_ex(mdctx, md, NULL)
+ && EVP_DigestUpdate(mdctx, pub, publen)
+ && EVP_DigestFinalXOF(mdctx, ctx->tr, sizeof(ctx->tr));
+ EVP_MD_CTX_free(mdctx);
+
+ return ret;
+}
+
+static int mu_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ MU_CTX *ctx = (MU_CTX *)vctx;
+ struct ml_dsa_mu_set_ctx_params_st p;
+
+ if (ctx == NULL || !ml_dsa_mu_set_ctx_params_decoder(params, &p))
+ return 0;
+
+ if (p.ctx != NULL) {
+ void *vp = ctx->context;
+
+ if (!OSSL_PARAM_get_octet_string(p.ctx, &vp, sizeof(ctx->context),
+ &(ctx->context_len))) {
+ ctx->context_len = 0;
+ return 0;
+ }
+ }
+ if (p.propq != NULL) {
+ if (p.propq->data_type != OSSL_PARAM_UTF8_STRING
+ || !set_property_query(ctx, p.propq->data))
+ return 0;
+ }
+ if (p.pubkey != NULL) {
+ if (p.pubkey->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ if (!digest_public_key(ctx, p.pubkey->data, p.pubkey->data_size))
+ return 0;
+ ctx->keylen = p.pubkey->data_size;
+ }
+ if (p.digestname != NULL) {
+ int ret;
+
+ if (p.digestname->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ ret = ossl_der_oid_pq_dsa_prehash_digest(p.digestname->data,
+ &ctx->oid, &ctx->oid_len, &ctx->digest_len);
+ if (ret)
+ ctx->remaining = ctx->digest_len;
+ else
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s is not supported", p.digestname->data);
+ return ret;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *mu_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return ml_dsa_mu_get_ctx_params_list;
+}
+
+static int mu_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ MU_CTX *ctx = (MU_CTX *)vctx;
+ struct ml_dsa_mu_get_ctx_params_st p;
+
+ if (ctx == NULL || !ml_dsa_mu_get_ctx_params_decoder(params, &p))
+ return 0;
+
+ /* Size is an alias of xoflen */
+ if (p.xoflen != NULL || p.size != NULL) {
+ size_t xoflen = SHAKE256_SIZE;
+
+ if (p.size != NULL && !OSSL_PARAM_set_size_t(p.size, xoflen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (p.xoflen != NULL && !OSSL_PARAM_set_size_t(p.xoflen, xoflen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int check_init(MU_CTX *ctx)
+{
+ if (ctx->mdctx == NULL) {
+ EVP_MD *md = shake_digest(ctx);
+
+ if (md == NULL)
+ return 0;
+ if (ctx->keylen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ ctx->mdctx = ossl_ml_dsa_mu_init_int(md, ctx->tr, sizeof(ctx->tr), 1,
+ ctx->oid_len != 0, ctx->context, ctx->context_len);
+ if (ctx->mdctx == NULL)
+ return 0;
+ if (!ossl_ml_dsa_mu_update(ctx->mdctx, ctx->oid, ctx->oid_len))
+ return 0;
+ }
+ return 1;
+}
+
+static int mu_update(void *vctx, const unsigned char *in, size_t inlen)
+{
+ MU_CTX *ctx = (MU_CTX *)vctx;
+ int ret;
+
+ if (ctx->oid_len > 0) {
+ /* For the HASH-ML-DSA case we expect the input to be the size of the digest */
+ if (inlen > ctx->remaining) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
+ return 0;
+ }
+ ctx->remaining -= inlen;
+ }
+ ret = check_init(ctx)
+ && ossl_ml_dsa_mu_update(ctx->mdctx, in, inlen);
+ return ret;
+}
+
+static int mu_final(void *vctx, uint8_t *out, size_t *outl, size_t outsz)
+{
+ MU_CTX *ctx = (MU_CTX *)vctx;
+ size_t len = SHAKE256_SIZE;
+
+ if (ossl_unlikely(!ossl_prov_is_running()))
+ return 0;
+ if (out == NULL) {
+ if (outl == NULL)
+ return 0;
+ } else if (outsz > 0) {
+ if (outsz < len)
+ return 0;
+
+ if (ctx->remaining != 0)
+ return 0;
+ if (!ossl_ml_dsa_mu_finalize(ctx->mdctx, out, len))
+ return 0;
+ }
+ *outl = len;
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_ml_dsa_mu_functions[] = {
+ { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))mu_newctx },
+ { OSSL_FUNC_DIGEST_INIT, (void (*)(void))mu_init },
+ { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))mu_update },
+ { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))mu_final },
+ { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))mu_freectx },
+ { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))mu_dupctx },
+ { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))mu_set_ctx_params },
+ { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS,
+ (void (*)(void))mu_settable_ctx_params },
+ { OSSL_FUNC_DIGEST_GET_CTX_PARAMS, (void (*)(void))mu_get_ctx_params },
+ { OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS,
+ (void (*)(void))mu_gettable_ctx_params },
+ PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(mu),
+ PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
diff --git a/providers/implementations/digests/ml_dsa_mu_prov.inc.in b/providers/implementations/digests/ml_dsa_mu_prov.inc.in
new file mode 100644
index 0000000000..926c446999
--- /dev/null
+++ b/providers/implementations/digests/ml_dsa_mu_prov.inc.in
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the \"License\"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+{-
+use OpenSSL::paramnames qw(produce_param_decoder);
+-}
+
+{- produce_param_decoder('ml_dsa_mu_get_ctx_params',
+ (['OSSL_DIGEST_PARAM_SIZE', 'size', 'uint'],
+ ['OSSL_DIGEST_PARAM_XOFLEN', 'xoflen', 'size_t'],
+ )); -}
+
+{- produce_param_decoder('ml_dsa_mu_set_ctx_params',
+ (['OSSL_DIGEST_PARAM_MU_CONTEXT_STRING', 'ctx', 'octet_string'],
+ ['OSSL_DIGEST_PARAM_MU_PROPERTIES', 'propq', 'utf8_string'],
+ ['OSSL_DIGEST_PARAM_MU_PUB_KEY', 'pubkey', 'octet_string'],
+ ['OSSL_DIGEST_PARAM_MU_DIGEST', 'digestname', 'utf8_string']
+ )); -}
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 70fff1b797..5a7ffd276a 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -42,6 +42,7 @@ extern const OSSL_DISPATCH ossl_mdc2_functions[];
extern const OSSL_DISPATCH ossl_wp_functions[];
extern const OSSL_DISPATCH ossl_ripemd160_functions[];
extern const OSSL_DISPATCH ossl_nullmd_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_mu_functions[];
/* Ciphers */
extern const OSSL_DISPATCH ossl_null_functions[];
diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h
index bc98202879..6863f64714 100644
--- a/providers/implementations/include/prov/names.h
+++ b/providers/implementations/include/prov/names.h
@@ -271,6 +271,12 @@
#define PROV_NAMES_MDC2 "MDC2:2.5.8.3.101"
#define PROV_NAMES_WHIRLPOOL "WHIRLPOOL:1.0.10118.3.0.55"
#define PROV_NAMES_RIPEMD_160 "RIPEMD-160:RIPEMD160:RIPEMD:RMD160:1.3.36.3.2.1"
+/*
+ * Name taken from
+ * https://csrc.nist.gov/csrc/media/Projects/post-quantum-cryptography/documents/faq/fips204-sec6-03192025.pdf
+ * See ExternalMu-ML-DSA.Prehash
+ */
+#define PROV_NAMES_ML_DSA_MU "ML-DSA-MU"
/*-
* KDFs / PRFs
diff --git a/test/evp_test.c b/test/evp_test.c
index a780325523..c8920b6f73 100644
--- a/test/evp_test.c
+++ b/test/evp_test.c
@@ -2577,6 +2577,7 @@ typedef struct pkey_data_st {
size_t output_len;
STACK_OF(OPENSSL_STRING) *init_controls; /* collection of controls */
STACK_OF(OPENSSL_STRING) *controls; /* collection of controls */
+ STACK_OF(OPENSSL_STRING) *mu_controls; /* collection of controls */
EVP_PKEY *peer;
int validate;
} PKEY_DATA;
@@ -2635,6 +2636,7 @@ static int pkey_test_init(EVP_TEST *t, const char *name,
kdata->keyop = keyop;
kdata->init_controls = sk_OPENSSL_STRING_new_null();
kdata->controls = sk_OPENSSL_STRING_new_null();
+ kdata->mu_controls = sk_OPENSSL_STRING_new_null();
return 1;
}
@@ -2679,6 +2681,7 @@ static int pkey_test_init_ex2(EVP_TEST *t, const char *name,
}
kdata->init_controls = sk_OPENSSL_STRING_new_null();
kdata->controls = sk_OPENSSL_STRING_new_null();
+ kdata->mu_controls = sk_OPENSSL_STRING_new_null();
return 1;
}
@@ -2686,6 +2689,7 @@ static void pkey_test_cleanup(EVP_TEST *t)
{
PKEY_DATA *kdata = t->data;
+ ctrlfree(kdata->mu_controls);
ctrlfree(kdata->init_controls);
ctrlfree(kdata->controls);
OPENSSL_free(kdata->input);
@@ -2757,6 +2761,8 @@ static int pkey_test_parse(EVP_TEST *t,
return ctrladd(kdata->init_controls, value);
if (strcmp(keyword, "Ctrl") == 0)
return pkey_add_control(t, kdata->controls, value);
+ if (strcmp(keyword, "CtrlMu") == 0)
+ return ctrladd(kdata->mu_controls, value);
return 0;
}
@@ -2805,16 +2811,160 @@ err:
return ret;
}
+/* Calculate ML-DSA-MU.prehash() */
+static int calculate_mu(const uint8_t *pub, size_t publen,
+ const uint8_t *ctx, size_t ctxlen, const uint8_t *msg, size_t msglen,
+ const char *digestname, uint8_t *out, size_t outlen)
+{
+ EVP_MD_CTX *mdctx = NULL;
+ EVP_MD *md = NULL;
+ OSSL_PARAM params[4], *p = params;
+ int ret = 0;
+ size_t len;
+
+ if (pub == NULL || publen == 0)
+ return 0;
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_PUB_KEY, (uint8_t *)pub, publen);
+ if (ctx != NULL && ctxlen > 0)
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_CONTEXT_STRING,
+ (uint8_t *)ctx, ctxlen);
+ if (digestname != NULL)
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_DIGEST_PARAM_MU_DIGEST, (char *)digestname, 0);
+ *p = OSSL_PARAM_construct_end();
+
+ if (!TEST_ptr(mdctx = EVP_MD_CTX_new())
+ || !TEST_ptr(md = EVP_MD_fetch(libctx, "ML-DSA-MU", NULL))
+ || !TEST_true(EVP_DigestInit_ex2(mdctx, md, params)))
+ goto err;
+ /* stream the message */
+ while (msglen > 0) {
+ len = (msglen >= 15 ? 15 : msglen);
+ if (!TEST_true(EVP_DigestUpdate(mdctx, msg, len)))
+ goto err;
+ msg += len;
+ msglen -= len;
+ }
+ if (!TEST_true(EVP_DigestFinalXOF(mdctx, out, outlen)))
+ goto err;
+ ret = 1;
+err:
+ EVP_MD_free(md);
+ EVP_MD_CTX_free(mdctx);
+ return ret;
+}
+
+static int pkey_calculate_mu(EVP_TEST *t, uint8_t *mu, size_t *mulen)
+{
+ int ret = 0;
+ OSSL_PARAM *p = NULL;
+ static const OSSL_PARAM mu_digest_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ALG_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_END
+ };
+ OSSL_PARAM params[3] = {
+ OSSL_PARAM_END,
+ OSSL_PARAM_END,
+ OSSL_PARAM_END,
+ };
+ size_t params_n = 0;
+ uint8_t pub[3 * 1024];
+ size_t publen = 0;
+ uint8_t *ctx = NULL;
+ size_t ctxlen = 0;
+ const char *digestname = NULL;
+ PKEY_DATA *kdata = t->data;
+ EVP_PKEY *key = EVP_PKEY_CTX_get0_pkey(kdata->ctx);
+ uint8_t *in = kdata->input;
+ size_t inlen = kdata->input_len;
+ uint8_t digest[64];
+ EVP_MD_CTX *mdctx = NULL;
+ EVP_MD *md = NULL;
+
+ if (sk_OPENSSL_STRING_num(kdata->mu_controls) > 0) {
+ if (!ctrl2params(t, kdata->mu_controls, mu_digest_settable_ctx_params,
+ params, OSSL_NELEM(params), ¶ms_n))
+ goto err;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_MU_CONTEXT_STRING);
+ if (p != NULL) {
+ ctx = p->data;
+ ctxlen = p->data_size;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_MU_DIGEST);
+ if (p != NULL && p->data != NULL) {
+ /*
+ * If we are prehashing then calculate the hash of the kdata->input and
+ * set this as the new input
+ */
+ size_t xoflen = 0;
+ unsigned int len = 0;
+
+ digestname = p->data;
+ mdctx = EVP_MD_CTX_new();
+ if (mdctx == NULL)
+ goto err;
+ md = EVP_MD_fetch(libctx, digestname, NULL);
+ if (md == NULL)
+ goto err;
+ if (!EVP_DigestInit(mdctx, md)
+ || !EVP_DigestUpdate(mdctx, in, inlen))
+ goto err;
+ /* Deal with the SHAKE algorithm not setting a default xoflen */
+ if (EVP_MD_is_a(md, "SHAKE128"))
+ xoflen = 32;
+ else if (EVP_MD_is_a(md, "SHAKE256"))
+ xoflen = 64;
+ if (xoflen != 0) {
+ len = (unsigned int)xoflen;
+ if (!EVP_DigestFinalXOF(mdctx, digest, xoflen))
+ goto err;
+ } else {
+ if (!EVP_DigestFinal(mdctx, digest, &len))
+ goto err;
+ }
+ in = digest;
+ inlen = len;
+ }
+
+ if (!TEST_true(EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PUB_KEY,
+ pub, sizeof(pub), &publen)))
+ goto err;
+
+ if (!TEST_true(calculate_mu(pub, publen, ctx, ctxlen, in, inlen,
+ digestname, mu, *mulen)))
+ goto err;
+ ret = 1;
+err:
+ EVP_MD_free(md);
+ EVP_MD_CTX_free(mdctx);
+ ctrl2params_free(params, params_n, 0);
+ return ret;
+}
+
static int pkey_test_run(EVP_TEST *t)
{
PKEY_DATA *expected = t->data;
unsigned char *got = NULL;
size_t got_len;
EVP_PKEY_CTX *copy = NULL;
+ uint8_t mu[64];
+ size_t mulen = sizeof(mu);
+ const uint8_t *in = expected->input;
+ size_t inlen = expected->input_len;
if (!pkey_test_run_init(t))
goto err;
+ if (sk_OPENSSL_STRING_num(expected->mu_controls) > 0) {
+ if (!pkey_calculate_mu(t, mu, &mulen)) {
+ t->err = "KEYOP_MU_ERROR";
+ goto err;
+ }
+ in = mu;
+ inlen = mulen;
+ }
+
if (!pkey_check_security_category(t, EVP_PKEY_CTX_get0_pkey(expected->ctx)))
goto err;
@@ -2824,16 +2974,12 @@ static int pkey_test_run(EVP_TEST *t)
goto err;
}
- if (expected->keyop(expected->ctx, NULL, &got_len,
- expected->input, expected->input_len)
- <= 0
+ if (expected->keyop(expected->ctx, NULL, &got_len, in, inlen) <= 0
|| !TEST_ptr(got = OPENSSL_malloc(got_len))) {
t->err = "KEYOP_LENGTH_ERROR";
goto err;
}
- if (expected->keyop(expected->ctx, got, &got_len,
- expected->input, expected->input_len)
- <= 0) {
+ if (expected->keyop(expected->ctx, got, &got_len, in, inlen) <= 0) {
t->err = "KEYOP_ERROR";
goto err;
}
@@ -2848,16 +2994,12 @@ static int pkey_test_run(EVP_TEST *t)
got = NULL;
/* Repeat the test on the EVP_PKEY context copy. */
- if (expected->keyop(copy, NULL, &got_len, expected->input,
- expected->input_len)
- <= 0
+ if (expected->keyop(copy, NULL, &got_len, in, inlen) <= 0
|| !TEST_ptr(got = OPENSSL_malloc(got_len))) {
t->err = "KEYOP_LENGTH_ERROR";
goto err;
}
- if (expected->keyop(copy, got, &got_len, expected->input,
- expected->input_len)
- <= 0) {
+ if (expected->keyop(copy, got, &got_len, in, inlen) <= 0) {
t->err = "KEYOP_ERROR";
goto err;
}
diff --git a/test/recipes/30-test_evp_data/evppkey_ml_dsa_siggen.txt b/test/recipes/30-test_evp_data/evppkey_ml_dsa_siggen.txt
index b05a764a16..ec26755770 100644
--- a/test/recipes/30-test_evp_data/evppkey_ml_dsa_siggen.txt
+++ b/test/recipes/30-test_evp_data/evppkey_ml_dsa_siggen.txt
@@ -1967,3 +1967,34 @@ Ctrl = message-encoding:1
Ctrl = mu:1
Ctrl = deterministic:0
Ctrl = hextest-entropy:3FD850EC10DC8555D77B36ED6FB9BC599D1FB4044B214FB4171570865A0A0E07
+
+# ML-DSA test using external mu with no context (Using data from ML_DSA_87_345)
+FIPSversion = >=4.0.0
+FIPSversion = >=3.5.0
+Sign-Message = ML-DSA-44:ML_DSA_44_8
+Input = F0A4A18CCF982E891F3E917327901F738390E17AB95FF1FB7C76835038889F73AF0D244976DB622194FADA41149E534ACD96B92338529814EE186832B9C383F980A5117BDBED093DB31ACD5273DF13E5A83D14BB821F18C188FF368019163C614C4D202783FC88E2523C425114211425988148F525D047A0CD957763CF5B79F66BE1738704AC66F133398BC9C0E557006306DB920CA843C0D524D4EAEDBC1BDE0503376B22772B7414559D68901CC5A1806520EB397FFAA24D96FF2F207B47D1A21CE68687D76583F21511074B9056D454F565358DBEFAEC8ABE4388FB1D8A255DE8F9E7F11A96E69AADA9346C8461901EFA4DAAA1A4BA9B04D8A11EBD00319B4A7D8BAC2174CFA2A006D94859FEC52BDC01882CB9D4A3E8951A5CC1BD36EC74F6033331B03E92A727077F448D4FB5609A5E55AA75289F7DAF3BEAB31CF7AA1F4B66980F5F5F8A31ACFE55585252247FCD78B9675638DB8E1292913A6F3EA57C60D197B1CF83D853C6ABE350B7B11040765AF1C1740AF1E318689237B350DD8F4F0C0A63939ADABF392D71471BF3F7A5CA448A4DCDF4896A342B4BA942128137F3FF44AE57DB8500A7BFB6FD74250E6312D54A3588D08E93BA623E189B817EEF2BE66D2A57097B147A131D5C8843CC7343E62BC002AD1E60299BB7D2F0FF14C51B3E414198524A3D964CE2A631B72F71342E17AF47992E6C27067CA14711615CB94F14B7B501A15B967259301A4566A63DFEE3515CCD142FF5F6423AADE0B0EF7CFCEB975E6C70187F737D5BC8236369402A82F701DD59A5E67223261921CF13CE22547DAFA7D0BFF6E52FB572FBA1191C5C5B7DF47E12D0799A3F2E2B67FDAE29F056693D7FC59CA8767613AED34026A04F8D17D0F803248C91FC744378356DF8DD88B2214269AA732B76F7A39E62760F82A761D4F68CB2A378ABA62A39C81E6E1AB211494FA848B1620D5DABA69D383826AE0B97A487ABEFD202CA8A1B0C8188219E40D69A9DBE8E3E065BA17B84A54236C137BB51400C5A89ABE70E3787B7BEAFD9304CDF6EFA5D695CD7B0E2349AB9110111D3CB306151EA204594FB28EB15B63ED5163EB744479402ECB291F77BCE40E236229C6EFC84EF6E0323B68F60560F68A75D03DA9C4724AE41C7B551C0EE44FB235B7EFA7E5D9BD8F1DBFE68F6D6CAA10452E5BFCA633DB41D1BCC975DE04A028BAC652AF714BA8C1CCD3EDAFC41C90D5F13320AF0CFA88885760E39EAB8A62FB5FBA9BFE329B5365CD68F72ECC32B2C55B650040FFFFE346C4954AD4A1762AB402A9E286D1EC4AF18410AEF0414D2DA2359514D64E65A532316DEA8D9CD7866C59386BE9E4F85A604B4EB6953214B4BF12E7BC37E283FC7E93C77F3C5C86303AB5D10505FC3089F1CCF75A3AE43909B09A3703DD124C4D6E4E681A6EEA2757A5799ADE4BC55EE616B4CE743FE24AD6BE365953B42BB9D5942CDA54E2574EC041373C85BAE74009FDEB3F96E7D07A48934A61BDAA788ED20F55B58BDF4CD1564BB644AA2AEE211FE403CC330DB92B49A8B9A1C3AD9A7695D16763EC07241DCA2E4D50132A12F2450E1F48110E60496331CCBFFD070F93ABC97ECD2D233F3F2C24DAA3571C6D1658B6B8C341363004AE84871B36503294D1947C70307BB8FDD1507E51130ECDEA5CF3002FABE1281839AF48D611108FAA75EDB21506CA0968D654B4694E69B3E75AA83BB5FAB88283B9733A49D48F5295C9BCCAC6E6288288B90AD82E74E18B73F91D6573A6496730B4A84D9952C263BE83294AC9EDBCF4C01BD1FF51E9373BF70C8D0C384DD5BF0486A883EAF8AE6AB85CC165A0E887F10555A359655DFDD938933637BD7AC08FE8CA2351DEB25A6365375D4FC27FCF3D344A76F8CDCE8937E88736455C13A205AC83D56015CC4B7C5E7117EE85D6607ED8924B6F706D13BE3A492E2A3EB8E48D25E32622EE97DF8FA9F765D18CB38116A164FE3090C7E8D2B6B407F54BE573C442A49216FD01D3C6D2EC37594FCA72423E7A0FA27D2721C7FA1334C26EA26BD150A5AC0EF5CF80B1176B67C28FD2F5FB8676A7B7C6C479FEA397D68DC2FB324ABEC73C35C5F8B8E631D676E725D55FD0A0F7BDA0FD1A989CEB71F7BEA5C2978E528DA7EFB396F3342794D26F5155609B568C405A7994C856A6EFCCC93CE07B0445D84C8670B65CE1523745B5714F7CA83B249F20C6DD56AF1418A60078456B6D5A43BEDE0D9BCFF5AD0727D043DC6F4CF0351E985DC557F4B64A1240AC09F88A078991E09C7CED49CCECE62ED21CF92659421A60DF72F6EC245B5AFAD59CD2933609ED3D02B89F1AA5EE1B44B18C500144F7DD8E9D2E83F4C6A4A93B879327E83197AB221E3E2E26C0A78F5377BD90CD86E28E0D75142AD12C0B7B30EF6FB2354182F24259D6F3C3055292C80FB00031E5889B49C8697B33DA050911F13389636F9F1377EF7656122C4676F04804E6251D6C7478C311C4550AE1B8BFF920198C42DACCE5E52D9E5A015E6B9CCE761592B4F2321ABFDC4E3C74F8958DC0677097E335665DA79B274F873445015E494DA06F791769B7FCC1B93864810C6D258E566FB9D60C26851BDC6BEA0781858690F60502A35EBDE041B9419CE2B37783398EB5DA87FF4F52B312D7D549F9B603562B17F89FA6A44420F7A9155EE9A7E3A1B5D3BD94EBA1B8EE94A95792EED7FADEA0768A9719391D4C4AB639B82DE76C6A6AC421E484B3CCA5BF11D8B3F08EE72151434646124E65D7C84248A85778007D5D4821D6094C11F6BC9CE58F10D58E936E81203E54C9B28C9A3B30A6BA894294A0F9897F2255B27171AB0FC4DB001308E8F3319A0CA166E6C7442BBBCD93A1CC61B09F23EE6796A02B3CDFCBB089C0B5F70E3A5C55B445302D77E6B8F5DDBE4176B04E9BB60A17135F975E51D1846EBDFD21998BE5188CB04C552E058AE637798EA1401F37CDD7808A1F64D26491854B7D719F2D573CF2C6DF86599D9AA132B479B2E356DFEC2F9C5EF0C8D7DF5C36A5E2B8A6BD9AA13C7208200B675A268142E7343EAB377804243F39DE83BEFAE6DAEDA3C41BF429F8A85E2ACEE4106EC62D7477C2B6372459B6D49AE793598AACD7041BC6105405CAB9FA75CF812EE8CF16CD281E6BC1FAE6A3C41811BED3E32E2DBBFDCCDF75934592D394A12DD81DDEE659BB7677B868C4D0FB3BB3A998AE4EAB93A7AF0DDF28C2439FC6C496CBE1EF87C1BA6D228EC1C56B0B9BF847E6E569A82B7D0FB79590A0B6DE05398DC3D06CE3839A8F02466A18A35F44AE7FEFA058D954886738D5631250BCB39CA76524AFBA1BE7F83FCEBE3092A5D52AB7E16D7A6ABCAEA2638A78DB7BCA5DB0770CBE8889FC9FDD5A1B0577F6FBF1A1A0EEECAE7D2A9965EDBEAC49E932034536B52C84577619949FDAA504128C510FC4620D7F0F8465A39105FE93812FB7FF1F2804580BC613AE6DFC32E2DE2A7BEC522D69A121226D3B0D385A6B247780C2CD732E8E78B33D3E033E917CCF72DBEB71230B11E275723DA67D0B490416ED0F690255BA05ED0F6D4F5C16A35003A818715E820176F9C5132D246B78108C9A19D1F170779B5D39F324820BB7989F6211E52BC59E30BEF0E979F1073FE033EF5B13AF32C2259D3446FCB2C077D34F1434644BBBFFD7ADC914C3A614CF9426B5F4F9EEF6ACD3D66DA11BAD4C09D3C48F9EB0AB669628D93A1A4C16A8833E5FF448CC943C52DDC4DFDAE1E4D911C8152B6C29532F886F72993631D471A3A14F85F4BF041EFF30A8E1E023D618C21F7BEECDBA59D2C454A34CDB937D0BCFB503A0FAA6F88DA6D2E5B5FC17743BBC5E2CA34D9426A7524A2CED4CBAE1E4F6A509646F8266EEC2C982E461AC1CE141F007CD7863A0C91963336185BB4729C757621908C62BB41B9516FE6D401A85ABB2A893D4B93C58BE022A9B467283D9B7E0348C31C8A3EA83DC76A4CC6EDB43FE79D419950F19FBD393D1ACA0AF909249E36D4884190F8FCC3D92EEB50FFC9FBEDB4AE704DDBD1528C8A53158601816CB3BB4C8FE9A1F3960E8E7422432C478C52A1CC8C66BCE0EAC6BD2A3526078A38FB7217988C1B9DB940142D2DC69710B6BF7A1C11397DDFCA54D8AB865B34AB3CBB3BBA53E307066FB30609458734C27144856AD1434673B5C6FA9F938B50C04A5AB4628809543C294B6058AD32A5C5F46B37D71B7E05F11EC3C3A81F3052CF0BB33CEB4C282EA81BA1E9D7346437796E1AD0E65B5B738646018BB6B26FDE122395BDE4AC14DFC45192338F054CF2A97A7617FD437665C897E9FF2CED7F3B4E30EE24270EB1F08617135CC0240B70462E7836EDBE0E0147BDCB969C9AF7D8AD52579B75922121B164D60C44BD22932D859A24C52D8DA6CB64B6493063545C24581AE2B839F5E0533E647B64DA66FE06FBF9F1F61C30C83510D46B73746015DF3AE7ABBC298F27779141F6F5C2B6A0D029BD907834970B03D60B48606D7194345A3E1A7B3727C48C71268E28349E405835D8E409946F1D5562E2E3ED1C999D1F692B94030ED5A90EC725E852EC6EE6BAF964F90A91FD60EE412950E0368BB15A787B961B583ABC2EE37507F45E5F7EB3C31A79CDF3169A0046ED3ECB048D84CE1A47B26B21A9D47231ED212BFEA6DC7FF4546DBA59CBA12BE6B8C42DADEE1B04A1059B3ADF730830D4D399954181B80A1AEBB91F9226D3B58291DF29DAA845CB8262ADB6B1F76AAE7400E02D8024BCC6E29ED6F2B99E674FBC4C3149217D31B4001AB9FE479BA9F1DD73BEFA758F01B4DB00A70B5D46E53C60F5525AE8482133FA55BC82D897C689AA34389EDAC8440EA6199243B02B0ABD9C91950E245325161299C3B99EA8CE6D435736680381CEF60376AD0D5F34CCBD0F5A2AA6050AE1E09C3C20A9BF48855427F1777DE81A18C6389B180534C2523242820123FCF55FBAD51F831B4806911C7CE2CFB1660F3F117E279B64C5B6001F5768AF51640136F6A3DE8C282B96EED3FFDEBB34E2AA9BF1D3662533EC02986148AEB236D991FA3817B15C02011377A35A87675E1749CE0A9F89E25F3A4DA800E08E3CA339495B673CD37DB0B53B7AB6C65F81940379EF8452EC7F35F8370C9562113D7CC93E02DB1C6AD707CD21F61FBEDB9747E20B121E0A05E13C2478D114DB4BD2AB5A39915DA663A01597FA8A3428A4ADCA4BE8E034BD98B55B9BA93FBD7F6F0157551EC36BA6E32FC340CFB6BF607DE664355184DDA227CA1EA53740D627E0E44EBAEAB10E09E21CD0C6835C87B65B92DC7F669D64BEBB42072DCC4EC2B3F5469C566D1AF35965CBD2B64299E52FF8EAF4FAF3CB6D7D3BBBD5DE0B74D4B5D2125F688B7FA14F8CCF4BD41A9F7A912012A2A10495C555AF28D60E6A8029B19FED55BC2A05419A8CF88FB9211CA611CA3F5E342573A228CE8A6ECD192D471C896CF0C1B044D07E47C815A7BE94E01DF6EF3CB19CB21D90AE0600FA848962A19F4D4D01399D0006F0FBE64304512ECF85F2567CCEE66AD76D2A5EB14AC07A2E0B19F8EC6465104D6C0B9707898BBA0DB74BBAFEB76DBA973AB26649EA6C11870270CEBF5FE61B40F10DEA897EA489D474491E5C477C9ED9D857D380A5015B569415AC135F2F56D5A8027B554793EF0D320C4B46C3A69CAE832DEEB5BCC3436C63311C6DB2BCE0BF8FE102E42A2207C60D8F43E86EBF88B1835ECF3FEEE5B7AAE7A5784F59FD9C76F5B9054B57200C526DDC9A736E2A12DB81766B4F32D579942C5A04377575A3A802B212819B0927EEAE0115687AE305EBDDEDC5FDA59F28073AA0547F0EBB54824515577415F0C9467310EF5BDDB43581DA13F2C9F8E8BD5A26287164509BFF9D5CF21CAF65DFFED0B6BDE563F94F30AB05A41A0D58B17561C4BC5314C080045874E9AA0D603A26F5214B7A6D4A82167A3C31E454F4738607007CEA8BD41F1B312B30E510C96DDDE9F90E81DFB975DEAE472833DBB12F8959F9747A3D241F04A22A34E329315EA2700D96847DCD7F7C7BBA0EABD8C246660E59166E6CDDEA807D8C4E5F35F099BDF200F522173DE26F68E05828A49D90F18969D4C2EE51A3662FAEC2603CE75CD4E2B24868B3D0CFE21C04BF0F44192E3FEC8B4792C47C6F3B69A9F2E720FF7427F09C41D2DD7C7CD1FB983E5D8C0D0F2295E25FB10F4C70248426E52FA1CB7B5B614C1D533CC4224B3C57DE35A0F4614FD894B29B5AD6740DF07D96FFC8B928DACEDF51EE5DE8B6487EBA87845A61A21B25AEE9C9A54A3BB33B7A3BF20CA006C4DB3997431087FDC15D58E5958AB0872C70A3A0CE7E0313662261765BA913A164501134E18D12BFC67C67CDB9B05E15B1D5E506AE0456D6101A2555CF4D7A292A1295F49F615C127651A42455132E8B7831C4D5D33EB3F944B1963DA088116AA1BCE1E73BD64675AAB84461FE9A88A658C5DEE0B81CE60CF9D1CBE6D707CBBBB3A3BFDDBA865057117CF2B94BADCB0F976D456B43C40E619A9B6E8C2E58F29D72900EC9A920149D3022A5C77D80A8685DF93ECAA43D4BC26D68FDCF24817AB7447039F3152908D638EAA88F867BC9976B920DA90F360DAC8324F2B2F64C88D8B12B42471813FB6BA24E01B2E9CEE6601BF41BAC5A2CE286B752061094AB25399C915D6DDBE58C863F67D5F2BDC5FE8A35875E1B86256A0F991D2DA49A3AD27841BE77E7581CE77910A1D2C43B7CA4E4F6A04B672466F71479B689C25F9C28112C116E61A9F42A81EE82827D39E3C622E08C41C24CFF89AF1F21DC839DD5FC921F0BA8019E847137639866AD161735553A26985C2E481DD5130DE51B2C8E19553098956F39004979E86F0B6AD49CBD053C0912B62B2C2BB47AE202E8FD5AA76E023B4AD41C725E4CDDC38859FC98B715F6E375D4FD1C907A6CB1E1984D273C71C908038BB2B4E8B1DD10D3BBA797665C7A4F0F3ACB717706C6FC8805243C5DB07F30F163F53164061D404574225777F4B705D4AAD8BCA1BB6BF8BD16BA06F22285F5131E8C1D624FF9E7F76C2CE2FE8413420C165EE2A6924E7E8157805DED15AD715720DB8A37C31C338C7E4FF039EC4F3E74A81BA094DC16EEA9C4E17E828566475D65CF4DC287EA27B6F3BEBD81F84262D503E2545C0AD7266E9D1221EBD2264A3CD1E87B4F254157FC77AA6B02858CBBE6CD51A9D0A21845E11E7203DEE79F615EC1420DB3282F26ACB3F67450B944E1A0F0098B34850C7EDE9BA47E4BED0AF3ECFB2447662DACEF58852EDC21F7E1869CBEF0E9DA8C5C8309A93A5F1787ACDA8F176C323FE1A091D9E3179109152719FBBC06F8C8990B582D642907BE85D36F1637F14811DC42A3B7B882E6E48AE732B0F9E5EAF7C21281607D0E8185574A5CCAFC1BF047D812E0A7FEBADA1A6CD53CE5AE5E08E29D6B3228392123BDE9A171939D52BAC7CDE00F02C78863F53ABA82A0CB6B26C9DDFA1CB6E3BE28E715A72F61AF15D5EE760851D428E6DCB92215E661083865190321A8734130CF1FB537E8A441850A211C8BD9308529EB866D5DE6B3DDDD1EFD4C71E6BA37F2795039D6FE136D4A1709C8C43F2736B18AE9DCDF65BB8009F6B44A55EE598ED3E65BB210E20F13C766D31867BCD49269533BCA758E1D4E2C601BE612DAD31E30708598D1908AEC555F6BFDB4E7E125DCC725290D8AFF597E2D439BCE64733263880049E2B1598E7D485474E79C7B6FFCEA4B850D37A631BA67492A1F0735A9D353993057DD10284D3360B3BBBAC1C85762A2040E4686FDB85727F2D9A6C1DEFC102EA964D3D29FEF59CC93345E89E25A22D0E0E531BA02FCFD032A7CFF811AC69512449C1CEB28428835C84982FBBC82A4F25BC088730E54CBC54FFC1528242216162F1A0B874C7D916859400FCDC14D003FA523A430DDC42D4F236869BAABE275D7164448BCC14916FFF065383BA4EE2EB8B6C10A99FA2D5A78D7CCD0637D81CE917CB3BCF006B700AFBD9E5D383D041CF4CBE63C7A74F18BD3344F73C2F9D49E4E9E7540C1E3FA9FF23B7E8B85008FC74A8FCA371BD06C07BC749FEAA5A93C2E49245EC7044EACBE03020CE67CAD00731FF41DFF271DC2AC657A8F6DA2FDA1FE22FA398573AEBC678EF154DE7CD2814BE241E747
+Output = A029AA0426F7A310A65CF259255F8958BB60DDF85D6F5CD6F40A5E0B2BC3E397BA91289D2729179956931EE52F32067C52134D288FCBFAD05CD3BFE1271C9DA66C309B7C54E5E9B11D68ECECC1CFB3A12E20C2FC8CEBADE805637FD50A5D7B83599C5B8EDF4129774733B62FC2FEE413348CEF3236E5DD14DE0E5E3099D46EEBAFDC0058B39EE718A3345435C2FFA2152F76F254B37E0578F758682A5DAC9E4FC5CCBF470DD9CF089F0BC4B491BC15EA627A87220F9DAB5E4B7690D1AAB724A20BB05768BFA500E766C4BF57DA2E61850791D0A0D150CA129ED9EB20F68F92750175A0C3D8934C17EABD24C65231F0B1CE66592502A47AEB076D19C94BD3D6B67B404D6BFD95B9065034752FF17357D6399BBEBCF868273DA2AEA74E9451E97C80F14F48B7D6D5E3BD96DF1476625A39C1F9055CDF990B7D0B04B85F6FF640659EC8F476D95FC1EF7028312E5F9AD0E0AB06C0615517F5F43F699B7FD34564CB0FC224BB8474D74ECEB7B6D47A8625356CF5DFC9BC84CFC62003CC9148CA479FA350BF60356C5E4A27434277505EF1C15C060DD1D34F2085FB342BB96821974761E8DCCE54F40F0451E3854561C45D7A2503C416BEFAA7F81C2DE25BC6968FE5B07B9204DD1B3AF89E98737125A4161CD2890E1ADB0E03C53AE2F077BA5515AE4BE0EC1E290A28D08F8596E7ED7414935CDC7EA0F6CACDBDC30C6D5797022AF0260C648B52AFC661D1EFFDEF33FE861C8972A835F85EE5CCD2EBB7AD53E908A860874876C8843E87140F77E058754D5664FA9D7DFE5E7802218A038A9DC1575ED6ACD5256A82F31F177DDB513A916AE9A55B304B151D553AB4DE7053FD642B32AF1A90D40BE2132FBCD8E6817C4C7CDF0D9D566549B49E8D3134D3F15C756AE08F1595DD282B42E0F280126B6EF3FBBF306A8544991AC2501ED1488378B4935F540FDBA2F7E5989B1E91968CCBFE9846A88BB77C8693732416C08C6BB58C57AA1ED2F0DD788CA1E6E2CD9144C7229ACA28942C54A43CFCAB47B05B456E6CC32852202D0A0D6FFA650C199B6D5E17350A90F74827BDBE0C0F7A9115DE3BE566C8C4F584780B96DDAB763C5BF7300A0A377C69B0AB418AAF245654E8B4CD36668B03DC50A59F344F80C95958E5B4571F500827FC18D594D688013D4627013BF37A6D2D5BC043187011C058806F5112766CBE2A5A89E235A73B6C99B4FA91B41CA048C930198751247FFCDCDA9507D3EB5BFE2A6F70AB77D028C72CA697C5F66537448C294E2A0EBAFC9172E134478D5D542CE38AD66691130EB25BF889BA7F702BC329C26D2AA177BA828D0E3753713C1F1DB1A9051D339C7BF5D927906AF9242C2166B7001B4828590ABC4814C11018187419F2DC56A2A35BC56C1A1DD96E4372A4FC8A9B727992C46C285BC9C594157CFC7EDA0B171BFA2E1EC7359C68E9FBF9E4F16503F2276B5F6978F760DC72811EF628085723A14AD4085F38CF28D952179EEAEF02ACDE2F51F522C0E53080B144E295B425413E435998BAD98060649DBC0A520BF94B95ADD98D544D7A94D6447C13CAEE32B1AA7EA8094A8BFD983BA9A26D86BBE46068B20727405717D19605E7421B674B0CD0E953F93906CF022ECE9EDE561EA583C5F4E577BB4682E2268C8664767A59CE81D5FC64AA9C89A592D3FE9562CE680C14F5097C480AE76B0342357882DCF85A0B86EAD8234BA5F1A17944A0D3AFCDE6924F7905E6612D5DF60C90E560FE4ECE200A1BD77362397164B4076A9FBAF3EABF5459127B46FB2FD2D2D4526C639178899A596ACBE132B49C0C210592D9CF957732B1A31F9D5B90F17FC76C2AD03A8E2AF0BEB340594B14FFFB9EAF1991CDF8FD5DE162296160286A0EC81D47185556E4C9B1AA39F80C83399BA0F65011AA11708FB7A02B2B4A8DBE4ACEE3D14A978C097219125141DCB0B00F50F02967B3274698DCB0D1F0CF255024F1AC8F010950A113EB2622A7828402E391A753B5B61F6B494B2011563D508E3C6F79C8CB425605E1BB25A8DD19DDAE6CDB45C108AFB89CFF206C82879866D9E8FAC5388716172E43F26A8B8FF3FB216EEA37DC0E1088315357127F2D108C1CBE4F8D5322B425F3F97A7BE86977C370C859FFC5A8F574C2EDD37B32279C62CB21EEA21D2EDE1FDB498DC0C6C8E9EAC852F1ED49B8BBA9B7D5BA404B1091FA497C0589C54A5327E39078B996B3E092367DB99777DFC7D1E997CF67D71CB3079527945B9A636A27B6BC2C3FA7112B1AF4633DDD50FEF8FB2E44B75E50E2828205D8F7AAFF4ECC5AA361B484F1E51E8B50BB9504322AC2C94F440AB7A845F801AB00BCBB047F2F0E2A15F7E3CF2C7C2C62F99C68A0C377F3D52346017CBAD4BD42F83E624CB60B09BDE4D6418EDCF9201341C60F5ED70DC8FE1EE919B247EE6F4A4BDFCEA9CDC019C64CB067A69EE74DAA184CC30113C532DD88B8D1DA6DB02047B1417AB5135A97F420448C3D898B25A59631DA6B20A30C05358D7B61285A6D6459FD25BED5B9E9C9058CFBBE6876E40ED91A41BD6D70FFC89C8322C885020EE45F7F15C6D959927CB41A86114CB9813B4345DDD4C818E04C73018FC745915B257DF7D4A08EF9E7452124F587E2BE9FA5B60431F604D3DABED49F934E251772C16BFEB4DD50A7BB4DEBC15BDC07C9B64F0FA2E134CEFA5581D07C32256F4D2B5350CBF1FC5BA06311033879B98DDD39A3A64B383245703EDE83C6B763EC9574ECBCF1C0AEEE559A652BCE34F8EE052F6F3D5C9A183B30FFBEA840BCE225CBF7406433DB56F64208713A562531C52F328D030B7EC4BF053B169BF3A5C1CB2D1A2312FD5C7C36850D1613CD93FF74D54718646CF24A792485D3D88DA1632863D56E88EB06212ED46B075A64F9AA3D95099DC2D8FFA27936398EC5656BFD7A09F80DD5D23D3A59A3B76995711CBCBBAEBC9C3093041317776555726BA333B564B43AF5290DE27187484DDF8C1001D474C47D48DEDCDC01B820FD25E306C021D194B415266767184F69210BA95059286F411C61312B514626D749B2738D2878593910D6063E5481A416BE314C82024B54BBB8C4444A2CA8EF5B3B9BF8FBB082CDA6DB3AD99E4225A2CA1F4B30465B66B8919D0613149DE3277E8966E56445D96D29A51DBC9A803040C703EEE75C9C6025A27B09BB373F3F616B440693A14B8DDD1D7F8560A75E4AABD534D30CB50944703298DE19E9C0F160A7E5AC59DAB4DEBFAAB50C8BAD8E50E1945318F5A9C6BB6FCECC4A47CDAB226167A91E7030522FAA266CAFC58F8D7E08A52C2E5D7E9A84E78575B3E512143B838A8BA8B4D4E8ECEE0C1E32384C4E6B819AB2CEF2162C3C3F40425C63728E9B9DBCC0CED2F0010C365C63697B8292ACADC3D1D9000000000000000000000000000000000000000000000000000C182937
+CtrlMu = hexcontext-string:
+Ctrl = mu:1
+Ctrl = deterministic:1
+
+# ML-DSA test using external mu with context
+FIPSversion = >=4.0.0
+Sign-Message = ML-DSA-44:ML_DSA_44_1
+Input = 1E5A78AD64DF229AA22FD794EC0E82D0F69953118C09D134DFA20F1CC64A3671DDA292BFD9AC708D156639FFD63844C793D849C7743B4FB1195259D46D3801DB2EADCD31D515A93F60C84CDCBAEB8739777C54C826651FAAD368D582EBD0556242F4EA5D71BB44E8A06F2C94E52319EED545A4C29A44D0190A3FED19FD49AE8C5C32FFD4DA5C7E1C3FEE0CF67E2AE9C082D2CC9A5DB29441C9C4B9F0D883D6AC65AABECFE49CC1A1DF01752F8699DDC42FC59DD4F614371B02E3709BFE5D1EB41D92905CEE18861FA7C3AE94CD97253A60B84EF638F43FF507ADE86ABB69658D9643C6ABD33E7C7DBBD5C9350A3EC8EA4E2C0463EBB4F8E43D639B6B9642F571F9B8D295817367651B39AA30D97D59A2187EACB1DA5E4669D9471E3BA498B76606A2B363E0E068FADDDFC1311DD12C5C9E3F044DA9FB13AC1CB376EA6E4444DD6BC80205D9F8E11DCB56ACED0A621C50328D7FE975222F08A78D5CC2FE34508ACCCB9439225EE91E29796FD804BB397875741EBFCF11A8FF022463C3170A963B2EB1A943DB91C5514C4FFB1E22312D0B5073CD94707F3B760F6FCBE7AF366E2498920A32C68AEC42098F3E0B87B86FCE0B12F9852F2ECE38E36FCDC7D4CEE08B5A83D791FF391E4AF9FCC33EB81FE5DD36A0B58FC1C8A76F240A6BE4D4E53FCDCDF62A6D96A56EC1A2BA4ED69EF2424E54A2AC139CAE8184F06D376EFAC00547D26131FA6D3A80E3B4513E434D98DC5C7866A5DA2C112FFFF197A816892213E0FE9B6E7A4FBBB4F97CD925CBAD731C9883C0910EA103E341DC80B52234FCBCD91DEBB66A315F1D097447DD10520B7FA7BADC1245FC30CCB4059FD96CC529415AE978D62C1BD77596068E3E6EB8E9E0B5EC7B4C7557A651F317D241A6F52398B3B596F933C06B22065169EB4E23E4874646CF905DDCF7F0E3AB96983EDDC7FFF2DC62523F2F27847CD8286C4789FD8AB0101E6DF516741608D5295A45E8FB2FBEB1754CAE949765B3C4A6D1F3FFC60C06DC81390D86B1672810E6D695DA4923AA1C6C0D95046631492CF3A67D9D26EEACBF358F0CA54B2BBF0A5014C6E092A76B246F1AED72CB3C6C8DDE58B170727925ACB72782B54819365A806040D3BF2885A517F0164C9B63E6B84454BE81853F6B90E88508174C8314856689D6B2F4B0E29DDA36B0DD090D32BF3ECF385C9E9CE356133484A2C55FDCC8CD205ACE2325877DB15D1E69967EC56C28CD76284BE2A5C157F9D87C2E94FBF8A0465CCE795A8DB5F8833F445E3B4FEBB0B236C0587C0E2AEED428B1DB8001F1A27CA4E56A761E5E8ECEA5590D949580AAB3FC2F859D75442CC64498B91C849DDD4CD9F3BBFA9EADE1BC5DCDC03AF1336CE2CD5F1DDE3D7B7060E00823001AEC9C6594FEC802627712190EC318E6A40CCF6898D4082103714AA1F61D72B0C6EF7F76B36546C09C05B6D5802050881C667D52B9EAEF9A6250CBDBBE6407184D923A5E4B354C225E8852886E6A71CE4C0905FE36BF43014ECAAB0087A548EE8D2440F2EE9C912264B412FCEB3D650D5152800752953D8D8428FBDD57B1D671853DC6D378C321CD105893734DC82A49A803B25E17CD2B53F8B8B50F6AA27BF82860C515870D1104B197D57EE835463A38657FD3AAFFD14A524DE089C8D265E5D77BA9930362AE28D65C5DE8187EA0D9417EE2CF885273D1F3C8773635E7610F80EDFC890E88B81B3508251DD0C512CA83167129E2E5D3D304B71CBB7EE330FC7A2EAD2A4824EFB3A7C5B45B2678891084F61A66E4DB3E2D1744ADFE3B60E0E9644C9559D596C09D7C55052012EB18B1B6571C5ECD9548108882C008C16C09ADE6C7473E2A3F67A054E7FB2DECC6B0D84D6F6A440EC6B1AB8AC0993D3615857883F1E7C3C46621209C2A257F40D3D021BFF71E47E58C9FCD8B0BA54B6003CB79673C00BCE770D06CB74FD0AD943CB7300BF940018EECB583AECF3494D2D8706CD8B9BBF803DB1A00F9AC349A0F4D9CBFA07723D070A332223404980271CA18EBEAE0FB30FB57A72FC9B1A4C10FE98846D1A55FEBC35A4DD3EAB750AB0BE4B19E6E438398946E64FF63E72BBD761DD984AFA3E62369872F6D788F9F60570B17F46E1BDEE96E38EAB8C8C873F3C8C5628917A7B622D920B94D2DF2D07E272CC8D6DBF383D9A7F12C66C6BF4322D55C60480EC78C20E1F2E8F1F485DA9B9EB0780BEB4850290E7150A4C79714C112AED3088D6E2F9F38B31E2B1C924AF895F4FEDB8155D1A964EEB73D66C5D3001D05833E7D70909C8BF961BCF1B517C6AEB76A9B9832C795EC51781E0BBB999A6F10E03458F8AFFF90960D49EBBEFADD0CC2F8FF604310FD3ADC372D70203299424C7AEC922488A7B7CADFA92F0171247FE83E3D54DCE6EAFC7258E1A066C96F3F3D4A5CE279C54B004DDC462D0D7092BDD794398F72D78DA95C66586F89F593110DB79F942FFC1322CEB02B69904762CBC86A95FADA7CF6DDDE8CDE8DE819B17945D775703B5B276D8E140C69B46C353AF340893A6FD9E88227DBEF5769A237DA19E0D6C582560B51A504D3C3FE1EB8CEE3482F1FF4CB5A1C9CF7C647ACCD7262D80A876D3A188223607D05C9F58579EE229C24DC040FE5A14D10DE04796CCFAACEC948770CDA6A0724342FDA6CA72D7BFB40B37B77402B20829448A520A7578F5872702200752ED6179B1D302F683695B6FFACD0FC271C3EEB48EC3BB182A341655E42A33CE37AA811B0A3947F17696FD0F927C3E8BDEE9A15AD057B1D3F86BB2F9D4898D7078EDDC2958EEBC132ECF1C40A7B373DD3DEAEF719AACBC03D3D9F9B65C66C092D794B316596C76BE6B3C079C6B85BDE2220F60952C65053BF42585CF9D175F265B87C655A5A5EC9E800BE69FD8B03969A986DDAB8D2C1AADEA68BC360AF6CA41DBD04CA4EF5778FFBAFDB14712EF94B06B90C657693AF6A82CB5AD1EA5A79C3152F98F6D70A4FA7FEE46E683D44763877761DD97381D3B5B4DBD49D9C767469181859E76771C1249280E74A4964465769F7475EC491DC6E16FF67982EE127E62EB7CD839BDF30BFD4E26A50EFEAB51C74037FC45CA221E59CF4C697309506FCD0D734FE80BA5AE8BCA300E5767863DC97982E35DEF7BD63B54D3EF6C7EC63D5C7E1C953C74DB5A2385DBC1905D632DAEB8E84946BCEDE992AFC4B355D934578BD06BCA9534E6B1C2D9903BC91E85F01829C890D6521DB838B1CA23269E23C3C194D0164D7B577759BE9B5465169B6CC2EB4179ACF5FC6BF6B935FCAC40605E3E0552647B58DB59A36596C2BE04617EAE766396FC51C913F95B7F28D50AECF0815968D68539E326823DDB7619F5B765D21AF79930F1B796DBD79E5E8F5D978EEBB0C8756A4389960F9488A3F8A4FB97B9FE866C89C8A149CED32A0ECFE2CB0AE1C42B059D715468777151D24B766A05E66E414A68794306C655E6CEBC01076176C41A52BAE2060B38FEE5DCDF77AFB591D61FD75431DF0C26D5E7937C9D576D4E2149EB2C0BDBF645A57C477579ECBDE9636C036E5592C0BF465E86D84BA0B5365B46BC2599190DE97E3CE6F29E42756CCFDF1D429DB894ED7F844509B574828C6ED5C7EF498DC880AE9126FA7030339B584C5CCE3D240C233605A36199AC8AA5375722C1DDDDB92CAD5A6699EBCAE322E5DEB63019B9E391A7A5CCF2AA75DB153B02C55353196A1E8C768820B5A3C8F3085DD774C1B5A7FCBA85EB47EC3593357B2C4DA4CDEB70BD5377F5C1A6AE1782FDB9384DAB563178A7834ED3D6CD8682383ECEDD36B6AC309B72CAF33E5F700959B20572C7CB6449F9745B7A2C1C301E7B341126A77144F32BF0190F903AFD02FD6AE006DBF1B65955764E7BB9D830A4826B3E2B627D7439C8E1647E14B9C037F015426CB0B279A96F73942BAB0DE4907E3DC96062ED184FD7A0C955AB15CCF991E9899E68BED24BCB91187ABD4BDA92371F93E1B3622179A3AB1B930E454A77FE516775A01BA0021F7CC29456CBEAE333C79AE59DCB5B096F29BA08C0F1B89E357F46A83F1E69C1F521AD0E6B90A35DB3DC94B6D37FE73509968409EDA533CC7B3E24FA29AECFC88E6E6574F054AFF32397ADF4CE33CC6A796056FED6E8205AA6AD530B83C6F561C152EA9FEA50A550856AE20DFFDA79DE49B8C11F00911B790E4FD5A5A5256E15A5ADDF380216234A8F578FF2613EAA8C430830E1EC9B0FA08AECB60DAB24B8155D533CAA66CCD68AAABDBA1031A665B44D80CA6D4ABB78E2483604D890CDF83977B198D6E2E80D1D658BAD557D57E68FC546CB936E3FD6F62A2F9263DA4C065813377818363B0B1914289D11B19301DAA9DF24A7261B70FFD9CF01EE012146029D5EF88F32906EE9FDF1AEDF2FA0C53C0D9395205E93B71BB6BAB89524F90AC2F7BCB276DF1F7E4F0F734ADD2737AA826426D70BE43DCF5FD5D7AEA0DF6E5D38CED606CA333952E4AF896E1DF38CE3656B88037221D9A90E86F5A022041DC86D813E6A0A9D061A77A908E58D4C46457609A2CE97CBEE6738E093D04DA10BFD8516AA643902ED082A1FDF60890289925743513DF192DF07E5676CF795202A570991E816DEAD88C737945166D3D3DE95D828F07808158F6D2D7911C1DA14AC7B86A33457A861CB57BB5943710FFC70F739470C31ABB10A4DE9807899F544107B05616344DB07DCE46AAC29025378FD96130705FA8FA1ECE9F81F5F854625F3E45B96EA0D92840E3F319BE3889B025D4915CECFF9287C01FF6EF5D8E9371E805E24C404B0B20364D3002E6691A14BDFD0F89693D67418A86047AC10F7671E6D983D84C47AD355EFA9954411C0DC599B7FED4FF794A6CDD2F93EE80691BCFA1711FF32FB3CBBCD10E03B3A945711E7485E4DCDF17E206CCB38C18037F4361AE4399B23B814A7D94DDECE17F171F6948976EC4CBC9133B235B757A30609ABDF082CAC9AFB9B5E249AFA6F2530661422EBD789D3D34E4790C486BF164932C3ACF3FE7E0594D26C035ABF8AF421F848F4CD90E5D9A69FDD8632417D67AEB8D415EE912D795AD1B3C1A13073A0C489D241FE6DCF2D58B23E1167D8E748A41D43DE505F7E044A37528CBE4E8BE4B1356F298A1174E28734756C9567A0BD5E72002D1534E633A36C5373404661CE1ED379189635BAA44A6903468311609DAAA2C3100C9AB83A5DF55F44C5CF4B9A8AF87DF5F55B6021821CC7DD7F3428F2A129329BD0ECC69CC97E63F6574CC10236828AAC1401C6DD4EA3FCC32781B8C1EBB68186C1821D05A49207CC28AEC58EEFEF5F380604B932B0042DFDE26B2EC14A9CA51CDBA0F195BA80B0CD4476D16C91EF3E5652F35E90D36D37C317C786656F4C330A1B577E1E45A22B54A58C9FE7C735B568D09E0D8DA6B07449BF4737E5D55659D46EA89257480EE46071055147A6E70A5FAD6BCBB910A1B2BDAEB62D2A02AC69F6D446DED18562C49C572E2F597E230FDC714A824F4D2E1B3D8A27BA1C9B4B8E905ECF7AA6C37B9A3FAB18E73BDF3CD568D2C875914CC8B64EED925D1F926FA53E69FE96538BD294E6EBDF08036D08CFD7475C8C4079F89EF522F6896852760EA31C5444AF142CB55F08FF89DBBDD94576FABF95A112C192AE9DAA704131ADD665AAB5ECB27FCA96DCFD9F021C972C9F9337CB0638EBB958F906B469AB09B2FE1B0C21084D7C8D3200E8715A7BCA9963075589F43FB8D286A303593FB1DB8F5E580ACCA5FA23F51D3BFACF0B725C5C523676452A580B08E7B22B2C4299B93CCFF3E3B6C81CDFD94C8FD1FF489084B1DF45E2E8EEAB73420ED2E975D37074FF3B435F90243BA2452F54E0ADAA09EBC127FEACB798E4702F9417E26A51FF3E3D2D8C092461BCA08C0D7BDD6EDB0759E14D3B7501BDA28B4467C14CC4B7AFC0FCB3BBB503FB390C9CF06A5A1428E7E0E0DCAA1A12EBF1F8D280045D6E59A0E667900646BBE8368B575E50F3B6750B48C52FA665AE0750F2FA779D3A86806AFDF410DF1F71AD108742481173DEEC707FBA8AEDC94794F736AF77E3DC6F78C378C23FE026F4B5D3A2F8FDFAA54E007AEE540CA35A093D3DD437D3D5555019754CED5F7459DAC47C5DE4E6F675693F0A84CD33E5E4B5B0D84689BAFB7113B899FF7363D45885381249F1590AD173B79F2344F91DEA18E8B0B1A7F991ED1E7DB6EFC87A3D08625F8DDB2DFECD667280301BA809EEDAA86269A07C36A803133048CECF7D51A3D3372D23D3B65C72992CF8500CA52D573C866D2A2E103F9F7351917A7166836AD04CC0C5511B51159C87B0ABC61B54B4AABA2542E831F0940C8E3F24DBE90A49C8CE6B6169D81151C9555C1EA43C1DB2767D537BA05C394BAE3AAD626D2A6AF8A664E7E3CDDB320E35D59F117DAEDEA9F0AC5F46D1A0BEB0A2394C9BD657276876D9BD82E60E5ED5B4252C6F0CEB74FAC673B98482D1C263E7CDE21CA0E6336234AC0CF299952DBA20E2189404A67E9455FD8219B96C0473DC571803B88FB7DCC921057B7FDAE8489D72E097459EBB0FC1AA9C68B2FC934A178CCD81EDBB2D11796E32CFA6403EF25635064CA62BF0A9D8F5BEE591214B0BF0BBCBBECD3E8DA3CF96B34638940580803D9A0E0883F5D34B9AC06B5EFCB83CB47B53ABD0BF3FE0331ED09C0F3C3499611DD2596DFF8754679C7DFD2EC0B2A032C2727D3D76AE5286F7FD8FD85D7FB997EF3C019CEAFF36F891227F11B6E830AEB8F97EF5CC88AD2F6F346D267FE58A0B8FB406F151593C417849B613CDB1CB5523FFBDE78B494BE63D62E61740AF3247363CC3723D549E76E10A3F187EDA82455BADD5A22CB04853364455FFC01D4BB76224EE40071702F9A1EA97C3AC823A8352A6248C56946C147CDA99404E305188CDEB9AA95DD4FB703A2C3698D4C52DCF84A42021961992FCCE4C2502973D25E8837C8
+Output = A4E46D38AB1867BCDB21153C516FCBAA68875303ED438451E1F04597CE6E6B363B0CF32EB6EDFB3031E2611DF8B6549C04350795C0562F5ABA84B3081102F769D9AC2C01CF594AE408B5165197FC44AF951C0F82D5652E3A020D7CFDB970F4ACA2236FFF136CCE66401F063ADBDDD84839B56BC82FBF5CAFECA8FB055DDCC597F7E0E444AB3EDF5688827AA5E109386EDFBB4479803A30D2523F0BBDE033134B77127BC5427310018B59E0F93812BF0EEF4EC37A77F73D0AD3E4CDC4550215320C2C562E9DDFDFA3181029BFA46FD112AB4D1FA4CD0B9699B88F1BFBA2B00F0D330C6067B248028051EE7631609FCB2BC71573086F5DB99673C4318ADF8757941B0E7117C4BC2D047D5174E3A0E1E5CEC4EC2E29B9F617A2E75A91041543399477A07F24ADBA9F786E8040A30BFFBA0103C9331D790B1629886E94D535D3DFF043A89D5D9A5A6867920C31A94BBA0C3BCC473112087D31A54534CC772EC29106FAE39608F333238E74E18791227A475B7238B2F310AE5F1E196A3B14261C97F95355541AC56CE398F38535965C09708EEB7FCD23FE79B3E76FE4DE388B83AD13064F9AD4469AD85655AD3790B958F7B5D82269EBD2E06C343F7207F96CD16B7200150BBD327DD9B3405D58DD063193BA5B1648C8B4997BDD65271E782D2868109AFB01D30A4BA8720DA3E447B426B33C1CD94BDE859D18613D234C74A4790E23A174BC8CAEE8EADB19A9DAC27D340E62D8C2B3DF5D0D2147C4EA61078C9E4A2B3B1F9D0EEE979154E798547CDE291DB46D257228BE695D99C7F27356EF0C909573E989F555214C32C82C0251497B92935121CF1D921B0251025C56C7B1224AB18D19C6233D5B232D13763794B74F43801BF4BB85380E804AE931AD473B3D51DA4AF202C46EEA5E21259F31B732BAADC74C5E90F388DC121247EB447AC71504F483A7FBDEB5C3B6DDC6FEA607F3EB771026DDFB1756B1FB5F3DDF8AC834FD948C67F533244E8E6C9F574803E4029155B321ED4B54E1BFED00BD91FF596261F876756FBBC16EA125BE4D18ADF7B30D9DAD1A050D1B6804E6E5C88CCCAC2B3221BA2C0422B5575829A16A68D4192BE785C5AE609BF3AB68133A499BB0C3A112F391F47F3BA6828A37E6A5F776C5D45901660AE80FA23DA12C70E3BA88846864856F7C9E1EE0CBA22C1E48ACDAC8CC1E17CFE1EF2F50A7211F18E19866E6735BD17AF6696548C67CDAEC55A09951AB89426B54A2C4A9A60839D3C1B4B9FE26D9F53F4DDC832E1399BC04E4F8A1C0AC4083A0281514693F32266632010F74C4B302500E6B8C5F2F99664D1E4D1408ED726A88E301EB9D3B5E0493D9B6FC1884BC753B5D84B96B0E5F23E786DCA71D4EEA5A35F1748B4BAC9A43CE47A6FE073CEB1A57A5BF8D24009D11019F5EB04D3C79DD948D55F7F16886679F6CA592566EA18C51646F298722414C49164E27E85F94B1D4350799B6421BC179D7AC095652DBF6C75DBE4D2F4F831671E339941D1D21FE16BDA064C62859F5662139DF9C2F0B8A6F2562D1C7F9A509845722E916FE4D67F7CAB6E1459356499AE7FF61C9729409D4E1FD0731D69C2EEE5B7A4C5D85D2DA84984AFD6083C4794A837D0E1FD3283AEE7AB12558B186015C108F364A7EF09DD207818000267C0C975664F990CB51D48221AAEE94F5252FAD9E01652025F1678BE6044FAEBBA8E94DC43AF8902A5058CE29D6E17325B1F13A0FB3EEFE46067EEE5BF8E462CB9B6B466B14C541BD211E6260A31041C748EFDF1B35E75B0133E99283206344598ACEE2AD92519A90E99FD96DFEECB58BB40B2ADCA1B462E910D394181F9DF9B1D92686BA21E243DB7D8FE2A0713D6CCCBC0328F3F9DBCB3037DCF9E811F8C25B0AB9E73D4BCF1335187A86EE8911BB494B8F1B0228803EA37C8EFD3D947A08226F4A262984C20A9E58558E6AE03FA694622A634594DB35CC41974DF43C12D042DD752EAE99F31D34BB52AED1DA0069568E1C7A1EE725A7AED750DE79D94D7AB75499019D6C2DDA120A1C6F813AC008B16CAED0015AE609818FE7D8AD1EDD247DA3153EE3D9C8836C77EEC84B885F3856DFC304B57C67D930BCD6C77A29271F9035EA6212A9DCF5526DAF19C47533BF80BB6D15ECB582A78A67BA92F5B413E17AE69C2EFF3BC2EBF5256E92832130D3EC7F7AD76CC8C5DD59FC2EE9E0A7873806C152791741E6C6533F6368506FC4A655A92CF77FC44BD4CE3A4480D588DCAB0C8B398AE8CA6A7DA65792C3946102C45DF4C72EC9FAFF22430F131EDE726DC2987B4DABB72AFE5376A9798CFF3E8758B3F7BB56692BCC4822CA337395B6C2540FE966A70AC469FF6DDF0909AB83434C34A854381F5BADF46E4DB78A317E0BCA59A3A8C5972F8D059D7B3A51470D560D3B47EBC7FA13A253662305B78F7A146FE86B4381B4EE360ADB027C9646BBCEDEB03C959E4F3111B2F8434949CBF04CD487BBD100708F47C12715E4BC9E9D87DA5717246E6F3E4354636CE312560673F4037E4651A2098553C64C11BCFC9B881C7858583066D80353ABA92869BB20B75106C233619B95D7EC58630CC2D4158650582C345D3C3110361DDE04B69F38175D136F1E28020D36D2D90F2853748A232BAEEDEC38A739781BFB7F52154BA20DF52BEBA4CD65ABC7DC57EC484565186DAA795B42C8988293B2D4EF0524CBDE85143ADA3D1B28374D2B1A335C6A914705CBC2E2DBA7D363ED78590369FF256B3BE230412E821407865A07D1DA117BAE75B0226D173C14B15B3E85AE50A569ABD465D4E4500FDF535BBC2C1C5AE9C1CA73753CF488A9A4BB406549BD3BE25AFC62F7B70DFC2E940B8C37CF33D2F80AB3D1070B03617043BABF7D687CB04B33AAEE8005F58D06C4464440875A79AF4F5079FFA1CCA1B109419223FF9E56674D834FBFDE69F406873C3E54218EFD8F70D3DB7084AE9734E31906AD94491CE12B828CC785080DCC911ACF80C522817CCAEF838BC50ECBB499F12ABF32647C425C7A432C0321F13EF4E18B628DC68D7BD5D049592A73A96C2CC8F2D4924A1D14ABB2B15DB7BD5BD4A46B09FAFB0F80578C2B8D0F4767EE26AB05D22A3BF2F02FCDA404646495F27B2A4E6BFD33E732013DE703CAE16B45F9BC39BA8FA2EE8FF427DA4FAC6C822B905465014647873E1EE8832A87870107B9856E950EEDC6FB097F5383DDF6E4AE01C98D5AC6054AB35655318517E669E379140246AE63D52066037FF37B6814A911A15C0982140DC132143F8707C04C6F81742872FA83E6CFA8112C644BC1A86162527F924D9DF22341232E3540484956677D99D6DFFB055A7A7F8C9EAFB9BBC4C8D2D7DADEDFFA1B2B476578798091B8B9E2F0F1FBFE090B2E47818CAECED6E0E9FC00000000000000000000000000000000000000000000000D1E2D39
+CtrlMu = hexcontext-string:FDE19259E56C2602F3CB0DA509B912F88262A1701D4E02B513F45C97EBB100A17B208205098D6BED2638BEBDCDC52C4C5114E8CC8FD9A180E79CE750594C3BB188DB6A3605630C6A2F3049BECEE951FB45B5E426
+Ctrl = mu:1
+Ctrl = deterministic:1
+
+PrivateKeyRaw = ML_DSA_44_16:ML-DSA-44:7CC4159615967F7E957D79F40856B4A3D33677AFC73474C8C57769A07C73DB888352E734EC7E57489E19838B8EE3FDFA8C65F9668D774E78248A498D66D9AE97098F76F4B0BB0FC63148C768C777083AC6D7BCD1FC402DA530C10B73E0845F29210C714E0A1012FDCAC40FB85F183A0EFB436C8D3D587F6CDA5A703C5931B7328812610B3640D92066883671D1108D084760DAB6611A1385A2C00DD3407014402D62300800468561240E482884A340900CA62400461250088D1C836C01382A4240210BB324083532C9968CD2426219181208300D091181D212284B806D0849811C819194262912030DC2360252148ED212081BB43122270DC3C280023429A2326C129881C98661C84281031101918029A1380E1219521826615B2245122110180120C034321B448C61440201314EA4C0715BC04823169121A8295980085A1645D1348964084953A00402C62C1BB20418112E13C75093B40C4A42488C104514312210A308C8022093180C80248D18296E11C508018050D02846104572620286CCB20DC8188C64A8444B1231A1869119A00904190A58344D1145660188211A221153284E941050DAC88592A8311C015022094C21324949A63103918C50144108C0608C002E132642119041A34828CCB885141370A48205833009132625A4388418A06424C661D826025B280420B24D11B630E2C20C8122821B1784102502D19025A4342804824DA4265163048D12C0011B939140C04012246E50362150307080A85151046460206260469144084A9BA28149C220248131D2C68908C57120832C93186059820149C66014C82442142614A64C9C362654C2000A04481B3590C1360503066120096C81B4898B4412E3324C0810284C0030D4342584A084E4466823030822156001832091C20DD8266AC9144413B40193B061802410889611A2346EA2428021040D42C20012C70542066D8AA42D921840C90871DB168A10142DC8240284164E82442E0B954D0C202CCA484CA40885E2B63064029282C4310BB7292231484B3052A0306EA0148A1C0966C414260306100B2769D2448DDA20044C489202306CD8868918B56109951104B290E23411249860A4A689A1306EE1203162920D13354E89246623474880200524351142B22C1BC69013A0690A31228C36885CC6016414520C1832122288891204C1202CC3982D14B46410334522B36000B34991244C831804C43624DC020A54C0651B304199B23080064982B66C880610C8448CABAA867E47F51A02B425E91D0E36DEFB54FEDD2D93CAAF5B841866D3995649125D1D05D486DA16B301128572FCDFC0CD434AC188A2688E0E919FFC40DCE0BB771CEAB6DD18608BA18381CFE0410065BCADF12284DAA43776FBF3C20CED2EB71A927CCB86B092C28C269B1B7310BD406066B15F2E9741DCDCCF203C36DF55DE54C3ED39C998BA7ED4D265356F50B213747FF9ED2FB1646E71A1B136E8313F1388FA9262B27F4F569F0FB286B4EA4BABBE627498D7AFD5963CB4BA3AB0F294CB650BB5363A0B9F8F43E04042976AC208DD600E5F81F51AC6BC1AACA7384D5F1C50F83DF296D506D06817A306AE3BA17B3C0695C961BECA64B0554FEC4194999633C24D5342B861C58357A4DC9C1F0AF260086DAD968FF228CB8F0E93E2027FDF4C308D62559994F7033167A5FA71F1AB6D0EE72EBF9DC200AB3A53E94C6EE5A8D7AF8FA68E41F671F10BB4EC96BF6CA13D77D254DE3962D526793DBA996D63170DAD941D3D3733CF91064576F6705EF7936489A7FB06A226BD5A86123BAF93DB5DEEAD00F5FC77809FA5745C3F2C5277E8F9A15B866CDD9AF4B3DB32A867CD6DCD1E18B4EE2650E608B77C4685E236BCB168B2BCF56EC5A37BBE96201C83736E98CC40BD27F9CA8E66BF8E38BAD6832D019BD3E462DCDF55EE443AD4F1A375D034C6B9A3847A178274A738BB8DAD71AC99D4648AF7C24040B4801DE30B43161AC1991F1754745397552814FDA797AA85CD065BBB8F0202FE729F0087414E16407D9DF1F50AFD4E9631017FD825D7F5261429C7504F46E9F9018AC347C86ADC980BDD43AEF8AFDF05B7E2901F8BA773810DF3603C17B6C7920676658F11709982B17CF287E1DF15F2731D3CB619D00D6B26F256BF69A833ABCE080B4CB89F9F809E91B60C11F2F8AF0E6B553CE92972B81D87C75547A23CCBC21F80E523523B77EE86EF6D7676C169C6867FC042E26F091BCB3AE70E5CBC520ABB9112BCBAB80AF4BAF73909E20B49A9518CDA21655F1BCEFEC0B5D8FE06F470CFF9171F84351272CAC944B0164CC0687361B87BAAA61AA64B850B6D3C27FC5A703A111DC5776567DBEA138203DAABD4E0226216F0DBD575779991F2F9404D6FD3BFD19EEBA0CFDAED98A8393AEC6E0D1BF216DF5E1B447B2A68B3B4763048D0F7BAB7162340E25615C6B74BA320080F62D957C1185C744FB940EEF9A0A70A69A1BE206AB753A5F3F0379CA0E54E231E1B95840C692A0754EC7456050EF6D3A6D763908F0191280DEDC5742E7519BC6922123F78E398EBE4BD8A3FE14805910D89ED8458489C79A8AC99DEC8B05B8B0CCE81634790B793CAFFAC74FE8655824E92D5AAF0F4746B3077B6C0AC5A7FB49BBA294A2AC48BCDACF28A18EF1DD53D62A82DB788FCCB8C2AA045DE4EB1375E9026C3FDD1238BBCB8C553C8816176629073C10C9B9E330F8B767057B765891521B5D24933CC6FC3DA89AF19A6E33BA9D3665C36EE5B0BB4982A13467A5F37125D5E9EF987A8DBAA332EC9337920F72F1E633AF03494B059726FF585D81E6F268E8A4990646493A660FDD7D7B1834E4FC9FBAF745920192065389EB95F21E6FB887E78E37D4145A0DC7C10C11B5655FDD1FA52D0FA29340FE806E86D295B251C7D88560098FF6370AB72A2A68C5A0849D9A7A278069531EFD58A4E73B79E1F28B6FD88333F10D92C2D537628CC599CA2A58936FA7398026BD059E4B7F92396E3A964AE7547983E9ABBE0D3A518EA11BE7ACEEE87757A9CFBE315FF93C45B49FED0CD723FBA2F1AC638A42EE2873F4E9D7F68945513A126861F1F551E93A3ED9EC402063EB9464A4ED1776FF44C122295C3B310670190144B9A49DF42B2D21E237EB814F705681F0DF39BF4723CCE228BD07141C324339CFED901715D67DAA6253C87E78BC681E7D3D95A993493B4EC267D978915F1003FFF7A697D550042ED9661F270C5458B86D930D0912FC95FAA6B85C7D8DEC1A1FE0B60FA3CAC1331F2F1E17AA02FF8D807415BD7E2F6DA1FD71EBEAAD8F574722040E8F11C86D586DA70DDF630833D06F8C0B84B789967A971C1595AF7C8CE78598AEECDF981352C17957EE13683BD75FCCC3A1F823C89473D8478E62EBF780D2B7B8775951762184BC4119E2A35E7BFC6A372B1B152893689D56B576DF8FBABA7E47A756D154F4A7F31A18BD9001F66E49BBEF13B137D5BF2C612677CD8762808338106F085325040F45ED07C2153EAB1E7CA955A9D1076A1DD5F7C248386933E53375EDEC86B1240E0BED8C8C19D13CE2EAE382D235438D294944F81088A6CB9A8D3D4D84776B59ADC42DE04114B8A2BEE2D8D0F9EB49790C938B0A4D8930213FA80
+
+# HASH-ML-DSA test using external mu
+FIPSversion = >=4.0.0
+Sign-Message = ML-DSA-44:ML_DSA_44_16
+Input = 2752D00550D79203509F2371E98FE111BFEC81B2E48534F7F73F0725371B94ACD190421BEB5B083C4D6F819AE834C25BD11BF9B00641596A6DBC605913E073D23A0BA6ACEBB32DB99D88F43EEFA1B457981A05AAEDCEB8773C2DCE1914A6C441AD703C9D8C5A76D7C5E77EBFB896983BD575FFD721204276A83206618AF3A01C47ADD487DD89CC97F227CB304561A9438F580CB5C20BEE81F8192659F269FE9118C32062D92794E12AC1E310A23B90CF8BF9CB8984B9DF670FADAF726BF759D6386D6BD530353C5AE2906FA273FE2FA78C92504B3F37FE84995198168277729963570FD2E21C61DC450AC0833D79B9A7C432E9DE91BC2D47404EF192118265A214AC1B2CA6F3DA7570E2086D7E3FD28AE4A998E81A87467E84B5C6BC92D2BE4D773FC210049D515C1A0365D73BB1FD060B8465CFCFDDE2D6BD1FCAF850F479D5B9431F7C45DD0F497968C5FC5D039E4EAE16B10CA7779196BB08EE13E7CBC5E000C3DBD900AB1DBC900D4760739DDFAB32B1C316D57998941C1F27629A9F56C1C9399A3DAED000712D8685400CAEB6E36A88B23E8C8E199A0089AA011D4A43164D540E2A804A34E5EF50A4EF13981EC72144ADDEB5A415D8631B87E2B6F0C2D27599D92B75D727186768E2A2677DB5876C60F1DF1CB9B0277F8402A8629B4ED2F30C6CC9208B281324C99765CC955C65C3E97E05C3B5DEE263F9B69DEA4BE1FECEAE38C3E37970EB66B595FA9C13213621ECF4439A1D812BD0504290768EC8B9C43479EDCB7E52B82C182244E1AE33A58A2C2BB305A533489C034236319D0388F976A8D0B9AB624DA0F9E9CD651D2F1287742E7A17C22F6C0432992ECBF3B215E6886210C0BB1AB8A642753412377A35320E29E4B3EA115C86698BD3640990152EFB05E0536120B158E164281EB6D68F05FE385B08E111EB68B572E16C739A0B6B94EBE9C780336B6A32C5C4D67911F94BF746FE15FBA91AE4CC6086CD8AE31EA63AA230AF21864CFC4D57B2FBF6B8C83C3F6C4F9140450DC49B0B4DCC30B8C1E4C0BB8E424D38B746D83E5A096EB5EF1A2DB6864BC7DE61C9E57EE8D9CD253BA86020B0E6D2F6F2C5B81770A10EF34283E41BF26B61210570A1613940FE6F5D48E069FCB038D4351F22E76DCD4F022A1EBFE7482B6E1C125DD1E9BA081515AD25E4BFA33911016A2D466088747ED65A5B6A4E56AB95420884A30EB2A3017C7FAA5BECA3A95D956D1593DE6D76A0B56B838DBB8595C00912876901CE7E17FE0BB4D6282AE959E716EFE417B5A7C97285728CCAA20D3056D385D6FD917484512D488637E82527F74D2FC8B5EC938FE984FA7553D0454691EBBF4D52E1A716DFA4D0AB44914E6CFBC55D0FE89E1B1620D7B8A4A8542922D88C17865520ED555D1767D102B466A129A2C97CF21DD2A971156E3F37042E4776D35FAB32770FC5414F2CBB488D2B8F17EFA9D69B9FF538374AFD65E62EA489D3B05E2E447D91D279651CE7550AF36F7A546EDB8E02F7D401A329C44A892A5D3782C53CFCA6E485CB37D89274F7DC600E48EF108CDC0C99EC5BD428035C0A52F790DF326A02B76A65FAB79A9BE330D6CC2C2054DC22EE16B4303EB4104A7F83D27475A5CDA88C2F9CCB327BF2C1C92A10E57282DFDCC66D36ADDBF73154359876AA5C650E41BC3FAF0031A38F24C38F28700846E2F62E2ED983E68AE83B2FCE6EC2A0B651F29A192C55790D0B9FF11C37C3336122A45A1761AD57A85AE994280E3D428041FB535BD12BEB3C0D8FDC302C40737C643E0BB142E1B2A44D6C441891869DE8D01D5DAB3F97A02D569DF81451A5149E0126AFAC25139E9EA692F3715841DA06A32ED541DF75A213EA30C5E180593740DE9D596C8D4116A65F3C20E2AA8A5D1B7E796D89E5F33C5B923CED3BB9FDDBC7759EFCB65D587F11B7BB341BD0354FD86B7EDE5B20ACDEDA53B7C40C52CD619358F117FA62C99A5DD58E35C4CD8F1257493F091B9133B88D985F7FF7B5549FEC8002ABE38C9917749A6F8FD9397739D2AE7A61F1BAE29503D4A4947A3E34CD64676F3C0762C1D1AB31C3FD27378D312DD9BD2C82B8075BBD090DA45D4449389896C6666BA41390DAFC1AF9169F9F117802D8C476CC632ACEDAE11AF29BCEC52BD99DC6480633059E33F8E6E3D6369E2300402C0A7512355D0E6D9FAAB2C4D3E3FDCF4C3EC212B92EE2398801C1383C798BCCB76FB1C3FA26DB895ABF62E7D8BCFAA71B12AF2AF0B7AFDDCD6F3810E6692C01928A80CD8B19E1FC68B3CA680CB0A9C112A7A2687A1E57373D3CDDDC22433C24276E98827D0C4EBE7A2A4848EFEC1AAA4C5A53228E546F2A0CFEDAD9D75C26B1FCB954265AF51B66CA30BDEA4864351716B43849F438DE7C94F278997218EE371591AC2504305F84A4485B6CD1041D84974DF2C5943C42A8DE5857BA7AC0AF259C8EEF65E8CD65087ACC8537E1BCD5ECADF259213E8AC604F794F26939DA5129803D29A6F0CA77F06F31505A40393DD1C19195CCE92F63C535E03060BED439CB27C6711E8E977D43BE4351A14FFBE427F5B03D73A579F0F25BB73E6D0D3634C0D2A05625613281FF043B45D921BE7C744E3E96F2950C6B7EECD3500F96817E041C7360CC7032668BA3D28011F7DCC5ACC6D5EF8C3A5034326CC658C8BE25D1854C3CB5E66B43493DCAD143C93FD745449B0F346807CA41D25ACB0FB970315DE69BC0E93C2060BBFB3EF7577C238EFE7C80C9F91B0B6D453579D647273F8735224D66573E53C50D942B40D64C00BAA9A1F24F41A553AB4DF3FE3C012EF5E73C14435736CC30F0CBC16F4DC7586043727B053D0E7B9B787A8433872E1F867DE2F29837907336AEDE00CADD1ECD30807B2C5C88ABD7DAD54BD3CB6C7A0FF1959E8D1BF7D9DC70CE03B014E850E74C45F1AE9620B5240AFDEA71130042395FF3E91BC46B13212E114D11ADB6D4C4475ED40DA6DA02DAB162F92E1907EEBDDFB167E9AC97462152988899FA6687042B037901BE22EAC6DC95B30B8C65BAB7AB487735BA00C7C060922F91A1DF70646D8594D3BF990557E622E97458A6B3812929786BCBF58ABFD6389AC379D0E8C6090CB240E7E4E024B3422A44C09EF9FB56A86289A67A7120C67CB79FBDB99A1166AA1252A2092A8CB31FE53C15503B8ED8587C13E24F711C5E544A12709443987F4094FC2CD253D3BC44D91796D074108E283400511951FBA63F25F6DCE7D08D367B0C8A60CAB60D59CE2D334B175B834A27FA41217A148CCBF5C6E3DFADA1EA78252FF154BF378869557B66A61DF16C2304A37ED249B2666D604998532063D435D3513D847E88EEA6CC8AD4E082ED158E19044281D7BEC962B370DCADDD55F42A5BE893327F86274725559012151ADD3DF776A8148D301E6783BD0BA6CF13FB5D9DE9FA5F2A0C04C219CFB1F5254D1FBF16ACC0D9B291135648BD3E84494116D64BB66034D9F4E9B74F5BA6A0866624C14154E7E6E94713FD1ED35314C3541DC070385C4A0E6014FE93AA21C68F312A9CD202D86701870EA8016E95EDF27C62712136D2B003F7C319BCE96DF8EF146CB4C1469125C53FF642E85F41D27AD0E700A310DAFCE6EBE263289EA6F8CAF707424110BDD97D0582FC32B89BE65BEBB862E137F0F757AEC47F1F0F2B473B29FB83469CBE3233C838930832AAAA6A027353E5E222C57B4EDF0043F1A2B6998A2EDD6306E8CE3F9D4403FD6B4537AF0770C1C6C4D26A065E8A2E6BFED078CC36FF4D6959B2DAC33918B84C2823057C4BFF13696EED98D8983715E1AB085855912C2170C182AE1BDA30B35DAA023D736BDA73562609F957700FEC07CDB5F4EC76624F66977056BE553A73684408E8853EE396A0F4CEB8E093B9E1184103554D98F76692887C57017A6824DEEA8C1051282E0B23028CE4DCF5D7E3EF71D0277A195DF5555A10B34A07982DD9AAADB877A852E9687E6FAB8D4DC0A33BC75E99A741CF8CA204B9A411978A92AD1A329C9813AB4D0B78694AA500F90EFE224AD49946470B4645CB22F55E61D2C815AB875CBEC5869CF8E0EF4D16D26D26AE02736952A759A18E5A5D994745EB67B660786E20BD0095B427419FE999A1A365693B49C988288D19A719BF5A3BC33A7FD8E7B775D390E94C3CF27B357249A71AB8D50B7F6B409E18ED41B1A74DD2A6A277123341BB5B3A74BAB50927407B5A59C00C2CE266B1920C2873D10D05A4014619BCB0CDA26827E5B77130FD4CC78BBF4EB74B4F18C05F27DFD6A9E240F1E6EBB356EECC3AA9100C5C45A2C5D872E2D25612FEEC8FB59D0788EA76B374832B593709702E99E2D14DA3DA04B5C8E81256891D666C4B1EF4238587AE0CF6B4E660FB6BAEEAC86BF0BC7E4A0B83D9228F37F76D675BE327C5E5F4399A899E5097AE5EDDD8581F09C3F17949931004E85D1F114D5C8E63453ED4C3CEF48C5C6906115B0F6B3641A18554FBCBA021FB88B3E8C5F7F3A561F2BECA131AF9A55DBFBC196B8E98B8D0231605F3BE81210F52EEBC73524B1983719FF3C73C5958A678127CECB073E944273B1110771701E685076D72A01D703BA8CADF77DDDF0C34277C0AC067A716BF605C91C387C14D5117D1A7498D7F2382D3E494C7334AA1B8FE99D2C46819B82C50FBF2C3A044203D9112C17F980BCDF94173D920D467BA53203207A7FFDF64D9513F545B7964F6AD04682EED9DC2EB0640D1A4CDF2096CA4566B2797F125A5C0FF5F4183110ECAE640CC2A400C10BF48C9F9CDA4A0643893C2666A434A36792FA93BFD5C0AB4FF0E7645312B9C2F0AC1AF72CD6B544FFBFDBC4B37111C402B71F7C67017D16EBA966421F49C61F4DFFA77878476A871F61952CFCBC44B642F6AE293207B2A824834E875DD4A694DCD871083754CAA0D0418429A9919A2A30C416C5DEA2F7CEF7EEDD32F84AC8938B4EBE96E6757A69CC7CCC29D6BA86F1B636D2266831D39B7F678DB1BE872DAC6B5AE42093506102FA8EF7B24E672675909BCA15E733EE7F6F51F7478874E8B42F0A80FD30179D804CB96A56A142ED66AF0626A74B5CB6FC58144820D0ADCC54E5A8F52BCDD58BB73305629E203683A772D7F544C9CE5D24365ED2CBDEC085BF579A9D34FA603E945AE075A870ED4B2AD016F1666D13EF72DDEF9C0C79E6A7A8ADD38B7B6163CE643E5268A9413B13C25B4CA276793392E3060EA8B8C47CF6B389C6E384BF9667728D6AAA0006E57176A86079609A317F81684B01D8E64BDEE8526F8EC2B1BC22C4AD3CA7E8C4FB2E17D78F04A355592318899540E26B14BF25537EFB24FD5905BD03CEB9BD4A0926A92D7CEDA57122575D948C1BC769369366F9F1E1FBA45B84B53485C5ACB6B7A4A56CAA18FE8BB927356AAF80820E3A32D97B7074FEAE8353467B695E9867B20D150E2A019C001EE77EE0908EF791F02E831E3D6EB3DCB2DF6E46291D632807D5F99712723CCD5106EA2441A18DF1996F88DC86A04DBB94378056CEFD16F90BC0921E7090F178FB19CE7B1A580433363D3330067EF899F47A2813FB37E71F81F47C55D4B42EDC9F8F4B7F4A3225CC5B7B8EA60CA8FD881666D2AF6D9108469918BD30145FAEB4E34FD8F4071F00C71995B4F37E70383F3490B57379122C7883429E5AF94F52006F0D2634D83218CBEA4BC18A198B992763B67F66601DC20984B1361FDAFB02EB2FCE8F17979BE5B3F715813B2BA129B96269460C2A71DEFE24CDD0CDE61FE17B76ED3B43450B90A9B2A9D83F0BE6D7558F1C3FCC6689C85B11C4233835A85DDF549D11F306B8D5A235AC85F2DCE0B247F6167643EF360898E041FC1AADC656489CA52FCE2EDD7BF5601EF26F12B8AD0DEEC0FE8BE0FA84745B40C082AE45B1281B98AB79EEA118F4A2573E63BA53E9B0C49A606EFE61E1382F1E6F47C21E5CAE82BCC2FC6971984F3FECFDC222A392BA9892910AF433E4DE9504EB86F048E1902961174D1E7243559554A2D340763D0AA56F90A3AE761F9A09D17EBCC73DA3D2C03E486FBE382A29463F7103DDEEB013B1B2C4942BA1CE61C0C58538AB9995165870B1F099FECD6A03BC557CE7D05F8F1A1A466C53FC53723ED25E7B922DE5ABAC68D42D6DDA2E082F18907F23FD442F3127668135418F3CAE1FDAB7F6EC82943B896A2C0F90A6809B8BBDD328F192099A555824C211251694D3BE046AC983DCFBC970C606DE84994C38925A6814105C2435818AC97E6DAC4F710A8429E2B93DD615F7495DC64F1BE021A32FBD6497AF67E7F713F769F2E302CE8C45FA64BAEF584FCEFFB378441B5988E4BC61606ACBB704E8E4D2118D9515BAB191E19894C4DE049028204EA440CCD935750F08ADC4506DDE9F3998F3F877EF4E67C900F48EFDE3C50F34CB299594127DD609F58454128AA2D61792B8A313C4E359E438633AAF4F6A936CB04F0333F621E23C17CD5B4922903C4B93CE687721CF73CE6D9BC604A9FDC750DD76A2B6EE69E60B3FC4D338B1B77FFE77E49D16BB60E362F5F1ADEC2D2200FAF61D00928EB43030D523E8052269B00B77E336BAE8CD6DDE19316B2EAACB2F17DA05A271C2E7E1BE9F85D4293EFCA4611048A75C49EAF0A88EC38A7062DFB6B94D1701C6DAD0541F3F8095E975EC9193B1DDFE51746C3EDBDC8D422707213F256F5A15D080563F0EAA2BB888C6DD25C0EA8191A1F5161352E7822D09DEFE3AF8095CB6ABBD8C15437FF37787BF266DF98711254219388215F5C35CA599AEF211B377AF24C6BCE20FC3AADA773C19264427B6B8D11FEE7FA8717D58AF9BE8FE5A7AB390E56A95A03FF2353088E0F3935E72F88E87F96CC5545D19CA76FCE444F433CB7ADF67993CB64EB24DE542CCB23BF45583D0ED2215E583087EA74BFEF2302A7355933F87A406C96BBA1F52485CDBB10665EF146F77895EBA571B900611F8C1903D9FA873425A34984054BB64AFDA241EBD6DE7B3D058130E1681E1E89F09B06584022280FA7E745DA2E4B78A97653C49043F1FD25E45926020A2E1B6A7D78CF7DF42D9EC13385DCD40460972B833A0F83899C02C019FB86DEE45B9483BB493AC577FCE386D74D2C45B217206A380B5E9EB2D6B4B73D477AD8B4760F5FCB8FEB25CACF36E2D9C25A3D54400893D33A55F4BB6013A66F0595AEDEBD48328B8086BAABF4A58DFF1AFFC57D24BDD3F55895FCA89AE0514996196F5D36FDD887D8621113FDEC6837FE8E9CB15D3D8975F2E4DB95D00AED9299B7975CDC8B8DB7B022BAAD32C5DA342BCDB38772EA012E764CE9C3B489AA921043D9D7219304B1160F9C5E6C00C1DCA8901E7B9DFDD5728FB9F0E13DA3C64C8127ADF3A282C549485085485D91E8672D26677AD52CB9BE367B5AE6DD4D029B57F9F05467EE03100BF4A757BC0CEDE9A0CE2E3F3F1E46717B15E1A592D03EEFA841544B7B88355B66A7991C9D35D31820ECB71C3CAADDAC446BDD9C4A69B7A410448D8F240ED657EA6AEA1DCA8ADD58B12B0F49744F3435D1795951E01FC651C464A67A8E854430362C6B71DFAF259EBDBDBC10BA7B08B7BF1B15693E0D8355DCCACCF0299A156D3CC9C57D07F450E6E2985E6375A21B9054D014A4F381FAFBA97265309607371B2031B3D60B5C87D4BF08EA39082DC841FA62F760B6369FF0A0D0DA8EC810EFCCD267903DE416F34B30CBDA92719ACC84012E4085D40983B16F97ECFEA8B89F9A9A5BD3D5B1CA6F3BE7CC6A39E70E878CCF834768AA8A28A53C41085351DA189A04C2A1F8269C074896EB0FFDAEC0F277EB3ED9C153A45C3C54F971B8120627F7C918FADA8672CAA9449A0D07C67C9FAEFA1B5A79D2E1B3FE9C276BF1464BE3A3F2D57A4E6FEC9245B7ABA207A3F963C0EBB704F6AA19CC436769C618B88255E7A1B000293D8A66FE1104939E251A68634024739B328454F3FFB7D8D5EAA34A4A4AC7210B5CA3E45E53367AFE0DCBFA3F3EB1BC02CCA0AFC3BC5CCC21E9BA348B03A06A1F5AA714FC27403CE147104FCCD8C2901F2F4DEAB5E77CB270A00B9E8F18C20F5C97D302CFD0EC50B755963F6ECD6D2D884A4D4E13FD02F35C76E5E877F79178877F1D39064ADA6C7E60E7B7DBD8285588839521904A8E79D0DA4CA93D6AB19E0D72189723F5B11E6BDE98A176D3CDA91F225329959EEA9F16402EB768464A17025E8AA9824878DBAC2BC07BEAA5BAFF91B5EF8781B4FD971B9ADF9BD15D492B59834731D25F5E0FAAD4DD9236063AF6DB0015B272A49A498F5D08E05822B08ABEF682249C9500A768622C1BFE8CF5237B37BD82E80822DCA7E3C6D306C772D6F0DB1F2B2AB52E9983FDBB9480422D56310EA7CAF8A166DE22ACDACA2E83A423F7AD5E2BE226322302B6234EE6448882ADF556ABEA69834E0602525CD9304FA64CA2D909C4DA4B87BA92337CF06CE07409B3FB7D2AF1C6D0B622062DAEECB6353214BBC7B99235A872819CB13A39710C3000498F6681BA086A939BF983BE0856436DB561791175534261FFE5F93064521C89EBB743FAA3E87535D82EE5780A726696E19E8C8D961CB78D89096A303D42C181728D306FED250C42EE102D2CE0CC6A6915367326D38E171C26311FAF2A6345065BC64474DC7BCD54C45F1BAE38DEBEA8B9949382491D47B6020CEABA79D0A10ABE660A9F7423FEA9B9BC431B451A2911EB9980CC6D036C6665B998DA248611077B61C862EFAA4139610186B683AD6F9BE70F418BD81D6B86C8FA49B65D5FAA3F72C6C9B5B511212C2458E9D98A3560AF6FF63ACFC684CD44167E7310D765924BDA11EBE35DDF9122DF070FA66181043C0B201CEFAA85515EA926944733003433FE4B5A8F716BD08BE6E55517E3776AABC5A7110B99919635A05D4E2D6B7707341EF21DCC2DCAE31C2CB58141BAF1BCF85300BF3A3553251B1888595588FC41672610099F6FBEAB84E1475EBFADAFBDFAB3D62AADDE2D4428918C8BD84EC5648B742EF078EC6FCE7DAD9E687A7BB7A8A460A81AEC87B6951788F60F3D3C59224AC2DA7655531EA3CDC2B1637445CD1EA1CAC832710F2665EB834496903C912F1012AE479A58726C931A2912EDA1185C1F864CCF6C6A35F80974CFC7BFB1BEF7D59E058DA1ECA4ED99349CA70730D9BA4B04484E3367CE159F4F333E450BA02FC13142C65C646067D480363A7FD4483DB315D360E07F28186561F10253796DCF6E1C7C506D8DCF1E6529CE5C7F3477915287B8B3DB0F4B3D81B0542B83C590A2DCB56EF7669AC46BC8AE702CBDAAC8BA8D74C7E41C4A85DD13E2EA32F998CDD5558B0748EE04051A6BD4FD60ED5E1F6CE34DC4519BE62EE3FD5CFB6BB43040C60F5D8BDD17AC0751C563CA4A2D833A0E4D9F63395EDD32E1681EF4F555CB7D361F2BE3276D6D0B4687222ECEC79601F2525137316660F1F385F94A22F28E73F43192E12F758C6085D1CA9A4FD0B8DAB340E784C9C0AEC8015A59B3CDF3DD102171FF86A24A42D9D61AF7E02F614B4C38C73DFF03B0E5F3B7342B724F627A7DB1F9FC9B1DF1A1F140EB35AFB365FD2DCCA81648708E0A1D0D1C77433B83C40BC9AB452D8E18F54F0FAE8E0DCEF3102E7867F5146E4429F6268AB31240B19FCFF903A2C4937694C2BD858D998431A8C12FB7F5DB52BE0F6C70D9A323DAB2D59D48123F77B43FE66E4248D27DB199CFCAAD8EC5154FE8E1387B56037AE4D60A8F033E3354FB564FF4CD44C334F68AC2735388F9E542D8DC7B673ACE0333B3B2067DC808730C2352A77408EE2B644972495A372D97DC87580BB23F774DFEC4FCBB70277D69D31CAA811FD66D9BE38EBBDF88CCC8306F232EFA31DB90B7A4717EE2A3D1BA1F28FB1522679DBA0B828AE742F71C
+Output = 29B7DD5690B6791F21B42116670630DF0861DB7793B2D0EDF877B4870A2B19746FEEE2D761840E32E48E898FA15E1AF1AD6198248DE5428F7AE1711695DD9C094C17CB0C5D7F2DAEAF898A69593BADAD5979CFC8CA48A2D6E9244FF549DE089CBCCD8639C5E436643E316DD9DEFB9963407CF2E8D219055C78699987E5676DE4CF784668AC966AD00C953E83F570C5A84090A1BCAEFC38FA322FD968E41BE59629F34A14010D808C3060FA35B745049A60D675D180D50E95AE96BD82695A25167297A1D114F48BF10BF5A636D392F82E8AB3F630DEB722BD8090C31B8CD92015F10781900B83115EB03F64254093D10BD12EB4C6F5A679B08321C9BD666D171634B49BB511EBEE2AFBFBC4EC53C995F263CCFF0740944D692C6DF67542A2E95CCF865E33EFC697C0DD2B82F3C37C449EB63F6769D7C5A7AA7EC7F072C5620A4E5064F4B34ACC1F0219C325388800482F406BC628B049374D21F35706FA9568A50B6DDBC0212916F10DFF2ABF5B2C0D7ACA5ECFE7706FC0E11B482EEF750B857C0F602DDF21C5642CCAF4D0E780958D0885C1936DA7FB744A8F948C8AA3FB2E851F323B9AA0BA6CCDAC9F00E2B9A1360D0D0187CCDB21C11727A35ECC98E76446BBCAAFA0667217891352FB20AE87059F7838D667290291A5B713ADA1092FE6B54705E4D389777247F58AF2D448D0AE5A26A05AF7DA763B14270024BB547183607C0A1C76C058AB3808EC562FFF40BC3EC79824FF7A0B0D8F816B24ED40CB30675726C5837EE9159465BF92D0D01A28E675EB336B8AA653CB1B525145530FEDD6BD4155023DBD998155ABF9D49043582C90605BB0DF943A4B266C161EB5A0C6A45353D89EF80F5F74B7D0706E9F7D409E8163E44268F2A232E615F39D21D991BAC91C4B9513A8AE0A07D89F9DF4015019384DBE2B58BD32BB56C4752319BAA6A182C941E29EB61A2C2A43BACFEF81B30AD6C4E64DBD63FA849E9D13032B16899D263B695807E04874018DB280C4938EF3655FE4D0C73374CB7122F35A4499EA0C41492E87C5120FBC0F352C22D505FFA94BCFC9665D1A25AC9E28FEFA2CB1CB2580F4C7781FB150648CF7EDF96626A9AD32DAAF869DF0038C8F8BF89342327E472D1341BB92A2F60DA99A761C5C582EC041AAB0C30D5BD4BDF826C0F53D68C799A0F68D319459E9FAA2ECB825E424B62B2D6EAD49897F63E2B0B58AD8603797458E0A867AE5590C79729FFFD86C1062BEC01BF74897B7615BF95352ACE11CA91D79C9BE7E5E10C05A1F66CB5FDBFBB492C25A6B313821F8EB2E5483612EAE93BE3CAEEF340A5E715E8C85691E7E65F7BE30FB47967406121346A864E94878826ABD06C1B38D8FDA75706233B286590D8678C23AB85C4B4C0B07949E2FD93F6B93C12C3F172E25DD178DB06BBE470D166AB8D4AF266C604122BDA72767D9CA3AB944A89580AE6A25C4F8AC2FE67B6613F5E03C1B92DAEA2A84A0DBC76B90045F3E89E600B006EC607EB6002216DF22045107661E48F5FBAE18A6DDE4D9B36706057E6152DC28E5F2BEE17812057F0C6157A3C8420EAFA5BE83816E5E836E25059AAF7E8DFBF9551A5F5E5A3371E31856EEBEC0A6886A87D710942228FE53DCFCF4D2F075FF17B913872C36DEA0FCBB0CF08664D2E1B464C3BAC040E6D6A47F69BC494C4942ECAF9553D150F202A57548195364A229792C0D8136A676345FCB340B065A51EC358237053565C1E7BC2013C01094D480D9F6B5D80BA08B32FA9BE2CAFF496E48545E9062DD68C2DDD3AB4278C31167CB12B22AA5B7FCE5C5B0601390E9B3ED81B67F20ED529EADA36A25D65FFDC126CE58EBA566F27AF2DF84485D0AA047C89E40B93218D9DC8938B38BB9F94A67C174F22A99AF815E288927BDCACBCECADC65E660D6C5EE5C92C93C84D8CF95EC8FCD41C96FDA9A60718222D29650DDA785966439CB5E8FD4AD009F97BA4E70B881DF726319A0F0ED34A0E75BEA26831E659F53B685F475C3BB88BF171A92740F9D137A64E04F413F1565778915A0887CED9257D3901C09B9FBA90F42977486ECD9887638503BAFB959420B36370E286189F448214F9A7452990117A4E40A8250872022E72B5C126F453EAD0DC3270518F548E2D3B82090EE4FECA91D7CBF01E4B420E41AFC99BBD6FCC866B1860E3B258AE697CCD6FC1BDBB4C18814E58A1C6A98D0AEEE197933E65324F181CBF87ED0F0D87405F7FB865B170ADA773C84AF81C274A42290D5D95D51EF9BD791B05D016002306CE8522B125E50D50A11BF57C768A1E1F64389F701C085D7B0D39BCC004CB0AAEFC287579C87CEC2EB94CA5647C711156E536538440F33A85EB1F22DF6DC899E5B3DF6C92D39CE9CAEC14F4A2827378D964B8711F230C1E9D6C87791DA1F65AAEC4D81ED7488DE43FA86F18BED47C3E74387084ED031D49D94BDA1EB1317B62400F4C3BA147A6AD29D25B7F6E59B98BB7B6299569E582300A1A868489EB3BCEE8EFD7C193EAB1DA0D54AA53517DE0FE7095F5A0677092B20C0C99942F0E3AAC2741E926A0B84D1031D0C999DC1E9B8DDE18521A1D65D2F3C0053CD278784098FD240F70F16FB8B6F7A212843D19DE388D15B80305FFB906504CFCC8B0C8F05FE19EB8974A99C97DAA2D616C454BE333E803B39E080AA7FA8652050944760DA8C1F974FF9001854C0DCA1AC2E4DF7BA442DD60C02B0EF9F79FD4B6C199662577680F022DFA68B2A0B9491F4A9E44EBF7EE56CDD29D8191A2CA22E4089CF3F3953ED7B913BA1C1493A4F15B10CDAA0AEC992971D1423AD1C6957E63A3D46DBAF90F254347B1F4DCD947FBF01D6ECE01AAA2DCF5BD14D0F2B9EA3A352201DA66346D5EDB03D842543E8C4CD330AE2F2CF43CB0F043364CAC020669863442D9DD4DA1B6D3171A766F835A8C4A0CA891C6137AC45AC2F45BACC02E800EE6A0A8A17C1A326752195537F5A9E94D7D363CBEC46D5ABEDF2AF8198F92111124B1FA34EBDF2DB629DA0D81FCEDF6DB16B47867B061BB0472B89BDB8B9CD581D472C27611D604C2801CBA0CD22516499AC370A58ECFA7740CF4382FACB41CF6EF8283E6E910F2700F37AECFEE92EA043E8526E774DB7151C63624967C83BB852FA5E3D997CAC7D34A3CE87DFDAE7A51B053E96771D92CDC330122F8276900498445C3DCD106879F04DFEBBA5608739D84E5CBFC602B3E2B1775D38A2BBCE8C457E13A85497F59526C0FB3892C0A0CE06DCBCB716BB39903EEA1A4EEF760E82A25F5ABFC488EE40700E191BFFC58116959557481CC1ADEDE94EF0582588E435F701151F2125364D6A6D78A4B1B5D8E6F6262735384A517074A6D1EDFF2D303C515A96A2B2B4B9BBBCBEF3FE06070D0E1F4344526A7B8F97A5A6BBBFC7DEE0E4E600000000000000000000000000000000101C2B40
+CtrlMu = hexcontext-string:9C974FE4D8AA2E867B189FE65F594C745380CFA2CF710D96185924162100276EE186CDDBEF303B914863AB836CB8FFAD56E0816DBA72356A733F14143B77EA269B0C9F5F06A354D5ED8A1505DB80D62D6E608E0B26C37A81E6D47F4305291B5E69870343E66D0DE5EBA83035F41B3EF2831A3D57D553AD9E4F85CC1488407D1DC009201A725948ABB7635FD462CA06B3E807848347DB6789A1769C2E19C5484B7C9200FBCE27FA079E84323DDD4F15EBB3E6D9D18BFAB0C004EF426C60E630EAA03D2377B5BA99C2D80F7EF79C6C79E4E3FD154AAFE988A009B6B568E9D2171C7CCFA95633F8E8C93088BA149AB976BA572B3F7703ACB94A93576F9E83C94A
+CtrlMu = digest:SHA3-512
+Ctrl = mu:1
+Ctrl = deterministic:1
diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm
index 24c339c944..3a8cb6f1b1 100644
--- a/util/perl/OpenSSL/paramnames.pm
+++ b/util/perl/OpenSSL/paramnames.pm
@@ -172,6 +172,11 @@ my %params = (
'OSSL_DIGEST_PARAM_SIZE' => "size", # size_t
'OSSL_DIGEST_PARAM_XOF' => "xof", # int, 0 or 1
'OSSL_DIGEST_PARAM_ALGID_ABSENT' => "algid-absent", # int, 0 or 1
+# external mu digest parameters
+ 'OSSL_DIGEST_PARAM_MU_PUB_KEY' => "pub", # octet string
+ 'OSSL_DIGEST_PARAM_MU_CONTEXT_STRING' => "context-string", # octet string
+ 'OSSL_DIGEST_PARAM_MU_DIGEST' => '*OSSL_ALG_PARAM_DIGEST', # utf8 string
+ 'OSSL_DIGEST_PARAM_MU_PROPERTIES' => '*OSSL_ALG_PARAM_PROPERTIES', # utf8 string
# MAC parameters
'OSSL_MAC_PARAM_KEY' => "key", # octet string