Skip to content

PKCS#7: add ML-DSA support#10759

Open
Frauschi wants to merge 1 commit into
wolfSSL:masterfrom
Frauschi:pkcs7_mldsa
Open

PKCS#7: add ML-DSA support#10759
Frauschi wants to merge 1 commit into
wolfSSL:masterfrom
Frauschi:pkcs7_mldsa

Conversation

@Frauschi

Copy link
Copy Markdown
Contributor

Summary

Add ML-DSA as a signature algorithm for PKCS#7/CMS SignedData, for both signing and verification, following RFC 9882 ("Use of ML-DSA in the Cryptographic Message Syntax").

Unlike RSA/ECDSA, ML-DSA is used in CMS "pure" mode: the signature is computed over the complete message - the DER SET OF signed attributes, or the eContent directly when no signed attributes are present - rather than over a pre-computed DigestInfo, with an empty context string and absent signatureAlgorithm parameters.

Changes

wolfcrypt/src/pkcs7.c - core implementation

  • New helpers wc_PKCS7_MlDsaLevelFromOID, wc_PKCS7_BuildPureSigMessage, wc_PKCS7_MlDsaSign, wc_PKCS7_MlDsaVerify, wired into the existing per-algorithm switch sites (GetSignSize, SignedDataGetEncAlgoId, SetPublicKeyOID, CheckPublicKeyDer) and the sign/verify dispatchers.
  • wc_MlDsaKey is always heap-allocated (due to its size), matching the ML-DSA handling in asn.c; the accompanying DecodedCert uses the WC_DECLARE_VAR/WC_ALLOC_VAR_EX macros for WOLFSSL_SMALL_STACK handling.
  • Signer public key is cached in wc_PKCS7 only for RSA/ECC (consumed by the raw-sign callback paths), with the buffer-size bound retained; large PQC keys are not stored.

wolfcrypt/src/hash.c - map the SHAKE128/SHAKE256 OIDs to their hash types in wc_OidGetHash() (RFC 8702 digest algorithms).

certs/mldsa/ - add expanded-only PKCS#8 DER private keys (mldsa44/65/87-key.der) matching the existing self-signed ML-DSA certificates.

wolfcrypt/test/test.c - pkcs7signed_mldsa_test(): round-trip encode→verify across every enabled ML-DSA level with SHA-512 and (where available) SHAKE digests, asserting:

  • recovered content matches,
  • SHAKE digest AlgorithmIdentifiers are encoded with absent parameters per RFC 8702 (both the digestAlgorithms SET and SignerInfo.digestAlgorithm occurrences),
  • a tampered signature is rejected with SIG_VERIFY_E (negative path).

Configuration

Gated behind existing ML-DSA macros - no new public config option. Active when WOLFSSL_HAVE_MLDSA is enabled with ASN.1 support; signing and verification compile independently based on the ML-DSA backend's private-/public-key availability (WOLFSSL_MLDSA_NO_SIGN / WOLFSSL_MLDSA_NO_VERIFY). No change to default builds.

Compatibility

  • No public API changes; no changes to existing struct members.
  • RSA/ECDSA/RSA-PSS SignedData paths are unchanged.

Testing

  • ./configure --enable-all --enable-debug && make && ./wolfcrypt/test/testwolfcrypt - PKCS7signed test passed!, full suite green.
  • Verified clean compilation under degenerate ML-DSA feature combinations (NO_SIGN, NO_VERIFY, both, and NO_MAKE_KEY+NO_SIGN+NO_VERIFY) with the project's -Werror flag set.

Add ML-DSA signing and verification for CMS/PKCS#7 SignedData, following
RFC 9882. ML-DSA is used in CMS "pure" mode: the signature is computed
over the complete message (the DER SET OF signed attributes, or the
eContent when none are present) with an empty context string and absent
signatureAlgorithm parameters, rather than over a pre-computed DigestInfo
as with RSA/ECDSA.

wolfcrypt/src/pkcs7.c:
- New ML-DSA helpers: wc_PKCS7_MlDsaLevelFromOID, wc_PKCS7_BuildPureSigMessage,
  wc_PKCS7_MlDsaSign and wc_PKCS7_MlDsaVerify, wired into the per-algorithm
  switch sites (GetSignSize, SignedDataGetEncAlgoId, SetPublicKeyOID,
  CheckPublicKeyDer) and the sign/verify dispatchers. Only the final FIPS 204
  ML-DSA OIDs are accepted; pre-standard draft Dilithium OIDs are not.
- GetSignSize derives the ML-DSA signature length from the parameter set.
- wc_MlDsaKey is always heap allocated (it embeds multi-KB key buffers); the
  accompanying DecodedCert uses the WC_DECLARE_VAR/WC_ALLOC_VAR_EX macros for
  stack-vs-heap handling under WOLFSSL_SMALL_STACK.

wolfssl/wolfcrypt/pkcs7.h:
- Gate the cached signer public key buffer (publicKey/publicKeySz) behind the
  new WOLFSSL_PKCS7_HAVE_RAW_SIGN_CALLBACK macro. It is consumed only by the
  RSA/ECC raw-sign callback paths, so large PQC keys are no longer copied
  into the fixed RSA-sized buffer.

wolfcrypt/src/hash.c:
- Map the SHAKE128/SHAKE256 OIDs to their hash types in wc_OidGetHash().

certs/mldsa:
- Add expanded-only PKCS#8 DER private keys (mldsa44/65/87-key.der) matching
  the self-signed ML-DSA certificates, with README and include.am updates.

wolfcrypt/test/test.c:
- Add pkcs7signed_mldsa_test(): round-trip encode/verify of SignedData across
  ML-DSA-44/65/87, with and without signed attributes, including a check that
  the digest algorithm parameters are encoded as expected.
@Frauschi Frauschi self-assigned this Jun 23, 2026
@Frauschi Frauschi marked this pull request as ready for review June 23, 2026 11:52
@github-actions

Copy link
Copy Markdown

retest this please

@github-actions

Copy link
Copy Markdown

@Frauschi Frauschi assigned wolfSSL-Bot and unassigned Frauschi Jun 23, 2026
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.

2 participants