Source: Tectonic Quantum Readiness Audit v1.0 — Finding TEC-01
Tracking: this issue (#411)
Verified at: commit 6ad0e54 on 2026-06-11
Status: upstream-blocked (no PQ/hybrid Noise in rust-libp2p yet) — config seam can be scaffolded now; proportionate severity Low–Medium, below the audit's Medium/High.
Vulnerable code
The libp2p transport authenticates every node-to-node connection with a Noise XX handshake whose static Diffie-Hellman keys are X25519:
node/network/src/service.rs:513-521
/// Generate authenticated XX Noise config from identity keys
fn generate_noise_config(
identity_keypair: &Keypair,
) -> noise::NoiseAuthenticated<noise::XX, noise::X25519Spec, ()> {
let static_dh_keys = noise::Keypair::<noise::X25519Spec>::new()
.into_authentic(identity_keypair)
.expect("signing can fail only once during starting a node");
noise::NoiseConfig::xx(static_dh_keys).into_authenticated()
}
This config is applied as the authentication layer of the transport upgrade in build_transport:
node/network/src/service.rs:428-432
transport
.upgrade(core::upgrade::Version::V1)
.authenticate(generate_noise_config(&local_private_key))
.multiplex(core::upgrade::SelectUpgrade::new(
yamux_config,
The node's long-term identity keypair is secp256k1 (constrained by discv5), and into_authentic signs the ephemeral X25519 static key with it — so the handshake's peer authentication is rooted in secp256k1:
node/network/src/service.rs:469-493
/// Currently only secp256k1 keys are allowed, as these are the only keys supported by discv5.
// ...
let local_private_key = Keypair::generate_secp256k1();
Dependency correction. The audit cited git commit 5e3519fb66b92c7f7c0dc744ab360fd8b669fe54 as the source of libp2p. That is wrong — this repo does not vendor libp2p from git. It pins the published crate from crates.io:
node/network/Cargo.toml:46-49
[dependencies.libp2p]
version = "0.45.1"
default-features = false
features = ["websocket", "identify", "mplex", "yamux", "noise", "gossipsub", "dns-tokio", "tcp-tokio", "plaintext", "secp256k1"]
Cargo.lock resolves this to libp2p 0.45.1 (checksum 4172…9029) and libp2p-noise 0.36.0 (checksum cf2c…15ad), both source = "registry+https://github.com/rust-lang/crates.io-index". The handshake code above is in the crates.io release, not a forked git revision.
The vulnerability
Primitive: X25519 elliptic-curve Diffie-Hellman (Curve25519, a 256-bit prime-order group) used for Noise session-key agreement, plus secp256k1 ECDSA used to authenticate the static DH key (into_authentic).
Why quantum-vulnerable: Both are discrete-log problems over elliptic-curve groups. A cryptographically relevant quantum computer (CRQC) running Shor's algorithm solves ECDLP in polynomial time, recovering the X25519 private scalar and the secp256k1 identity key. This is a full break, not a security-margin reduction; the symmetric/AEAD half of Noise (ChaCha20-Poly1305) only faces Grover, which halves the effective key strength (256→128-bit security) and remains comfortably safe — so the AEAD is not the concern here. The concern is the asymmetric handshake.
What a CRQC attacker could actually do here:
- Harvest-Now-Decrypt-Later (HNDL) on session confidentiality: record a Noise session today, later recover the X25519 ephemeral/static keys with Shor, derive the session keys, and decrypt the captured traffic.
- Live impersonation / MITM: with the secp256k1 identity key broken, forge a peer's authenticated handshake. This is a live-attack capability (requires a CRQC at attack time), not HNDL.
Honest impact assessment. The audit rated this Medium severity / High urgency on the basis of confidentiality exposure. That is overstated for this channel, for a concrete reason: the traffic carried over these Noise sessions is public-by-design storage-network data, so there is little secret confidentiality to harvest.
- Gossipsub payloads are file/chunk announcements —
NewFile, FindFile, AnnounceFile, AnnounceChunks (node/network/src/types/pubsub.rs:213-228) — i.e. public availability metadata, not user secrets.
- RPC carries chunk data that is already publicly retrievable from the storage network by design.
There is no private user content whose long-term confidentiality is meaningfully degraded by decrypting these sessions years later, so the HNDL confidentiality blast radius is low. The genuinely quantum-relevant weakness is peer authentication: an attacker with a CRQC could impersonate node identities or MITM connections. But that is a live capability gated on CRQC existence (no harvest advantage), and the protocol already disseminates the same data over a public gossip mesh with application-layer signatures on announcements.
Proportionate rating: Low–Medium severity, Low urgency. This is a real long-horizon item to track, but it is not a confidentiality emergency and the audit's High urgency is not justified for a public-data transport.
Constraint
This cannot be fixed unilaterally in this repository today. The Noise handshake and its supported DH suites are entirely defined by the libp2p-noise crate (pinned 0.36.0 via libp2p 0.45.1 from crates.io). libp2p-noise 0.36.0 offers only the classical X25519/X25519Spec suite — there is no ML-KEM, hybrid, or pqXX variant to switch to. We consume the published crate, not a vendored/modifiable copy, so we cannot add a PQ KEM ourselves without forking and maintaining the crypto crate. Additionally, the handshake must remain interoperable with every other node on the network, so any change to the suite is effectively a network-wide upgrade, not a local toggle that one node can flip.
Potential solution
Blocked on upstream — track the dependency.
-
Migration trigger to watch: hybrid post-quantum Noise (e.g. X25519 + ML-KEM-768, a pqXX-style pattern) landing in rust-libp2p. Tracking issue: libp2p/rust-libp2p#6236 ("Post-quantum / hybrid Noise handshake"). When a release ships hybrid Noise:
- Bump
libp2p (and transitively libp2p-noise) in node/network/Cargo.toml — note this is a large major-version jump from 0.45.1, so expect transport/swarm API churn beyond just Noise.
- Change the return type and body of
generate_noise_config (node/network/src/service.rs:513-521) to construct the hybrid/pqXX config instead of NoiseConfig::xx over X25519Spec.
- Benchmark: the hybrid initiator is expected to be roughly ~2x the XX handshake cost (extra KEM encapsulation + larger handshake messages); validate connect latency and CPU under churn before enabling network-wide.
-
Scaffolding that CAN be done now (low cost): introduce a config seam so the suite selection is not hard-coded. Add a network config field (e.g. noise_handshake: Classic | Hybrid) and have build_transport choose which generate_noise_config* to call. With current libp2p-noise 0.36.0 only the Classic arm compiles, but this isolates the swap point and lets a future hybrid build flip behind a flag for staged rollout/testing without touching call sites. This is the only meaningful scaffolding available; the actual PQ primitive cannot be added until upstream ships it.
-
Not recommended: forking libp2p-noise to graft in ML-KEM ourselves. Maintaining custom transport crypto for a public-data channel is disproportionate to the Low–Medium risk and creates an interop fork.
Checklist
Locations verified against source at 6ad0e54; drafted by Claude Code from the third-party Tectonic PQ audit.
Source: Tectonic Quantum Readiness Audit v1.0 — Finding TEC-01
Tracking: this issue (#411)
Verified at: commit
6ad0e54on 2026-06-11Status: upstream-blocked (no PQ/hybrid Noise in rust-libp2p yet) — config seam can be scaffolded now; proportionate severity Low–Medium, below the audit's Medium/High.
Vulnerable code
The libp2p transport authenticates every node-to-node connection with a Noise XX handshake whose static Diffie-Hellman keys are X25519:
node/network/src/service.rs:513-521
This config is applied as the authentication layer of the transport upgrade in
build_transport:node/network/src/service.rs:428-432
transport .upgrade(core::upgrade::Version::V1) .authenticate(generate_noise_config(&local_private_key)) .multiplex(core::upgrade::SelectUpgrade::new( yamux_config,The node's long-term identity keypair is secp256k1 (constrained by discv5), and
into_authenticsigns the ephemeral X25519 static key with it — so the handshake's peer authentication is rooted in secp256k1:node/network/src/service.rs:469-493
Dependency correction. The audit cited git commit
5e3519fb66b92c7f7c0dc744ab360fd8b669fe54as the source of libp2p. That is wrong — this repo does not vendor libp2p from git. It pins the published crate from crates.io:node/network/Cargo.toml:46-49
Cargo.lockresolves this tolibp2p 0.45.1(checksum4172…9029) andlibp2p-noise 0.36.0(checksumcf2c…15ad), bothsource = "registry+https://github.com/rust-lang/crates.io-index". The handshake code above is in the crates.io release, not a forked git revision.The vulnerability
Primitive: X25519 elliptic-curve Diffie-Hellman (Curve25519, a 256-bit prime-order group) used for Noise session-key agreement, plus secp256k1 ECDSA used to authenticate the static DH key (
into_authentic).Why quantum-vulnerable: Both are discrete-log problems over elliptic-curve groups. A cryptographically relevant quantum computer (CRQC) running Shor's algorithm solves ECDLP in polynomial time, recovering the X25519 private scalar and the secp256k1 identity key. This is a full break, not a security-margin reduction; the symmetric/AEAD half of Noise (ChaCha20-Poly1305) only faces Grover, which halves the effective key strength (256→128-bit security) and remains comfortably safe — so the AEAD is not the concern here. The concern is the asymmetric handshake.
What a CRQC attacker could actually do here:
Honest impact assessment. The audit rated this Medium severity / High urgency on the basis of confidentiality exposure. That is overstated for this channel, for a concrete reason: the traffic carried over these Noise sessions is public-by-design storage-network data, so there is little secret confidentiality to harvest.
NewFile,FindFile,AnnounceFile,AnnounceChunks(node/network/src/types/pubsub.rs:213-228) — i.e. public availability metadata, not user secrets.There is no private user content whose long-term confidentiality is meaningfully degraded by decrypting these sessions years later, so the HNDL confidentiality blast radius is low. The genuinely quantum-relevant weakness is peer authentication: an attacker with a CRQC could impersonate node identities or MITM connections. But that is a live capability gated on CRQC existence (no harvest advantage), and the protocol already disseminates the same data over a public gossip mesh with application-layer signatures on announcements.
Proportionate rating: Low–Medium severity, Low urgency. This is a real long-horizon item to track, but it is not a confidentiality emergency and the audit's High urgency is not justified for a public-data transport.
Constraint
This cannot be fixed unilaterally in this repository today. The Noise handshake and its supported DH suites are entirely defined by the
libp2p-noisecrate (pinned0.36.0vialibp2p 0.45.1from crates.io).libp2p-noise 0.36.0offers only the classicalX25519/X25519Specsuite — there is no ML-KEM, hybrid, orpqXXvariant to switch to. We consume the published crate, not a vendored/modifiable copy, so we cannot add a PQ KEM ourselves without forking and maintaining the crypto crate. Additionally, the handshake must remain interoperable with every other node on the network, so any change to the suite is effectively a network-wide upgrade, not a local toggle that one node can flip.Potential solution
Blocked on upstream — track the dependency.
Migration trigger to watch: hybrid post-quantum Noise (e.g.
X25519 + ML-KEM-768, apqXX-style pattern) landing in rust-libp2p. Tracking issue: libp2p/rust-libp2p#6236 ("Post-quantum / hybrid Noise handshake"). When a release ships hybrid Noise:libp2p(and transitivelylibp2p-noise) in node/network/Cargo.toml — note this is a large major-version jump from0.45.1, so expect transport/swarm API churn beyond just Noise.generate_noise_config(node/network/src/service.rs:513-521) to construct the hybrid/pqXXconfig instead ofNoiseConfig::xxoverX25519Spec.Scaffolding that CAN be done now (low cost): introduce a config seam so the suite selection is not hard-coded. Add a network config field (e.g.
noise_handshake: Classic | Hybrid) and havebuild_transportchoose whichgenerate_noise_config*to call. With currentlibp2p-noise 0.36.0only theClassicarm compiles, but this isolates the swap point and lets a future hybrid build flip behind a flag for staged rollout/testing without touching call sites. This is the only meaningful scaffolding available; the actual PQ primitive cannot be added until upstream ships it.Not recommended: forking
libp2p-noiseto graft in ML-KEM ourselves. Maintaining custom transport crypto for a public-data channel is disproportionate to the Low–Medium risk and creates an interop fork.Checklist
0.45.1/ libp2p-noise0.36.0, not git rev5e3519fb….noise_handshakeconfig seam in network config +build_transportso the suite is selectable rather than hard-coded.generate_noise_configto the hybrid variant, and benchmark handshake cost (~2x) before a coordinated network upgrade.Locations verified against source at 6ad0e54; drafted by Claude Code from the third-party Tectonic PQ audit.