Skip to content

Add Pure ML-DSA to the SymCrypt provider#161

Open
mamckee wants to merge 21 commits into
mainfrom
mamckee-ml-dsa
Open

Add Pure ML-DSA to the SymCrypt provider#161
mamckee wants to merge 21 commits into
mainfrom
mamckee-ml-dsa

Conversation

@mamckee

@mamckee mamckee commented Feb 25, 2026

Copy link
Copy Markdown
Collaborator

This PR adds pure ML-DSA to the SymCrypt provider through the provider signature interface. This PR was tested with the EVP crypto API, as well as TLS and CMS APIS.

This PR also adds an ML-DSA encoder/decoder for testing on platforms with OpenSSL versions that don't support ML-DSA by default (e.g. AZL3 with OpenSSL 3.3).

Composite ML-DSA and external mu will be added later.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds pure ML-DSA (Module-Lattice-Based Digital Signature Algorithm) signature support to the SymCrypt provider, implementing the signature algorithm through OpenSSL's provider interface. The implementation includes key management, signature operations, and encoder/decoder functionality for ML-DSA-44, ML-DSA-65, and ML-DSA-87 variants. The PR also introduces ML-DSA encoders/decoders for compatibility with OpenSSL versions that don't natively support ML-DSA (e.g., OpenSSL 3.3 on AZL3).

Changes:

  • Added ML-DSA signature operations including sign/verify interfaces with support for context strings and parameter validation
  • Implemented key management for ML-DSA keys supporting private seed and private key formats, including key generation, import/export, and matching operations
  • Added PEM/DER encoders and decoders for ML-DSA PrivateKeyInfo, EncryptedPrivateKeyInfo, and SubjectPublicKeyInfo
  • Integrated ML-DSA into OpenSSL provider with TLS signature algorithm capabilities
  • Modified fallthrough attribute syntax in AES cipher code for better portability

Reviewed changes

Copilot reviewed 11 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
SymCryptProvider/src/signature/p_scossl_mldsa.h Header defining ML-DSA signature context structures and algorithm info
SymCryptProvider/src/signature/p_scossl_mldsa.c Implementation of ML-DSA signature operations (sign, verify, parameter handling)
SymCryptProvider/src/keymgmt/p_scossl_mldsa_keymgmt.h Header for ML-DSA key management interface
SymCryptProvider/src/keymgmt/p_scossl_mldsa_keymgmt.c Key management implementation including keygen, import/export, and key operations
SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Encoder for ML-DSA keys to various formats (DER/PEM, text)
SymCryptProvider/src/decoder/p_scossl_decode_mldsa.c Decoder for ML-DSA keys from various formats
SymCryptProvider/src/p_scossl_base.c Provider registration integrating ML-DSA algorithms and TLS capabilities
SymCryptProvider/src/p_scossl_names.h Algorithm name definitions for ML-DSA variants
SymCryptProvider/inc/scossl_provider.h Constants, OIDs, and parameter definitions for ML-DSA
SymCryptProvider/CMakeLists.txt Build configuration updated to include ML-DSA source files
SymCryptProvider/src/ciphers/p_scossl_aes.c Fallthrough attribute changed from GCC-specific to standard comment

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/inc/scossl_provider.h Outdated
Comment thread SymCryptProvider/inc/scossl_provider.h Outdated
Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/src/keymgmt/p_scossl_mldsa_keymgmt.c Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 14 changed files in this pull request and generated 11 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread SymCryptProvider/src/decoder/p_scossl_decode_mldsa.c Outdated
Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/inc/scossl_provider.h Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.h Outdated
Comment thread SymCryptProvider/src/keymgmt/p_scossl_mldsa_keymgmt.c
Comment thread SymCryptProvider/src/decoder/p_scossl_decode_mldsa.c
Comment thread SymCryptProvider/inc/scossl_provider.h Outdated
Comment thread SymCryptProvider/src/decoder/p_scossl_decode_mldsa.c

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 15 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread SymCryptProvider/src/decoder/p_scossl_decode_mldsa.c Outdated
Comment thread SymCryptProvider/src/encoder/p_scossl_encode_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 17 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread SymCryptProvider/src/keymgmt/p_scossl_mldsa_keymgmt.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/keymgmt/p_scossl_mldsa_keymgmt.c
Comment thread SymCryptProvider/src/ciphers/p_scossl_aes.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c Outdated
Comment thread SymCryptProvider/src/signature/p_scossl_mldsa.c
_In_ OSSL_CALLBACK *param_cb, _In_ void *cbarg);

