EVP_SIGNATURE-ML-DSA(7SSL)
π EVP_SIGNATURE-ML-DSA π
bestie. sit down. grab a strawberry. this is the EVP_SIGNATURE-ML-DSA page out of the OpenSSL manual collection (that's the 7SSL corner of the docs, mwah). it's giving post-quantum signature realness and i'm obsessed, so let's go β¨
πΈ NAME πΈ
EVP_SIGNATURE-ML-DSA, EVP_SIGNATURE-ML-DSA-44, EVP_SIGNATURE-ML-DSA-65, and EVP_SIGNATURE-ML-DSA-87 β these are literally the EVP_PKEY ML-DSA signature implementations. that's it, that's the whole name section π
π DESCRIPTION π
so the ML-DSA-44, ML-DSA-65 and ML-DSA-87 EVP_PKEY implementations support key generation, and one-shot sign and verify, using the ML-DSA signature schemes described in FIPS 204 (yes, that one: https://csrc.nist.gov/pubs/fips/204/final) π«Ά
the different algorithm names correspond to the parameter sets defined in FIPS 204 Section 4 Table 1 π (the signatures range in size from ~2.5K to ~4.5K depending on the type chosen, no cap). and there are 3 different security categories also depending on the type. three of them. we love options.
EVP_SIGNATURE_fetch(3) can be used to explicitly fetch one of the 3 algorithms β which you can then use with EVP_PKEY_sign_message_init(3), EVP_PKEY_sign(3), EVP_PKEY_verify_message_init(3), and EVP_PKEY_verify(3) to actually perform one-shot message signing or signature verification. slay.
okay now the normal signing process, which is called Pure ML-DSA Signature Generation. it 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 (same link, 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 bestie hear me β OpenSSL does not support Pre Hash ML-DSA Signature Generation, but you can do it yourself by doing the Pre hash encoding externally and then choosing the option to not encode the message. diy queen π
π§ 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 of1uses 'Pure ML-DSA Signature Generation' as described above. setting it to0does 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 be32bytes."deterministic"(OSSL_SIGNATURE_PARAM_DETERMINISTIC)<integer>β the default value of0causes the per message randomness to be randomly generated using a DRBG. setting this to a nonzero value causes the per message randomness to be set to32bytes of zeros. this value is ignored if"test-entropy"is provided π"mu"(OSSL_SIGNATURE_PARAM_MU)<integer>β the default value of0causes sign and verify operations to process a raw message. setting this to a nonzero value 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.- 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 nonzero. - see
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. literally just ghosted.
- note that the message encoding steps from FIPS 204 (
see EVP_PKEY-ML-DSA(7) for information 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 returns1if the operation is FIPS approved, or0otherwise. see "FIPS indicators" infips_module(7)for more information. supplying"test-entropy", or setting the test-only"message-encoding"parameter to0, causes this indicator to return0. so like... not approved, bestie. it said what it said π
π 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. don't fight it, it's the rule π
π§ EXAMPLES π§
signing with an ML-DSA EVP_PKEY structure
to sign a message using an ML-DSA EVP_PKEY structure:
void do_sign(EVP_PKEY *key, const unsigned char *msg, size_t msg_len)
{
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);
/* 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);
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);
}
signing with an ML-DSA EVP_PKEY and an external mu
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);
}
π¦ SEE ALSO π¦
EVP_PKEY-ML-DSA(7), EVP_MD-ML-DSA-MU(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. new girl on the block, and she ate π₯
π 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.
okay that's the whole page, bestie, you're fully caught up π this cutie came to you from the 4.0.3 era, dated 2026-09-29, and it's filed under EVP_SIGNATURE-ML-DSA(7SSL). go forth and sign something iconic π«Άπ
π¦ where this page comes from
- ubuntu stonking: openssl 4.0.3-1ubuntu1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms