EVP_SIGNATURE-ML-DSA(7ossl)

πŸ’– EVP_SIGNATURE-ML-DSA(7ossl) πŸ’–

okay so bestie, welcome to the ML-DSA glow-up ✨ This page lives in the OpenSSL manual, and it's all about EVP_SIGNATURE-ML-DSA, EVP_SIGNATURE-ML-DSA-44, EVP_SIGNATURE-ML-DSA-65 and EVP_SIGNATURE-ML-DSA-87 β€” aka the EVP_PKEY ML-DSA signature implementations. yes that's a whole lotta names, we'll get through it together 🫢


🌸 NAME 🌸

EVP_SIGNATURE-ML-DSA, EVP_SIGNATURE-ML-DSA-44, EVP_SIGNATURE-ML-DSA-65, EVP_SIGNATURE-ML-DSA-87 β€” the EVP_PKEY ML-DSA signature implementations, no cap πŸ’…


✨ DESCRIPTION ✨

So the ML-DSA-44, ML-DSA-65 and ML-DSA-87 EVP_PKEY implementations support key generation, plus one-shot sign and verify using the ML-DSA signature schemes described in [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final). iconic behavior honestly πŸ‘‘

The different algorithm names correspond to the parameter sets defined in FIPS 204 Section 4 Table 1 (same link, [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) πŸ’–). lowkey important: the signatures range in size from ~2.5K to ~4.5K depending on the type chosen. There are also 3 different security categories, again depending on the type πŸŽ€

EVP_SIGNATURE_fetch(3) can be used to explicitly fetch one of the 3 algorithms, which can then be used with EVP_PKEY_sign_message_init(3), EVP_PKEY_sign(3), EVP_PKEY_verify_message_init(3), and EVP_PKEY_verify(3) to perform one-shot message signing or signature verification. that's the whole squad right there 🫢

The normal signing process (called Pure ML-DSA Signature Generation) encodes the message internally as:

0x00 || len(ctx) || ctx || message

where ctx is some optional value of size 0x00..0xFF. This process is defined in [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) Algorithm 2 step 10 and Algorithm 3 step 5. OpenSSL also allows the message to not be encoded β€” which is required for testing πŸ‘€. And heads up: OpenSSL does not support Pre Hash ML-DSA Signature Generation, but the user can do this themselves by doing Pre hash encoding externally and then choosing the option to not encode the message. iykyk 🧚

πŸ¦‹ ML-DSA Signature Parameters πŸ¦‹

The following parameter can be used for both signing and verification. It may be set by passing an OSSL_PARAM array to EVP_PKEY_sign_message_init(3) or EVP_PKEY_verify_message_init(3):

  • "context-string" (OSSL_SIGNATURE_PARAM_CONTEXT_STRING) <octet string> β€” a string of octets with length at most 255. By default it is the empty string.

The following parameters can be used when signing. They can be set by passing an OSSL_PARAM array to EVP_PKEY_sign_init_ex2(3):

  • "message-encoding" (OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING) <integer> β€” the default value of 1 uses 'Pure ML-DSA Signature Generation' as described above. Setting it to 0 does not encode the message, which is used for testing πŸ§ͺ. The message encoding steps are defined in [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) Algorithm 2 step 10 and Algorithm 3 step 5.
  • "test-entropy" (OSSL_SIGNATURE_PARAM_TEST_ENTROPY) <octet string> β€” used for testing, to pass an optional deterministic per-message random value. If set, the size must be 32 bytes. don't play with it 🫣
  • "deterministic" (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. periodt πŸ’€
  • "mu" (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 "mu" value from [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) Algorithm 7 step 6 and Algorithm 8 step 7.
ngl, note that the message encoding steps from [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. so like, don't forget that πŸ™ˆ

See EVP_PKEY-ML-DSA(7) for info related to ML-DSA keys πŸ”‘

πŸ’œ FIPS indicator πŸ’œ

The following parameter can be retrieved using EVP_PKEY_CTX_get_params(3):

  • "fips-indicator" (OSSL_SIGNATURE_PARAM_FIPS_APPROVED_INDICATOR) <integer> β€” the OpenSSL FIPS provider returns 1 if the operation is FIPS approved, or 0 otherwise. See "FIPS indicators" in fips_module(7) for more info. Supplying "test-entropy", or setting the test-only "message-encoding" parameter to 0, causes this indicator to return 0 🌈

πŸŽ€ NOTES πŸŽ€

For backwards compatibility reasons, EVP_DigestSignInit_ex(), EVP_DigestSign(), EVP_DigestVerifyInit_ex() and EVP_DigestVerify() may also be used β€” but the digest passed in mdname must be NULL. like, mandatory mandatory πŸ’…


πŸ”₯ EXAMPLES πŸ”₯

To sign a message using an ML-DSA EVP_PKEY structure:

void do_sign(EVP_PKEY *key, 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
    };
    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);

    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);
    sig = OPENSSL_zalloc(sig_len);
    EVP_PKEY_sign(sctx, sig, &sig_len, msg, msg_len);
    ...
    OPENSSL_free(sig);
    EVP_SIGNATURE_free(sig_alg);
    EVP_PKEY_CTX_free(sctx);
}

🌈 SEE ALSO 🌈

EVP_PKEY-ML-DSA(7) provider-signature(7), EVP_PKEY_sign(3), EVP_PKEY_verify(3), [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final)


⭐ HISTORY ⭐

This functionality was added in OpenSSL 3.5. a whole era 🌸


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](https://www.openssl.org/source/license.html).


okay that's the whole page bestie, you ate πŸ’…βœ¨ this was OpenSSL 3.5.9, dated 2026-09-29, and the manual page is EVP_SIGNATURE-ML-DSA(7ossl) β€” go forth and sign something iconic πŸ¦‹πŸ’–πŸŽ€

πŸ“¦ where this page comes from