← Week 4: Migration Planning

Day 25: Extending toy-pki with ML-DSA Signing

Phase 3 · August 1, 2026

← Week 4: Migration Planning

Agenda (2–3 hours)

  • Read (20 min): rcgen ML-DSA support status (check GitHub issues); aws-lc-rs signature + ML_DSA_65
  • Build (120 min): Add ML-DSA signing capability to toy-pki
  • Verify (30 min): Sign and verify a cert structure with ML-DSA
← Week 4: Migration Planning

The Challenge: rcgen + ML-DSA

As of 2026, rcgen may not yet natively support ML-DSA as a signing algorithm
(it uses ring or aws-lc-rs under the hood depending on features).

Two approaches:

Option A: Use rcgen's KeyPair::from_pem_and_sign_algo with a custom algorithm
if rcgen has added ML-DSA support (check the latest release).

Option B: Build the certificate TBSCertificate with rcgen but sign it manually
using aws-lc-rs ML-DSA. This requires understanding how rcgen exposes the TBS bytes.

Either way is a learning exercise. Document which approach works and why.

← Week 4: Migration Planning

Manual TBSCertificate Signing (Option B)

use rcgen::{CertificateParams, KeyPair};
use aws_lc_rs::signature::{KeyPair as DsaKeyPair, ML_DSA_65};

// Generate an ML-DSA key pair
let ml_dsa_kp = DsaKeyPair::generate(&ML_DSA_65)?;
let pub_key_bytes = ml_dsa_kp.public_key().as_ref().to_vec();

// Use rcgen to build TBSCertificate bytes (the part that gets signed)
// rcgen 0.13 allows extracting the TBS bytes before finalizing
// (check the API — this varies by version)

// Sign TBS bytes with ML-DSA
let tbs_bytes: Vec<u8> = get_tbs_bytes_from_rcgen(&params)?;
let signature = ml_dsa_kp.sign(&tbs_bytes)?;

// Assemble final DER: TBSCertificate + algorithm + signature
← Week 4: Migration Planning

What a ML-DSA Certificate Looks Like

An ML-DSA-65 leaf cert DER will contain:

SEQUENCE {                          -- Certificate
  SEQUENCE {                        -- TBSCertificate
    [0] INTEGER 2                   -- version v3
    INTEGER <serial>
    SEQUENCE { OID 2.16.840.1.101.3.4.3.18 }  -- ML-DSA-65 signature algorithm OID
    SEQUENCE { ... }                -- issuer DN
    SEQUENCE { ... }                -- validity
    SEQUENCE { ... }                -- subject DN
    SEQUENCE { OID, BIT STRING }    -- SubjectPublicKeyInfo (ML-DSA or ML-KEM)
    [3] { ... }                     -- extensions
  }
  SEQUENCE { OID 2.16.840.1.101.3.4.3.18 }    -- signatureAlgorithm
  BIT STRING <3293 bytes>           -- ML-DSA-65 signature
}

Total: ~3.5–4 KB for a typical leaf cert. Compare to ~1 KB for ECDSA.

← Week 4: Migration Planning

Verifying the ML-DSA Certificate

use aws_lc_rs::signature::{UnparsedPublicKey, ML_DSA_65_PUBLIC_KEY};
use x509_parser::prelude::*;

// Parse the cert
let (_, cert) = X509Certificate::from_der(&cert_der)?;

// Extract the TBSCertificate bytes (what was signed)
let tbs_der = cert.tbs_certificate.as_ref();

// Extract the signature
let sig_bytes = cert.signature_value.as_ref();

// Extract the public key
let pub_key_bytes = cert.public_key().subject_public_key.as_ref();

// Verify
let pub_key = UnparsedPublicKey::new(&ML_DSA_65_PUBLIC_KEY, pub_key_bytes);
pub_key.verify(tbs_der, sig_bytes)?;
println!("ML-DSA certificate signature valid ✓");
← Week 4: Migration Planning

Adding to toy-pki CLI

If rcgen supports ML-DSA natively:

pki issue server localhost --algorithm ml-dsa-65

If manual signing is required, add a separate command:

pki issue-pqc server localhost

Document the approach and its limitations clearly. This is exploratory — the
important outcome is understanding where the gaps are, not a polished feature.

← Week 4: Migration Planning

Challenge Assignment

Implement PQC certificate signing in toy-pki:

  1. Generate an ML-DSA-65 key pair with aws-lc-rs
  2. Build a certificate (rcgen or manual DER) with:
    • ML-DSA subject public key in SubjectPublicKeyInfo
    • ML-DSA-65 in signatureAlgorithm
    • ML-DSA-65 signature over the TBSCertificate
  3. Verify the certificate using aws-lc-rs
  4. Parse it with x509-parser — what does it report for the signature algorithm?
  5. Try to verify with openssl — does it recognize ML-DSA-65?
    (It may not, depending on openssl version and oqs-provider availability)

Document all gaps you encounter. These gaps ARE the lesson.

← Week 4: Migration Planning

Resources

  • rcgen GitHub issues: search "ML-DSA" or "Dilithium" for current status
  • aws-lc-rs signature::ML_DSA_65: docs.rs/aws-lc-rs
  • IETF draft-ietf-lamps-dilithium-certificates: the OID definitions
  • Open Quantum Safe liboqs-based openssl: for testing PQC cert verification