Skip to content

Lab-06: PQC Code Signing

Key Message: Software signatures must remain valid for years. PQC ensures they can’t be forged by future quantum computers.


“We sign our software releases. How long do those signatures need to be valid? And what happens when quantum computers can forge classical signatures?”

Code signatures are long-lived. A signed binary from 2024 might still be verified in 2034. If quantum computers can forge ECDSA signatures by then, attackers could create malicious software that appears legitimately signed.

TODAY FUTURE (5-15 years?)
───── ────────────────────
You sign software Attacker with quantum computer
│ │
│ ECDSA signature │ Breaks ECDSA
│ │
▼ ▼
Signed binary ────────────────► Forges valid signature
(still in use) on malicious binary
"Legitimate" malware
passes verification

WITHOUT CODE SIGNING
────────────────────
Developer Attacker Client
│ │ │
│ firmware.bin │ │
│ ────────────────────────────┼──────────────────────────►│
│ │ │
│ │ firmware.bin (modified) │
│ │ ──────────────────────► │
│ │ │
│ │ ▼
│ │ ┌──────────────┐
│ │ │ Which one │
│ │ │ is real? │
│ │ └──────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ │
│ SUPPLY CHAIN ATTACK: Modify code in transit │
│ │
│ │
│ Developer │
│ │ │
│ │ firmware.bin (original) │
│ ▼ │
│ ┌──────────┐ │
│ │ Mirror │ ◄──── Attacker injects malware │
│ │ Server │ │
│ └──────────┘ │
│ │ │
│ │ firmware.bin (compromised) │
│ ▼ │
│ ┌──────────┐ │
│ │ Client │ Installs the firmware │
│ │ │ without knowing it's │
│ │ │ been modified │
│ └──────────┘ │
│ │
│ Real-world examples: │
│ - SolarWinds (2020): malware injected in an update │
│ - CodeCov (2021): build script compromised │
│ - 3CX (2023): supply chain of a supply chain │
│ │
└──────────────────────────────────────────────────────────────────┘

Sign the code BEFORE distributing it:

┌──────────────────────────────────────────────────────────────────┐
│ │
│ WITH CODE SIGNING │
│ │
│ Developer │
│ │ │
│ │ 1. Signs firmware.bin with ML-DSA │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ firmware.bin │ │
│ │ + firmware.bin.p7s (signature) │ │
│ │ + signing.crt (certificate) │ │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Client │ 2. Verifies the signature │
│ │ │ │
│ │ │ ✓ Hash matches │
│ │ │ ✓ Signature valid │
│ │ │ ✓ Certificate in the chain │
│ │ │ │
│ │ │ → Installation authorized │
│ └──────────┘ │
│ │
│ If the firmware is modified: │
│ ❌ Hash doesn't match → Signature invalid → REJECTED │
│ │
└──────────────────────────────────────────────────────────────────┘

PropertyMeaning
IntegrityThe file hasn’t been modified
AuthenticityIt really comes from the publisher
Non-repudiationThe publisher can’t deny signing it

  1. Create a Code Signing CA (ML-DSA-65) 1b. Issue a code signing certificate
  2. Sign a firmware binary (CMS/PKCS#7) 2b. Verify the signature (VALID)
  3. Tamper with the binary and verify again (INVALID)

Terminal window
./journey/06-code-signing/demo.sh

Terminal window
# Create a PQC CA for code signing
qpki ca init --profile profiles/pqc-ca.yaml \
--var cn="Code Signing CA" \
--ca-dir output/code-ca
qpki ca export --ca-dir output/code-ca --out output/code-ca/ca.crt
Terminal window
# Generate key and CSR
qpki csr gen --algorithm ml-dsa-65 \
--keyout output/code-signing.key \
--cn "ACME Software" \
--out output/code-signing.csr
qpki cert issue --ca-dir output/code-ca \
--profile profiles/pqc-code-signing.yaml \
--csr output/code-signing.csr \
--out output/code-signing.crt
Terminal window
# Create a test firmware
dd if=/dev/urandom of=output/firmware.bin bs=1024 count=100
qpki cms sign --data output/firmware.bin \
--cert output/code-signing.crt \
--key output/code-signing.key \
--out output/firmware.p7s
Terminal window
# Verify: 1) hash matches 2) signature valid 3) cert chain trusted
qpki cms verify output/firmware.p7s \
--data output/firmware.bin \
--ca output/code-ca/ca.crt
# Result: VALID
Terminal window
# Any modification changes the hash → signature becomes invalid
echo "MALWARE" >> output/firmware.bin
qpki cms verify output/firmware.p7s \
--data output/firmware.bin \
--ca output/code-ca/ca.crt
# Result: INVALID - hash mismatch

Software TypeTypical LifespanPQC Urgency
IoT firmware10-20 yearsCritical
Industrial control15-30 yearsCritical
Medical devices10-15 yearsCritical
Desktop software5-10 yearsHigh
Mobile apps2-5 yearsMedium
  1. Firmware tampering: Attacker forges signature on malicious firmware update
  2. Supply chain attack: Malicious package appears legitimately signed
  3. Historical verification: Old signatures become untrustworthy

ComponentClassical (ECDSA P-384)Post-Quantum (ML-DSA-65)Notes
Public key~97 bytes~1,952 bytesIn certificate
Signature~96 bytes~3,309 bytesPer signed file
Certificate~1 KB~6 KBOne-time distribution

For a 100 MB binary, the signature overhead is negligible.


Code signing certificates have specific extensions:

ExtensionValuePurpose
Extended Key UsagecodeSigningLimits certificate use
Key UsagedigitalSignatureSigning operations only
Basic ConstraintsCA: falseEnd-entity certificate

ScenarioRecommendation
IoT/embedded firmwareNow - Long device lifespan
Critical infrastructureNow - High-value targets
Enterprise softwarePlan for 2025-2026
Consumer appsCan wait, but plan ahead

  1. Long-lived signatures: Code signatures may be verified for 10+ years
  2. Quantum threat: Future quantum computers could forge classical signatures
  3. PQC solution: ML-DSA signatures remain unforgeable
  4. Size trade-off: ~3 KB signature vs ~100 bytes (negligible for binaries)
  5. Drop-in replacement: Same workflow, different algorithm


You’ve proven WHO signed the code and that it hasn’t been tampered with.

But WHEN was it signed? If the certificate expires, how do you prove the signature existed before expiration?

PQC OCSP | QLAB Home | Next: Timestamping → — Prove WHEN documents were signed.