From d259de68c010d81916aa495892841dd5bc405303 Mon Sep 17 00:00:00 2001 From: dozyio Date: Sat, 11 Apr 2026 22:19:18 +0100 Subject: [PATCH 1/5] docs: RFC ml-dsa --- RFC/0004-mldsa-peer-ids.md | 140 +++++++++++++++++++++++++++++++++++++ peer-ids/peer-ids.md | 39 ++++++++++- 2 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 RFC/0004-mldsa-peer-ids.md diff --git a/RFC/0004-mldsa-peer-ids.md b/RFC/0004-mldsa-peer-ids.md new file mode 100644 index 000000000..2305788ec --- /dev/null +++ b/RFC/0004-mldsa-peer-ids.md @@ -0,0 +1,140 @@ +- Start Date: 2026-04-11 +- Related PRs: [js-libp2p/pull/3432](https://github.com/libp2p/js-libp2p/pull/3432) +- Related Spec: [peer-ids/peer-ids.md](../peer-ids/peer-ids.md) + +# RFC 0004: ML-DSA Key Support for libp2p Peer Identities + +## Abstract + +This RFC proposes adding support for post-quantum ML-DSA identity keys to +libp2p. + +It defines a new `KeyType` value for ML-DSA in libp2p key protobufs, a wire +format for serializing ML-DSA public/private keys, and how peer IDs are derived +from ML-DSA public keys. + +## Motivation + +libp2p peer identities are currently based on classical signature schemes (RSA, +Ed25519, secp256k1, ECDSA). + +To prepare libp2p identity and signature systems for post-quantum migration, we +need an interoperable encoding and verification story for ML-DSA keys. + +Without a shared specification: + +- Implementations may choose incompatible key encodings. +- Cross-implementation interoperability is not guaranteed. +- Future migration to hybrid or fully post-quantum deployments becomes harder. + +## Design + +### 1. `KeyType` extension + +The key protobuf enum in the peer-id spec is extended with: + +```protobuf +enum KeyType { + RSA = 0; + Ed25519 = 1; + Secp256k1 = 2; + ECDSA = 3; + MLDSA = 4; +} +``` + +### 2. ML-DSA parameter sets + +This RFC supports the three standardized ML-DSA parameter sets: + +- `ML-DSA-44` +- `ML-DSA-65` +- `ML-DSA-87` + +### 3. Key serialization in protobuf `Data` + +For `PublicKey` and `PrivateKey` messages where `Type = MLDSA`, the `Data` +field is: + +``` + +``` + +Where `variant-prefix` is one byte: + +- `0x01` = `ML-DSA-44` +- `0x02` = `ML-DSA-65` +- `0x03` = `ML-DSA-87` + +Raw key lengths are the standard ML-DSA key sizes for each variant: + +- `ML-DSA-44`: public key `1312` bytes, private key `2560` bytes +- `ML-DSA-65`: public key `1952` bytes, private key `4032` bytes +- `ML-DSA-87`: public key `2592` bytes, private key `4896` bytes + +Implementations MUST reject malformed key payloads (unknown prefix, +missing prefix, or length mismatch). + +### 4. Signature semantics + +ML-DSA signatures are generated and verified using the standard ML-DSA +algorithm for the corresponding parameter set. + +- Input to signing is the exact message bytes. +- No additional pre-hash step is applied by libp2p at the key API boundary. +- Verification is performed over the same message bytes with the same parameter + set. + +### 5. Peer ID derivation + +Peer ID derivation remains unchanged: + +1. Protobuf-encode `PublicKey` with `Type = MLDSA` and `Data` as specified + above. +2. Compute peer ID multihash according to the existing rule: + - identity multihash if encoded key is `<= 42` bytes + - SHA-256 multihash otherwise + +Because all ML-DSA public key encodings are much larger than 42 bytes, ML-DSA +peer IDs always use SHA-256 multihash. + +## Backward Compatibility + +- Existing peers and key types are unaffected. +- Implementations that do not support `KeyType = MLDSA` will reject those keys + as unsupported. +- Text and CID peer ID formats are unchanged. + +## Security Considerations + +- This RFC only defines identity key representation and signature verification + semantics. +- It does not by itself provide hybrid authentication or downgrade resistance + between classical and post-quantum identities. +- Deployments should continue evaluating algorithm maturity, implementation + quality, and runtime support in their target environments. + +## Implementation Status (Non-Normative) + +Current language support for ML-DSA is still evolving: + +- Node.js runtime support is experimental. +- Browser WebCrypto support is unavailable. +- Go has an internal implementation for ML-DSA but no public API as of Go 1.26. + +This RFC specifies interoperability behavior independent of implementation +maturity. +Production deployments SHOULD evaluate runtime support, performance, and audit +status before enabling ML-DSA identities. + +## Open Questions + +The following questions should be resolved before promoting this to a candidate +recommendation: + +1. Default variant selection for new key generation (`ML-DSA-44`, `-65`, or + `-87`). +2. Whether any libp2p subsystem should require dual-signature or hybrid + identity strategies. +3. Whether to define mandatory test vectors in the peer-id spec for ML-DSA + encodings. diff --git a/peer-ids/peer-ids.md b/peer-ids/peer-ids.md index 4daf38c44..d5c91bd5a 100644 --- a/peer-ids/peer-ids.md +++ b/peer-ids/peer-ids.md @@ -28,6 +28,7 @@ about maturity level and spec status. - [Ed25519](#ed25519) - [Secp256k1](#secp256k1) - [ECDSA](#ecdsa) + - [ML-DSA](#ml-dsa) - [Peer Ids](#peer-ids) - [String representation](#string-representation) - [Encoding](#encoding) @@ -63,6 +64,7 @@ enum KeyType { Ed25519 = 1; Secp256k1 = 2; ECDSA = 3; + MLDSA = 4; } message PublicKey { @@ -110,16 +112,17 @@ The second is for generating peer ids; this is discussed in the section below. ### Key Types -Four key types are supported: +Five key types are supported: - RSA - Ed25519 - Secp256k1 - ECDSA + - ML-DSA Implementations MUST support Ed25519. Implementations SHOULD support RSA if they wish to interoperate with the mainline IPFS DHT and the default IPFS bootstrap nodes. Implementations MAY -support Secp256k1 and ECDSA, but nodes using those keys may not be able to connect to all other -nodes. +support Secp256k1, ECDSA, and ML-DSA, but nodes using those keys may not be able to connect to all +other nodes. In all cases, implementation MAY allow the user to enable/disable specific key types via configuration. Note that disabling support for compulsory key types @@ -185,6 +188,33 @@ To sign a message, we hash the message with SHA 256, and then sign it with the [ECDSA standard algorithm](https://tools.ietf.org/html/rfc6979), then we encode it using [DER-encoded ASN.1.](https://wiki.openssl.org/index.php/DER) +#### ML-DSA + +For `MLDSA` keys, the serialized `Data` field includes a one-byte variant prefix +followed by raw key bytes: + +``` + +``` + +Variant prefixes are: + +- `0x01`: ML-DSA-44 +- `0x02`: ML-DSA-65 +- `0x03`: ML-DSA-87 + +Public/private key lengths are the standard lengths for each variant: + +- ML-DSA-44: public key `1312` bytes, private key `2560` bytes +- ML-DSA-65: public key `1952` bytes, private key `4032` bytes +- ML-DSA-87: public key `2592` bytes, private key `4896` bytes + +Implementations MUST reject malformed key encodings (unknown variant prefix, +missing prefix, or key length mismatch). + +ML-DSA signatures follow the normal ML-DSA standard. Signatures are generated +and verified over the exact message bytes. + ### Test vectors The following test vectors are hex-encoded bytes of the above described protobuf encoding. @@ -221,6 +251,9 @@ Specifically, to compute a peer ID of a key: 5. If the length is greater than 42, then hash it using the SHA256 multihash. +Because ML-DSA public keys are larger than 42 bytes, ML-DSA peer IDs always use +the SHA256 multihash. + ### String representation There are two ways to represent peer IDs in text: as a raw From f794fcde2b7c33152cc820642bb3749785702ef9 Mon Sep 17 00:00:00 2001 From: dozyio <37986489+dozyio@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:26:31 +0100 Subject: [PATCH 2/5] Update RFC/0004-mldsa-peer-ids.md add expanded to private key size Co-authored-by: Marcin Rataj --- RFC/0004-mldsa-peer-ids.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RFC/0004-mldsa-peer-ids.md b/RFC/0004-mldsa-peer-ids.md index 2305788ec..2570ceb3f 100644 --- a/RFC/0004-mldsa-peer-ids.md +++ b/RFC/0004-mldsa-peer-ids.md @@ -68,9 +68,9 @@ Where `variant-prefix` is one byte: Raw key lengths are the standard ML-DSA key sizes for each variant: -- `ML-DSA-44`: public key `1312` bytes, private key `2560` bytes -- `ML-DSA-65`: public key `1952` bytes, private key `4032` bytes -- `ML-DSA-87`: public key `2592` bytes, private key `4896` bytes +- `ML-DSA-44`: public key `1312` bytes, expanded private key `2560` bytes +- `ML-DSA-65`: public key `1952` bytes, expanded private key `4032` bytes +- `ML-DSA-87`: public key `2592` bytes, expanded private key `4896` bytes Implementations MUST reject malformed key payloads (unknown prefix, missing prefix, or length mismatch). From 035eaaca939e83bc519e9190c3faf41c2f9db71d Mon Sep 17 00:00:00 2001 From: dozyio <37986489+dozyio@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:10:13 +0100 Subject: [PATCH 3/5] Update RFC/0004-mldsa-peer-ids.md Co-authored-by: Marcin Rataj --- RFC/0004-mldsa-peer-ids.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RFC/0004-mldsa-peer-ids.md b/RFC/0004-mldsa-peer-ids.md index 2570ceb3f..f49c83cf5 100644 --- a/RFC/0004-mldsa-peer-ids.md +++ b/RFC/0004-mldsa-peer-ids.md @@ -138,3 +138,4 @@ recommendation: identity strategies. 3. Whether to define mandatory test vectors in the peer-id spec for ML-DSA encodings. +4. Canonical private key format: should it be FIPS 204 expanded form ([multiformats/multicodec#399](https://github.com/multiformats/multicodec/pull/399), W3C DI Quantum-Safe Cryptosuite)? From bcf1dba0e31dc33e77317f0cd4989aab778802e9 Mon Sep 17 00:00:00 2001 From: dozyio <37986489+dozyio@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:10:34 +0100 Subject: [PATCH 4/5] Update RFC/0004-mldsa-peer-ids.md Co-authored-by: Marcin Rataj --- RFC/0004-mldsa-peer-ids.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RFC/0004-mldsa-peer-ids.md b/RFC/0004-mldsa-peer-ids.md index f49c83cf5..597677d4d 100644 --- a/RFC/0004-mldsa-peer-ids.md +++ b/RFC/0004-mldsa-peer-ids.md @@ -121,6 +121,7 @@ Current language support for ML-DSA is still evolving: - Node.js runtime support is experimental. - Browser WebCrypto support is unavailable. - Go has an internal implementation for ML-DSA but no public API as of Go 1.26. + - Worth noting: `crypto/mlkem` went public in Go 1.24, so `crypto/mldsa` going public soon is likely. For now, `github.com/cloudflare/circl/sign/mldsa` is available. This RFC specifies interoperability behavior independent of implementation maturity. From a917466c636e50da576bfa3c3ece66be1295f7cf Mon Sep 17 00:00:00 2001 From: dozyio Date: Wed, 15 Apr 2026 20:33:04 +0100 Subject: [PATCH 5/5] docs: clarify ML-DSA signature sizes, add references, normative semantics --- RFC/0004-mldsa-peer-ids.md | 42 +++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/RFC/0004-mldsa-peer-ids.md b/RFC/0004-mldsa-peer-ids.md index 597677d4d..aa8da84c2 100644 --- a/RFC/0004-mldsa-peer-ids.md +++ b/RFC/0004-mldsa-peer-ids.md @@ -51,6 +51,8 @@ This RFC supports the three standardized ML-DSA parameter sets: - `ML-DSA-65` - `ML-DSA-87` +Parameter names and sizes are defined by [FIPS 204](#references). + ### 3. Key serialization in protobuf `Data` For `PublicKey` and `PrivateKey` messages where `Type = MLDSA`, the `Data` @@ -66,24 +68,27 @@ Where `variant-prefix` is one byte: - `0x02` = `ML-DSA-65` - `0x03` = `ML-DSA-87` -Raw key lengths are the standard ML-DSA key sizes for each variant: +Raw key and signature lengths are fixed per ML-DSA variant: -- `ML-DSA-44`: public key `1312` bytes, expanded private key `2560` bytes -- `ML-DSA-65`: public key `1952` bytes, expanded private key `4032` bytes -- `ML-DSA-87`: public key `2592` bytes, expanded private key `4896` bytes +| Variant | Public key (bytes) | Expanded private key (bytes) | Signature (bytes) | +| --- | ---: | ---: | ---: | +| `ML-DSA-44` | 1312 | 2560 | 2420 | +| `ML-DSA-65` | 1952 | 4032 | 3309 | +| `ML-DSA-87` | 2592 | 4896 | 4627 | -Implementations MUST reject malformed key payloads (unknown prefix, -missing prefix, or length mismatch). +Implementations MUST reject malformed key payloads, including unknown +`variant-prefix`, missing prefix, or length mismatch against the table above. ### 4. Signature semantics ML-DSA signatures are generated and verified using the standard ML-DSA algorithm for the corresponding parameter set. -- Input to signing is the exact message bytes. -- No additional pre-hash step is applied by libp2p at the key API boundary. -- Verification is performed over the same message bytes with the same parameter - set. +- Implementations MUST sign the exact message bytes. +- Implementations MUST NOT apply an additional pre-hash at the libp2p key API + boundary. +- Implementations MUST verify signatures over the exact same message bytes and + ML-DSA parameter set. ### 5. Peer ID derivation @@ -121,7 +126,9 @@ Current language support for ML-DSA is still evolving: - Node.js runtime support is experimental. - Browser WebCrypto support is unavailable. - Go has an internal implementation for ML-DSA but no public API as of Go 1.26. - - Worth noting: `crypto/mlkem` went public in Go 1.24, so `crypto/mldsa` going public soon is likely. For now, `github.com/cloudflare/circl/sign/mldsa` is available. + - Worth noting: `crypto/mlkem` went public in Go 1.24, `crypto/mldsa` is in + proposal phase [golang/go#77626](https://github.com/golang/go/issues/77626). + For now, `github.com/cloudflare/circl/sign/mldsa` is available. This RFC specifies interoperability behavior independent of implementation maturity. @@ -136,7 +143,14 @@ recommendation: 1. Default variant selection for new key generation (`ML-DSA-44`, `-65`, or `-87`). 2. Whether any libp2p subsystem should require dual-signature or hybrid - identity strategies. + identity strategies. 3. Whether to define mandatory test vectors in the peer-id spec for ML-DSA - encodings. -4. Canonical private key format: should it be FIPS 204 expanded form ([multiformats/multicodec#399](https://github.com/multiformats/multicodec/pull/399), W3C DI Quantum-Safe Cryptosuite)? + encodings. +4. Canonical private key format: should it be FIPS 204 expanded form + [multiformats/multicodec#399](https://github.com/multiformats/multicodec/pull/399), + W3C DI Quantum-Safe Cryptosuite)? + +## References + +- [FIPS 204] NIST, *Module-Lattice-Based Digital Signature Standard* (ML-DSA), + https://csrc.nist.gov/pubs/fips/204/final