SCOSSL_STATUS p_scossl_mldsa_keymgmt_get_encoded_key(_In_ const SCOSSL_MLDSA_KEY_CTX *keyCtx, SYMCRYPT_MLDSAKEY_FORMAT format,
_Out_writes_bytes_(*pcbKey) PBYTE *ppbKey, _Out_ SIZE_T *pcbKey);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out_writes_bytes(*pcbKey) PBYTE *ppbKey, Out

NP: Inout for both of these; same as ML-KEM

extern "C" {
#endif

#define SCOSSL_MLDSA_PRIVATE_SEED_LENGTH 64

@samuel-lee-msft samuel-lee-msft Jul 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SCOSSL_MLDSA_PRIVATE_SEED_LENGTH 64

The private seed length for ML-DSA is normally considered to be 32 bytes (it's exposed as SYMCRYPT_MLDSA_PRIVATE_SEED_SIZE in recent SymCrypt.h)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And yeah, that is the expectation of OpenSSL too: https://docs.openssl.org/master/man7/EVP_PKEY-ML-DSA/#keygen-parameters

case SYMCRYPT_MLDSA_PARAMS_MLDSA65:
return 1952;
case SYMCRYPT_MLDSA_PARAMS_MLDSA87:
return 2592;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of comments:

  1. these are byte lengths, not bit lengths - you'll want to multiply by 8 I think
  2. Consider using SYMCRYPT_MLDSA_PUBLIC_KEY_SIZE_MLDSAxx macros rather than magic values for the byte-lengths

if (ret != SCOSSL_SUCCESS && allocatedKey)
{
OPENSSL_secure_clear_free(pbKey, cbKey);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NP: This logic seems fine, but diverges from ML-KEM for not much reason, prefer both to have the same shape

OPENSSL_secure_clear_free(genCtx, sizeof(SCOSSL_MLDSA_KEYGEN_CTX));
}

static SCOSSL_MLDSA_KEYGEN_CTX *p_scossl_mldsa_keygen_init(_In_ const OSSL_PARAM params[], _In_ SYMCRYPT_MLDSA_PARAMS mldsaParams)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In

NP: In doesn't mean anything for an enum type which is always passed by value


typedef struct
{
OSSL_LIB_CTX *libctx;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see that this is ever set or used?

OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED, NULL, 0),
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),

Nit: upstream doesn't seem to support this for ML-DSA, should SCOSSL?

return SCOSSL_FAILURE;
}

if (!OSSL_PARAM_set_int(p, cbSignature))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int

NP: size_t?

memcmp(pbKey1, pbKey2, cbKey1) != 0)
{
goto cleanup;
}

@samuel-lee-msft samuel-lee-msft Jul 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tend to think it's safer to just compare public keys; we don't ever have a scenario where we have an initialized key and the public key is not available for comparison.

memcmp with the private keys could possibly be considered a sidechannel issue in some scenario.


cbKey = ASN1_STRING_length(p8Data);

format = cbKey == 64 ? SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED : SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_KEY;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

64

This should be 32, and should probably be SYMCRYPT_MLDSA_PRIVATE_SEED_SIZE rather than a magic number


#define SCOSSL_MAKE_MLDSA_DECODER(decoderType, bits) \
static SCOSSL_MLDSA_KEY_CTX \
*p_scossl_##decoderType##_to_mldsa##bits(_In_ SCOSSL_DECODE_CTX *ctx, _In_ BIO *bio) \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bits

NP: paramSet

goto cleanup;
}

if (p_scossl_mldsa_keymgmt_get_encoded_key(keyCtx, SYMCRYPT_MLDSAKEY_FORMAT_PUBLIC_KEY, &pbKey, &cbKey) != SCOSSL_SUCCESS)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p_scossl_mldsa_keymgmt_get_encoded_key

Should align this with ML-KEM per #171 (comment) - either both let get_encoded_key allocate, or both allocate the public key


if (keyCtx->format == SYMCRYPT_MLDSAKEY_FORMAT_PRIVATE_SEED)
{
if (BIO_printf(out, "MLDSA Private-Key (512 bit private seed encoding):\nprivate-seed") <= 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

512

256

return p_scossl_encode_does_selection(&p_scossl_mldsa_##encoderType##_der_desc, selection); \
}

#define MLDSA_ENCODER_DISPATCH(encoderType, bits) \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bits

one more bits->paramSet nitpick

@samuel-lee-msft samuel-lee-msft left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants