Skip to content

Feature/pki pqc mldsa 27239#32007

Open
netanmangal wants to merge 6 commits into
hashicorp:mainfrom
netanmangal:feature/pki-pqc-mldsa-27239
Open

Feature/pki pqc mldsa 27239#32007
netanmangal wants to merge 6 commits into
hashicorp:mainfrom
netanmangal:feature/pki-pqc-mldsa-27239

Conversation

@netanmangal

Copy link
Copy Markdown

Description

Add ML-DSA (FIPS 204) post-quantum digital signature support to the PKI secrets engine.

This PR implements ML-DSA-65 and ML-DSA-87 as new key types for Vault's PKI secrets engine, enabling quantum-resistant
certificate issuance. This addresses #27239, which has been open since May 2024.

Why now: France's ANSSI will
https://gizmodo.com/the-quantum-threat-to-encryption-is-coming-france-just-set-a-2027-deadline-2000773650. NIST finalized
ML-DSA (FIPS 204) in August 2024. Competitors (EJBCA, Microsoft AD CS, AWS Private CA) have already shipped PQC
certificate support. Vault's Transit engine already uses circl for experimental ML-DSA — this PR extends that to PKI.

What's included:

  • ml-dsa-65 and ml-dsa-87 as key_type options for PKI roles (path_roles.go)
  • ML-DSA key pair generation using Cloudflare's circl (already vendored since Vault 1.19+)
  • ML-DSA certificate signing for both self-signed roots and CA-issued leaf certificates (ca_util.go, helpers.go)
  • Custom ASN.1 X.509 certificate and CSR construction (mldsa.go) — required because Go's x509.CreateCertificate does not
    yet support ML-DSA
  • Full extension parsing in ML-DSA certificates: BasicConstraints, SubjectKeyId, AuthorityKeyId, KeyUsage, ExtKeyUsage,
    SANs
  • Post-signing signature verification using circl's ML-DSA Verify
  • Distinct PEM labels (ML-DSA-65 PRIVATE KEY, ML-DSA-87 PRIVATE KEY) for interoperability
  • OIDs per NIST FIPS 204: 2.16.840.1.101.3.4.3.18 (ML-DSA-65), 2.16.840.1.101.3.4.3.19 (ML-DSA-87)
  • 44 unit tests covering key generation, PEM round-trip, certificate signing, CSR creation, extension preservation, and
    full bundle paths — all passing with zero regressions on existing tests

Backward compatible: No changes to existing RSA, ECDSA, or Ed25519 workflows. ML-DSA is additive and opt-in.

Library choice: Uses circl (already in Vault's dependency tree) per maintainer guidance in #27239. Can be swapped to Go
stdlib crypto/mldsa when Go 1.27 ships.

@netanmangal
netanmangal requested review from a team as code owners June 22, 2026 16:42
@netanmangal
netanmangal requested a review from Monkeychip June 22, 2026 16:42
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

The `vercel.json` schema validation failed with the following message: should NOT have additional property `public`

Learn More: https://vercel.com/docs/concepts/projects/project-configuration

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jun 22, 2026
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vault-ui Error Error Jun 22, 2026 7:05pm

Request Review

@hashicorp-cla-app

hashicorp-cla-app Bot commented Jun 22, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@hashicorp-cla-app

Copy link
Copy Markdown

CLA assistant check

Thank you for your submission! We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution. Read and sign the agreement

Learn more about why HashiCorp requires a CLA and what the CLA includes

Have you signed the CLA already but the status is still pending? Recheck it.

Add post-quantum ML-DSA (FIPS 204) key types throughout the PKI engine
and certutil SDK package. This includes PrivateKeyType constants,
key type validation, signature length handling, public key comparison,
subject key ID generation, and role configuration support.

Uses cloudflare/circl library which is already a dependency of Vault.

Relates to hashicorp#27239

Signed-off-by: netanmangal <imnetanmangal@gmail.com>
Implement ML-DSA-65 and ML-DSA-87 private key generation using the
cloudflare/circl library. Keys are generated via circl's GenerateKey
and stored as raw serialized bytes in PRIVATE KEY PEM blocks.

Also adds key deserialization support in ParseDERKey, getPKCS8Type,
and getSigner methods so ML-DSA keys can be round-tripped through
storage and PEM encoding.

Relates to hashicorp#27239

Signed-off-by: netanmangal <imnetanmangal@gmail.com>
Implement custom X.509 certificate and CSR creation for ML-DSA keys.
Since Go's standard crypto/x509 library does not yet support ML-DSA
signature algorithms, this adds manual ASN.1 construction for:

- Self-signed root CA certificates with ML-DSA keys
- CA-signed leaf certificates using ML-DSA issuer keys
- Certificate signing requests (CSRs) with ML-DSA keys

The implementation constructs TBS certificates and CSRs using ASN.1,
signs them with circl's ML-DSA signer (which implements crypto.Signer),
and assembles the final DER-encoded structures.

Uses OIDs from NIST FIPS 204:
- ML-DSA-65: 2.16.840.1.101.3.4.3.18
- ML-DSA-87: 2.16.840.1.101.3.4.3.19

Relates to hashicorp#27239

Signed-off-by: netanmangal <imnetanmangal@gmail.com>
Add comprehensive tests for ML-DSA-65 and ML-DSA-87 support:

- Key generation for both ML-DSA-65 and ML-DSA-87
- Key round-trip through PEM encoding and parsing
- Public key comparison
- Subject key ID generation
- Public key size reporting
- Key type and signature length validation
- Self-signed certificate creation
- Certificate with SANs (DNS, IP, email, URI)
- CSR creation
- Full certificate creation via CreationBundle
- Key bundle creation and PEM export

Also fixes CSR signature validation to skip Go's standard
CheckSignature for ML-DSA CSRs, since Go's x509 library does not
yet support ML-DSA signature verification.

Relates to hashicorp#27239

Signed-off-by: netanmangal <imnetanmangal@gmail.com>
- C1: Fix signCertificate() to handle ML-DSA CA keys: add MLDSA65/87
  cases to switch, call createMLDSACertificate() for ML-DSA CAs,
  fall back to parseMLDSACertificate(), and skip Go's CheckSignature
  for ML-DSA CSRs (unsupported by stdlib)

- C2: Use distinct PEM labels "ML-DSA-65 PRIVATE KEY" and
  "ML-DSA-87 PRIVATE KEY" instead of generic "PRIVATE KEY".
  Update ParseDERKey(), getPKCS8Type(), extractAndSetPrivateKey(),
  getSigner(), ToCertBundle(), ToCSRBundle(), and ToPrivateKeyPemString()

- C3: parseMLDSACertificate() now parses all extensions: Basic
  Constraints (IsCA/MaxPathLen), SubjectKeyId, AuthorityKeyId,
  KeyUsage, SANs (DNS/IP/Email/URI), and ExtKeyUsage

- C4: Document that ML-DSA key OID = signature OID per FIPS 204

- M2: Fix CSRBundle.ToParsedCSRBundle() to recognize MLDSA65Block
  and MLDSA87Block PEM types

- M3: Add post-creation signature verification in
  createMLDSACertificate() and createMLDSACSR()

- M6: Remove dead code marshalTimeASN1()

- Clean up stale placeholder Ed25519 comments

Relates to hashicorp#27239

Signed-off-by: netanmangal <imnetanmangal@gmail.com>
Fix pre-existing struct field tag formatting in PolicyIdentifierWithQualifierEntry
(comma-separated tag pairs replaced with space-separated to satisfy go vet).

Relates to hashicorp#27239

Signed-off-by: netanmangal <imnetanmangal@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cryptosec secret/pki size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant