mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-10 23:20:53 +02:00
bc3288204e
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. - InitWithCert copies the signer public key into the RSA-sized publicKey buffer only for RSA/ECC certs (the raw-sign callback consumers); large PQC keys such as ML-DSA would overflow it and are never read back, so publicKeySz stays 0. - 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. - wc_PKCS7_SignedDataBuildSignature skips building the DigestInfo for ML-DSA, which signs the full message in pure mode and never consumes it. - wc_PKCS7_MlDsaSign wraps the ML-DSA private-key decode in PRIVATE_KEY_UNLOCK/PRIVATE_KEY_LOCK. Unlike RSA/ECC, the FIPS module gates wc_MlDsaKey_PrivateKeyDecode behind the private-key read lock, so signing would otherwise fail with FIPS_PRIVATE_KEY_LOCKED_E under --enable-fips. The macros are no-ops in non-FIPS builds. wolfssl/wolfcrypt/pkcs7.h: - Document that the fixed-size signer public key buffer (publicKey/publicKeySz) holds only RSA/ECC keys; it stays RSA-sized. 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. The expanded-only shape (no seed) decodes via wc_MlDsaKey_ImportPrivRaw without keygen-from-seed or the ASN template, so pkcs7signed_mldsa_test also passes in WOLFSSL_MLDSA_NO_MAKE_KEY and non-WOLFSSL_ASN_TEMPLATE builds. certs/renewcerts.sh: - Generate the mldsa<N>-key.der files from the matching mldsa<N>-key.pem in the expanded-only shape (openssl pkey -provparam ml-dsa.output_formats=priv), so a regeneration keeps the DER key in step with the cert. The OpenSSL detection probe now requires both ML-DSA keygen and that conversion across all three levels, so the block runs fully (matched cert+key) or is skipped entirely rather than aborting mid-way. 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. The message-digest OID is selected from the enabled hash set (SHA-512, else SHA-256, else SHA-1) so the test builds when SHA-512 is disabled. A negative case confirms ML-DSA rejects a caller-supplied pre-computed content hash with BAD_FUNC_ARG.
ML-DSA (FIPS 204) test key material for wolfSSL tests.
File variants, per level N in {44, 65, 87}:
mldsa<N>_bare-seed.der raw 32-byte seed
mldsa<N>_seed-only.der PKCS#8 with seed-only private key
mldsa<N>_bare-priv.der raw expanded private key
mldsa<N>_priv-only.der PKCS#8 with expanded-only private key
mldsa<N>_seed-priv.der PKCS#8 with seed-and-expanded private key
mldsa<N>_oqskeypair.der liboqs concatenated (priv || pub) format
mldsa<N>_pub-spki.der SubjectPublicKeyInfo wrapping the public key
Self-signed certificates and their matching keys (used by the PKCS#7/CMS
SignedData tests), per level N in {44, 65, 87}:
mldsa<N>-cert.pem / mldsa<N>-cert.der self-signed ML-DSA certificate
mldsa<N>-key.pem matching private key (PEM,
seed-and-expanded PKCS#8)
mldsa<N>-key.der matching private key (DER,
expanded-only PKCS#8)
The mldsa<N>-key.der files were derived from the matching mldsa<N>-key.pem
using OpenSSL 3.5+, selecting the portable expanded-only private-key shape:
openssl pkey -in mldsa<N>-key.pem \
-provparam ml-dsa.output_formats=priv -outform DER \
-out mldsa<N>-key.der
Unlike the standalone mldsa<N>_priv-only.der vectors above, these correspond
to the public key in mldsa<N>-cert.der.
The *_pub-spki.der files were derived from the matching *_priv-only.der files
using OpenSSL 3.5+:
openssl pkey -inform DER -in mldsa<N>_priv-only.der \
-pubout -outform DER -out mldsa<N>_pub-spki.der
Regenerating the private-key variants requires producing each of the
PKCS#8 shape options explicitly; OpenSSL's default output is the
seed-and-expanded form.