EVP_MD-ML-DSA-MU(7)

NAME
EVP_MD-ML-DSA-MU - The ML-DSA-MU EVP_MD implementation

DESCRIPTION
Support for computing the value of external mu for ML-DSA using the EVP_MD API.

Normally the value of "mu" is calculated internally as part of an ML-DSA sign or verify operation. "mu" is defined as:
    mu = SHAKE256(tr || M', 64)

Where 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 "mu" calculation to be done externally and then "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 "mu" value can then be supplied as an input to EVP_SIGNATURE-ML-DSA(7) using the ML-DSA Signature Parameter OSSL_SIGNATURE_PARAM_MU (i.e. "mu"). This allows larger messages to be hashed (or hidden) before they are passed to pure ML-DSA sign or verify operations.

  Identities
This implementation is available with the FIPS provider as well as the default provider, and is identified with the name "ML-DSA-MU".

  Parameters
This implementation supports the following settable OSSL_PARAM(3) parameters:

"pub" (OSSL_DIGEST_PARAM_MU_PUB_KEY) <octet string>
    A ML-DSA encoded public key value of size 1312, 1952 or 2592 bytes depending on the respective key type of ML-DSA-44, ML-DSA-65 or ML-DSA-87. This can be retrieved from a 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.

"context-string" (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.

"digest" (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 "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.

    It is the users responsibility to chose a digest that has the minimum required security strength. To match the security category for ML-DSA-44, ML-DSA-65 and ML-DSA-87 the user should chose a digest that has a security strength of at least 128 bits, 192 bits and 256 bits respectively.

    OpenSSL accepts the following digest names:

    "SHA-224" and "SHA3-224"
        These digests have a security strength of 112 bits.

    "SHA-256" and "SHA3-256"
        These digests have a security strength of 128 bits.

    "SHA-384" and "SHA3-384"
        These digests have a security strength of 192 bits.

    "SHA-512" and "SHA3-512"
        These digests have a security strength of 256 bits.

    "SHAKE-128" and "SHAKE-256"
        For SHAKE-128 and SHAKE-256 the expected XOF digest lengths are 32 and 64 respectively which correspond to a security strength of 128 and 256 bits respectively.

    The total size of the "HashedMessage" passed to EVP_DigestUpdate() MUST match the size of the digest.

"properties" (OSSL_DIGEST_PARAM_MU_PROPERTIES) <utf8 string>
    Sets the properties to be queried when trying to fetch the underlying digest.

  Gettable Parameters
This implementation supports the common gettable parameters described in EVP_MD-common(7).

CONFORMING TO
FIPS 204 and https://csrc.nist.gov/csrc/media/Projects/post-quantum-cryptography/documents/faq/fips204-sec6-03192025.pdf

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))
    }

SEE ALSO
EVP_SIGNATURE-ML-DSA(7), EVP_PKEY-ML-DSA(7), provider-digest(7), OSSL_PROVIDER-FIPS(7), OSSL_PROVIDER-default(7)

HISTORY
The ML-DSA-MU pseudo-digest was added in OpenSSL 4.0.0.

COPYRIGHT
Copyright 2025-2026 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>.