Skip to content

PQC PGP keys: composite ML-KEM-768 + ML-DSA-65 in RSA slots [UNTESTED]#31

Open
0c-coder wants to merge 15 commits into
trustcrypto:masterfrom
0c-coder:feature/pqc-pgp-slots
Open

PQC PGP keys: composite ML-KEM-768 + ML-DSA-65 in RSA slots [UNTESTED]#31
0c-coder wants to merge 15 commits into
trustcrypto:masterfrom
0c-coder:feature/pqc-pgp-slots

Conversation

@0c-coder

@0c-coder 0c-coder commented Jul 6, 2026

Copy link
Copy Markdown

Post-quantum PGP keys on OnlyKey — UNTESTED, by inspection

Adds composite ML-KEM-768 + ML-DSA-65 PGP keys, imported and used on-device the way RSA keys are. One RSA slot (1-4) holds one composite key as a 160-byte seed blob:

[0:32]   Ed25519 secret     (sign, ecc half)
[32:64]  ML-DSA-65 seed      (sign, pqc half)      FIPS 204 32-byte seed
[64:96]  X25519 secret       (decrypt, ecc half)
[96:160] ML-KEM-768 seed     (decrypt, pqc half)   FIPS 203 64-byte seed (d||z)

Seeds expand on-device at use time; the public key is never stored. Sign/decrypt each pick a half via a component selector byte. openpgp.js does the KMAC combine / signature concatenation host-side.

What's here

  • onlykey/okpqc.{h,cpp} — the composite ops (okpqc_sign / okpqc_decrypt) + inline Ed25519/Curve25519 wrappers (Arduino Crypto lib).
  • mlkem_native/ — ML-KEM-768 (reused from feature/mlkem-768).
  • mldsa_native/ — ML-DSA-65, configured for OnlyKey: MLD_CONFIG_PARAMETER_SET=65, MLD_CONFIG_REDUCE_RAM (sign ~17 KB vs ~69 KB — required to fit the MK20DX256's 64 KB RAM), custom RNG bridge, asm backends stripped (pure C for Cortex-M4).
  • onlykey/okcore.{cpp,h}, onlykey/okcrypto.cpp — 18-line integration: KEYTYPE_PQC_PGP (7), 160-byte load/flashget, sign/decrypt dispatch.
  • onlykey/DESIGN.md, onlykey/BUILD.md, onlykey/firmware-edits.patch.

Verified

Compiles + links + runs on host (the crypto core): ML-KEM-768 decaps round-trips, ML-DSA-65 signs (3309 B) and verifies, on the exact config.

Not yet validated on hardware

  • Teensy/Cortex-M4 build (no cross-compiler here) — check the flash budget (256 KB total; firmware is already large).
  • ML-DSA message framing ([0,0]||digest) vs openpgp.js ml_dsa.js.
  • Full round-trip: import a 160-byte composite key -> sign/verify + encrypt/decrypt via openpgp.js.

Companion host-side work: openpgp.js hardware hooks (0c-coder/openpgpjs onlykey-pqc-hooks) and the python-onlykey type=7 loader (to follow).

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.

1 participant