From d8e696235008f1bbfcdb0f93909a74558c3d2440 Mon Sep 17 00:00:00 2001 From: zzzckck <152148891+zzzckck@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:12:14 +0800 Subject: [PATCH] BEP-708: Post-Quantum Hybrid Key Exchange for the BSC P2P Transport --- BEPs/BEP-708.md | 467 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+) create mode 100644 BEPs/BEP-708.md diff --git a/BEPs/BEP-708.md b/BEPs/BEP-708.md new file mode 100644 index 00000000..4ebca5df --- /dev/null +++ b/BEPs/BEP-708.md @@ -0,0 +1,467 @@ +``` +BEP: +Title: Post-Quantum Hybrid Key Exchange for the BSC P2P Transport (RLPx v5) +Status: Draft +Type: Standards +Created: 2026-08-14 +Description: Upgrade the RLPx handshake to a hybrid X25519 + ML-KEM-768 key exchange so that BSC peer-to-peer sessions resist "harvest-now, decrypt-later" attacks by a future cryptographically relevant quantum computer. +``` + +## Abstract + +This BEP specifies **RLPx handshake version 5**, a backward-compatible upgrade to the BNB Smart Chain (BSC) peer-to-peer transport that replaces the purely classical, secp256k1-based session key agreement with a **hybrid post-quantum key encapsulation**: `X25519MLKEM768` (X25519 ECDH combined with ML-KEM-768 as standardised in NIST FIPS 203). + +Concretely, this BEP: + +1. Adds two fields (`kem-group`, `kem-share`) to the RLPx `auth` and `ack` messages, using the EIP-8 forward-compatible encoding so that unupgraded peers keep working. +2. Replaces the v4 ad-hoc Keccak key-derivation chain with an HKDF-SHA256 key schedule whose input keying material is the concatenation of the classical and post-quantum shared secrets, bound to a hash of the full handshake transcript. +3. Defines optimistic negotiation, graceful fallback to v4, and an explicit downgrade-detection mechanism. +4. Defines a staged mainnet rollout that requires **no hard fork**, because the RLPx transport is not consensus-critical. + +Session *authentication* remains classical (secp256k1) in this BEP. Migrating account and node identity signatures to post-quantum algorithms is deferred to Phase 2 of the BNB Chain post-quantum roadmap; pairing-based cryptography (BLS aggregation, KZG, BN254 precompiles) is deferred to Phase 3. + +## Motivation + +### The threat is retroactive, not prospective + +BSC's RLPx transport derives every session key from an ephemeral secp256k1 ECDH. Shor's algorithm solves the elliptic-curve discrete logarithm problem in polynomial time. An adversary with a cryptographically relevant quantum computer (CRQC) can therefore recover the session key of **any RLPx connection whose ciphertext it recorded**, at any point in the future. + +This is the "harvest-now, decrypt-later" (HNDL) problem, and it is the only quantum threat that is *already accruing damage today*. Every other quantum risk to BSC — forged transaction signatures, forged validator votes, forged BLS aggregates — requires the CRQC to exist *at the moment of the attack*. Key exchange does not. Traffic captured in 2026 can be decrypted in 2036. + +This asymmetry is why the P2P key-exchange upgrade is **Phase 1** of the roadmap and is scheduled ahead of the account-signature (Phase 2) and pairing-based (Phase 3) work, even though those phases protect assets of higher nominal value. + +### What is actually at risk in BSC P2P traffic + +RLPx traffic is not "public data in transit". Decrypting a recorded BSC session reveals: + +| Exposure | Impact | +| --- | --- | +| Pre-inclusion transaction propagation | Reconstruction of historical mempool ordering; retrospective MEV and front-running forensics; deanonymisation of transaction origin by first-relay analysis | +| Validator ↔ sentry topology | Historical mapping of validator nodes behind sentries, enabling targeted analysis of past liveness incidents and informing future eclipse/DoS strategy | +| `NewBlock` / `NewBlockHashes` timing | Fine-grained propagation graph of the validator set | +| Node identity linkage | Correlation of a node's enode identity with IP addresses over time | +| Private-order-flow channels | Any bilateral relay arrangement carried over devp2p subprotocols | + +None of this is catastrophic in isolation. In aggregate it is a permanent, retroactive deanonymisation and market-microstructure leak against a chain that produces sub-second blocks and hosts a large MEV economy. + +### Why now + +* **The primitives are standardised.** FIPS 203 (ML-KEM) is final. `X25519MLKEM768` is the de-facto hybrid deployed by default in Chrome, Firefox, Cloudflare, and AWS, and is specified in `draft-ietf-tls-ecdhe-mlkem`. +* **The implementation cost is near zero for BSC.** Go 1.24 ships `crypto/mlkem` (ML-KEM-768 and ML-KEM-1024) and `crypto/ecdh` (X25519) in the standard library, both FIPS-140-3 track, both pure Go, no cgo. `bsc` requires no new third-party cryptographic dependency. +* **It is not a hard fork.** RLPx is a transport concern. This change can be shipped, defaulted on, and eventually made mandatory purely through client releases and configuration, with no state-transition or consensus impact. +* **Handshakes are rare.** The cost is paid once per TCP connection, not per message. A BSC full node performs on the order of 10²–10³ handshakes per day. + +### Non-goals + +* This BEP does **not** make RLPx authentication post-quantum. An adversary who already possesses a CRQC can impersonate a node in real time by breaking secp256k1. That is Phase 2's problem, and it is not retroactive. +* This BEP does **not** change discv4/discv5 node discovery, ENR signing, the BSC consensus engine, block or transaction formats, the EVM, or any precompile. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHOULD", "MAY" are to be interpreted as described in RFC 2119. + +### 1. Notation and constants + +| Symbol | Definition | +| --- | --- | +| `keccak256(x)` | Keccak-256 as already used by RLPx | +| `SHA256` | SHA-256, FIPS 180-4 | +| `HKDF-Extract(salt, ikm)` | RFC 5869 extract, instantiated with HMAC-SHA256 | +| `HKDF-Expand(prk, info, L)` | RFC 5869 expand, instantiated with HMAC-SHA256 | +| `\|\|` | byte-string concatenation | +| `ecdh.agree(sk, pk)` | secp256k1 ECDH, returning the 32-byte X coordinate (unchanged from v4) | +| `X25519(sk, pk)` | RFC 7748 X25519, returning 32 bytes | +| `MLKEM768.KeyGen()` | FIPS 203 ML-KEM-768 key generation → `(ek, dk)` | +| `MLKEM768.Encaps(ek)` | → `(ct, ss)` | +| `MLKEM768.Decaps(dk, ct)` | → `ss` | + +ML-KEM-768 sizes (FIPS 203): + +| Object | Size | +| --- | --- | +| Encapsulation key `ek` | 1184 bytes | +| Ciphertext `ct` | 1088 bytes | +| Shared secret `ss` | 32 bytes | +| Decapsulation key `dk` (expanded) | 2400 bytes | +| Decapsulation key seed | 64 bytes | + +### 2. Hybrid group registry + +A `kem-group` is a 16-bit unsigned integer identifying a hybrid key-agreement suite. Values are chosen to match the corresponding IANA TLS `NamedGroup` codepoints so that no separate registry discipline is required. + +| `kem-group` | Name | Initiator share | Responder share | Status | +| --- | --- | --- | --- | --- | +| `0x11EC` (4588) | `X25519MLKEM768` | 1216 bytes | 1120 bytes | **REQUIRED** | +| `0x11EB` (4587) | `SecP256r1MLKEM768` | 1249 bytes | 1153 bytes | reserved, OPTIONAL | +| `0x11ED` (4589) | `SecP384r1MLKEM1024` | 1665 bytes | 1665 bytes | reserved, OPTIONAL | + +Implementations of this BEP **MUST** support `X25519MLKEM768`. All other values are reserved for future BEPs; a node receiving an unrecognised `kem-group` **MUST** treat the handshake as if no `kem-group` had been offered (see §6). + +For `X25519MLKEM768`: + +``` +kem-init-share = mlkem768-ek (1184) || x25519-pub (32) // 1216 bytes +kem-resp-share = mlkem768-ct (1088) || x25519-pub (32) // 1120 bytes +pq-shared = mlkem768-ss (32) || x25519-ss (32) // 64 bytes +``` + +The ML-KEM part precedes the ECDH part in all three cases. This matches `draft-ietf-tls-ecdhe-mlkem`, which reverses the usual hybrid naming order for `X25519MLKEM768` specifically, and it allows byte-for-byte reuse of existing test vectors and implementations. Note that the reserved `SecP256r1*` / `SecP384r1*` groups use the opposite order (ECDH share first) in that draft; a future BEP enabling them **MUST** restate their layout explicitly rather than generalising from this section. + +### 3. Wire format + +The RLPx handshake framing (2-byte big-endian size prefix, ECIES envelope, EIP-8 random padding) is **unchanged**. Only the RLP body and the version number change. + +#### 3.1 `auth` (initiator → recipient) + +``` +auth = auth-size || enc-auth-body +auth-size = size of enc-auth-body, big-endian uint16 +auth-vsn = 5 +auth-body = [ sig, initiator-pubk, initiator-nonce, auth-vsn, + kem-group, kem-init-share, ... ] +enc-auth-body = ecies.encrypt(recipient-pubk, auth-body, auth-size) +``` + +* `sig`, `initiator-pubk`, `initiator-nonce` are **unchanged** from v4. In particular `sig = sign(ephemeral-privk, static-shared-secret ^ initiator-nonce)` over secp256k1, and the initiator's secp256k1 ephemeral public key is still recovered from `sig`. +* `kem-group` is RLP-encoded as an integer (3 bytes on the wire for `0x11EC`). +* `kem-init-share` is RLP-encoded as a byte string of exactly the length mandated for the group. +* The initiator **MUST** generate a fresh `(ek, dk)` ML-KEM key pair and a fresh X25519 key pair for every handshake attempt. Neither may be cached or reused across connections. + +#### 3.2 `ack` (recipient → initiator) + +``` +ack = ack-size || enc-ack-body +ack-size = size of enc-ack-body, big-endian uint16 +ack-vsn = 5 +ack-body = [ recipient-ephemeral-pubk, recipient-nonce, ack-vsn, + kem-group, kem-resp-share, ... ] +enc-ack-body = ecies.encrypt(initiator-pubk, ack-body, ack-size) +``` + +* `kem-group` in the `ack` **MUST** equal the `kem-group` from the `auth`. +* `kem-resp-share` carries the ML-KEM ciphertext produced by `MLKEM768.Encaps(ek)` and the recipient's fresh X25519 public key. + +#### 3.3 Size impact + +Computed with the current `bsc`/`go-ethereum` parameters: 113 bytes of ECIES overhead (65-byte ephemeral public key + 16-byte IV + 32-byte HMAC-SHA256), 100–199 bytes of EIP-8 random padding, plus the 2-byte size prefix. + +| Message | v4 | v5 | Δ | +| --- | --- | --- | --- | +| `auth` | 384–483 B | 1607–1706 B | +1223 B | +| `ack` | 317–416 B | 1444–1543 B | +1127 B | +| **Total handshake** | **~0.70–0.90 KB** | **~3.05–3.25 KB** | **≈ +2.3 KB** | + +Both messages remain far below the 65535-byte ceiling imposed by the 2-byte size prefix. Implementations **MUST NOT** apply a handshake-message size limit below 8192 bytes. + +### 4. Key schedule + +Version 5 replaces the v4 Keccak chain with HKDF-SHA256. The frame layer (AES-256-CTR + Keccak-based MAC) is **unchanged**; only the derivation of `aes-secret` and `mac-secret` changes. + +#### 4.1 Shared secrets + +``` +Z_c = ecdh.agree(ephemeral-privk, remote-ephemeral-pubk) // secp256k1, 32 B +Z_x = X25519(x25519-privk, remote-x25519-pub) // 32 B +Z_pq = MLKEM768 shared secret // 32 B + (initiator: Decaps(dk, ct); recipient: from Encaps(ek)) +``` + +`Z_c` is retained. It is the only shared secret that is cryptographically bound to the authenticated secp256k1 identity (via `sig`), and retaining it guarantees that v5 is **never weaker than v4** against a classical adversary. + +#### 4.2 Transcript binding + +``` +transcript = keccak256(auth || ack) +``` + +where `auth` and `ack` are the complete on-wire byte strings, **including** their 2-byte size prefixes and the ECIES envelopes. + +#### 4.3 Derivation + +``` +ikm = Z_pq || Z_x || Z_c // 96 bytes +salt = keccak256(initiator-nonce || recipient-nonce) // 32 bytes +prk = HKDF-Extract(salt, ikm) +okm = HKDF-Expand(prk, "rlpx-v5 keys" || transcript, 96) + +shared-secret = okm[0:32] +aes-secret = okm[32:64] +mac-secret = okm[64:96] +``` + +`"rlpx-v5 keys"` is the 12-byte ASCII literal, with no NUL terminator. + +Both parties **MUST** zeroise `Z_c`, `Z_x`, `Z_pq`, `ikm`, `prk`, `dk`, and the X25519 private key immediately after `okm` has been computed. + +#### 4.4 MAC initialisation (unchanged) + +``` +egress-mac = keccak256.init((mac-secret ^ recipient-nonce) || auth) +ingress-mac = keccak256.init((mac-secret ^ initiator-nonce) || ack) +``` + +(as seen from the initiator; the recipient mirrors the two.) + +#### 4.5 v4 compatibility + +If the handshake falls back to v4 (§6), the v4 key schedule is used **verbatim**, including the Keccak chain. Implementations MUST NOT apply the HKDF schedule to a v4 handshake. + +### 5. Operation ordering and DoS resistance + +The recipient **MUST** process an incoming `auth` in exactly this order: + +1. ECIES-decrypt `enc-auth-body` with its static private key. +2. RLP-decode; reject malformed bodies. +3. Verify `sig` and recover the initiator's secp256k1 ephemeral public key. +4. **Only then**: validate `kem-group` and `kem-init-share` length, and perform `MLKEM768.Encaps` and X25519. + +This ordering ensures that an unauthenticated peer cannot induce any post-quantum computation. Step 1 already requires an ECIES operation, so no new pre-authentication amplification vector is introduced. + +`kem-init-share` **MUST** be length-checked before being parsed. ML-KEM-768 encapsulation-key validation (the modulus check of FIPS 203 §7.2) **MUST** be performed; `crypto/mlkem`'s `NewEncapsulationKey768` does this. A malformed share **MUST** cause the handshake to be aborted, not downgraded. + +Implementations **SHOULD** cap concurrent inbound pending handshakes (a pending v5 handshake holds ~4 KB of state versus ~1 KB for v4). + +### 6. Negotiation, fallback, and downgrade detection + +There is **no additional round trip**. Negotiation is optimistic and rides on EIP-8's rule that decoders must ignore trailing RLP list elements. + +#### 6.1 Initiator + +* A v5-capable initiator **SHOULD** send a v5 `auth` to every peer. +* If the peer's ENR (§6.3) advertises no PQ support and the node is configured for `--rlpx.pq=compat`, it MAY send a v4 `auth` to save bandwidth. This is an optimisation only. +* On receiving the `ack`: + * if `ack-vsn >= 5` **and** a well-formed `kem-group`/`kem-resp-share` pair is present → use the §4 schedule; + * otherwise → use the v4 schedule and mark the session `pq=false`. + +#### 6.2 Recipient + +* If `auth-vsn >= 5` and `kem-group` is supported → reply with a v5 `ack`, use the §4 schedule. +* If `auth-vsn >= 5` but `kem-group` is unsupported or the share is the wrong length → reply with a **v4** `ack` and use the v4 schedule. +* If `auth-vsn == 4` → behave exactly as today. + +A v4-only peer that receives a v5 `auth` parses the first four list elements, ignores the rest per EIP-8, and replies with a v4 `ack`. No error is produced on either side. This is the property that makes the rollout non-breaking. + +#### 6.3 Downgrade detection + +A classical attacker **cannot** strip the `kem-*` fields from an `auth` or `ack`: they sit inside an ECIES envelope addressed to the peer's static key, and tampering with the envelope makes it fail to decrypt. Nor can it mount a transparent MITM, since that would require forging `sig` under the initiator's secp256k1 static key. + +What a classical on-path attacker **can** do is downgrade by *denial*: repeatedly reset or blackhole connections carrying a v5 `auth` until the initiator's retry logic gives up and falls back to v4. It still cannot read the resulting session, but it obtains PQ-unprotected ciphertext to store for later quantum decryption. Two normative rules and two detection mechanisms address this. + +**Normative rules.** + +* An implementation **MUST NOT** fall back from v5 to v4 in response to a transport-level failure (connection reset, timeout, or handshake abort). Fallback occurs **only** when a syntactically valid v4 `ack` is received in-band. +* An implementation **MUST NOT** retry a peer with a v4 `auth` after a v5 attempt to the same peer failed at the transport level. Such a peer is retried with v5 under the normal backoff schedule. + +**Detection mechanisms.** + +**(a) ENR advertisement.** Nodes supporting this BEP **SHOULD** publish the ENR key/value pair: + +``` +"pqkex" = 1 // RLP-encoded uint; 1 = RLPx v5 with X25519MLKEM768 +``` + +ENRs are signed by the node's secp256k1 identity key and are therefore not forgeable by a classical attacker. If a peer's verified ENR advertises `pqkex >= 1` but the completed handshake was v4, the local node **MUST** treat this as a downgrade event. + +**(b) `Hello` cross-check.** The RLPx `Hello` message is extended with a trailing field: + +``` +hello-body = [ protocolVersion, clientId, capabilities, listenPort, nodeId, + pq-capable, ... ] +``` + +`pq-capable` is an RLP boolean, `1` if the sender implements this BEP. `Hello` is exchanged over the already-established frame encryption, so a downgrading attacker cannot alter it without breaking secp256k1 in real time. If both peers set `pq-capable = 1` but the handshake used v4, this is a downgrade event. + +**Response to a downgrade event**, by rollout stage (§9): + +| Stage | Behaviour | +| --- | --- | +| Stage 1 | Log at WARN, increment `p2p/rlpx/downgrade` metric, keep the connection | +| Stage 2+ | Disconnect with reason `0x10` (subprotocol-specific / `PQDowngrade`), increment metric, apply peer backoff | + +Nodes configured with `--rlpx.pq=strict` **MUST** refuse any v4 handshake outright, regardless of stage. + +### 7. Configuration + +A new client flag is introduced: + +``` +--rlpx.pq= +``` + +| Mode | Meaning | +| --- | --- | +| `off` | v4 only. Emit v4 `auth`; ignore incoming `kem-*` fields. | +| `compat` | Accept v5, prefer v5, fall back to v4 silently. | +| `on` (default from Stage 1) | Always offer v5, fall back to v4, enforce downgrade detection per stage. | +| `strict` | v5 only. Abort any handshake that does not complete as v5. | + +Validators and their sentry nodes **SHOULD** run `--rlpx.pq=strict` between each other (via a static-peer allowlist) from Stage 1 onward, since that path is fully under operator control and carries the highest-value traffic. + +### 8. Test vectors and conformance + +The reference implementation **MUST** ship, under `p2p/rlpx/testdata/`, at minimum: + +1. **Deterministic handshake vectors.** Fixed initiator/recipient static keys, ephemeral secp256k1 keys, X25519 keys, ML-KEM-768 key-generation seed (64 bytes, FIPS 203 §7.1), and nonces, producing byte-exact `auth`, `ack`, `transcript`, `okm`, `aes-secret`, `mac-secret`. +2. **v4 ↔ v5 interoperability vectors.** v5 initiator against v4 recipient, and v4 initiator against v5 recipient, each asserting the v4 key schedule is used and the session is marked `pq=false`. +3. **Negative vectors.** Wrong `kem-init-share` length; `kem-group` mismatch between `auth` and `ack`; an `ek` failing FIPS 203 modulus validation; a transcript-substitution attempt. Each MUST abort the handshake. +4. **ML-KEM known-answer tests.** The FIPS 203 ACVP KATs, asserted against the linked `crypto/mlkem` implementation, so that a toolchain change cannot silently alter the primitive. + +Conformance additionally requires an interop matrix run against `go-ethereum` (v4-only) and at least one non-Go BSC-compatible client before Stage 2. + +### 9. Rollout + +No hard fork is required. The RLPx transport is not consensus-critical, so activation is by client release and configuration. + +| Stage | Trigger | Behaviour | +| --- | --- | --- | +| **Stage 0** | `bsc` release *N* | Implementation merged, `--rlpx.pq=off` by default. Testnet-only enablement. Interop and fuzz testing. | +| **Stage 1** | `bsc` release *N+1*, ≥ 6 weeks after Stage 0 | Default `--rlpx.pq=on`. Fallback silent. Downgrade events logged. Metric-driven measurement of PQ session share on Chapel and Mainnet. | +| **Stage 2** | PQ session share ≥ 90% on mainnet for 4 consecutive weeks | Downgrade events cause disconnect. Announced via BNB Chain release notes and validator comms. | +| **Stage 3** | PQ session share ≥ 99% and ≥ 6 months after Stage 2 | `--rlpx.pq=strict` becomes the default; v4 `auth` rejected. v4 code path retained behind `--rlpx.pq=compat` for one further release, then removed. | + +Metrics to be exported: `p2p/rlpx/handshake/pq` (counter), `p2p/rlpx/handshake/legacy` (counter), `p2p/rlpx/downgrade` (counter), `p2p/rlpx/handshake/duration` (histogram, labelled by version). + +## Rationale + +### Why hybrid rather than pure ML-KEM + +Lattice cryptanalysis is roughly a decade old, versus four decades for ECDLP. A hybrid construction fails only if *both* components fail. The cost is 32 bytes and one X25519 operation. Every major deployment (TLS, SSH, Signal, WireGuard proposals) has made the same choice, and NIST SP 800-56C Rev. 2 explicitly sanctions concatenating shared secrets into an approved KDF. There is no serious argument for pure ML-KEM here. + +### Why `X25519MLKEM768` rather than reusing secp256k1 + +The user-facing alternative was `secp256k1 + ML-KEM-768`, which avoids introducing a new curve. It was rejected because: + +* `X25519MLKEM768` is the single most-deployed post-quantum key exchange in existence. Reusing its exact share layout, secret ordering, and codepoint means BSC inherits third-party test vectors, third-party cryptanalysis, and battle-tested constant-time implementations. +* X25519 is roughly an order of magnitude faster than secp256k1 ECDH and has no invalid-curve, small-subgroup, or point-compression pitfalls. +* Go's `crypto/ecdh` provides X25519 in the standard library with a constant-time implementation; BSC's secp256k1 path depends on `btcec`/`libsecp256k1`. +* The secp256k1 ECDH is retained anyway (`Z_c`), so nothing is lost — the result is a three-secret schedule that is strictly stronger than either two-secret alternative. + +### Why retain the secp256k1 ephemeral ECDH + +Three reasons: + +1. **Authentication binding.** `sig` covers the secp256k1 ephemeral key. `Z_c` is therefore the shared secret provably tied to the authenticated static identity. The X25519 and ML-KEM shares are authenticated only transitively (they travel inside an ECIES envelope addressed to the recipient's static key, and they are covered by `transcript`). Keeping `Z_c` in the `ikm` makes the binding direct rather than transitive. +2. **Non-regression.** A v5 session is, by construction, at least as strong as a v4 session against any classical adversary. This makes review and risk sign-off dramatically simpler. +3. **Minimal diff.** The `sig` / ephemeral-key-recovery machinery is the most subtle part of the existing handshake. Not touching it removes a large class of implementation risk. + +### Why HKDF instead of extending the v4 Keccak chain + +The v4 derivation (`shared-secret = keccak256(ephemeral-key || keccak256(...))`) is an ad-hoc construction with no security proof, no domain separation, and no transcript binding. Since a new version number is being introduced anyway, moving to HKDF-SHA256 buys: an approved KDF under SP 800-56C Rev. 2 (required for any FIPS-relevant deployment), free domain separation via `info`, and a natural place to bind the transcript. The frame layer is deliberately untouched so the change stays confined to one function. + +### Why bind the transcript + +Without transcript binding, the `kem-group` field is unauthenticated and an attacker could try to negotiate a weaker reserved group without either party noticing. `transcript = keccak256(auth || ack)` covers the version numbers, the group identifier, both shares, both nonces, and the ECIES envelopes, at the cost of one hash over ~3.4 KB. It is the cheapest possible defence against negotiation tampering. + +### Why optimistic negotiation instead of a capability round trip + +An explicit "which suites do you support?" exchange would add a full RTT to every connection — measurable on a chain with 750 ms blocks and a large, geographically dispersed peer set. EIP-8 already guarantees that trailing RLP elements are ignored, so appending fields costs nothing and degrades gracefully. The residual downgrade risk is handled by §6.3 rather than by burning a round trip. + +### Why not upgrade node identity keys in Phase 1 + +Node identity (`enode` / NodeID) is a secp256k1 key used for signatures, not key agreement. Breaking it requires a CRQC at attack time, not afterwards, so it carries no HNDL exposure. Moving it to ML-DSA would additionally change ENR sizes, the discv4/discv5 wire format, the enode URL format, bootnode lists, and every peer-management database — a far larger blast radius for no retroactive benefit. It belongs with Phase 2. + +## Backward Compatibility + +This BEP is **fully backward compatible** and requires **no hard fork**. + +* **Consensus:** unaffected. No change to blocks, transactions, state, gas, or the EVM. +* **v4 peers:** a v5 `auth` is parsed successfully by any EIP-8-compliant v4 implementation, which ignores the trailing fields and answers with a v4 `ack`. Both sides then complete a normal v4 handshake. +* **v5 peers talking to v4-only clients** (including upstream `go-ethereum` nodes on shared infrastructure) continue to work indefinitely until Stage 3. +* **Tooling:** enode URLs, ENRs, discovery, and the admin RPC surface are unchanged, except for the optional additive `pqkex` ENR entry and the additive `pq-capable` field in `Hello` — both of which are ignored by older software under existing forward-compatibility rules. +* **Bandwidth:** +2.3 KB per handshake. For a node holding 100 peers with a 1-hour mean connection lifetime, that is ~230 KB/hour — roughly four orders of magnitude below the block and transaction gossip such a node already carries. +* **Stage 3 is the only breaking step**, and it is gated on ≥99% adoption plus a 6-month notice period. + +A risk worth stating explicitly: this BEP relies on non-geth clients honouring EIP-8's "ignore trailing elements" rule. Any implementation that strictly validates `auth-vsn == 4` will reject a v5 `auth` outright. Stage 0 therefore requires an interop sweep against every known BSC-compatible client, and `--rlpx.pq=compat` exists precisely so that operators can pin behaviour if a non-conforming implementation is found in the wild. + +## Security Considerations + +### What this BEP achieves + +Confidentiality of RLPx session traffic becomes IND-CCA2 secure against a quantum adversary under the Module-LWE assumption, provided the ML-KEM-768 component holds. Recorded traffic from a v5 session cannot be decrypted by a future CRQC. ML-KEM-768 targets NIST security category 3 (≈ AES-192), comfortably above the 128-bit classical level of the surrounding construction. + +### What this BEP does not achieve + +**Authentication remains classical.** An adversary who possesses a CRQC *at connection time* can derive a node's secp256k1 static private key from its public enode identity and impersonate it, or mount a full MITM. This BEP does not, and cannot, prevent that; it prevents only the retroactive attack. Phase 2 addresses live impersonation. Operators should not read "post-quantum P2P" as "quantum-safe P2P". + +### Downgrade attacks + +The `kem-*` fields are not modifiable in flight (they are inside an ECIES envelope), and a classical attacker cannot MITM the handshake (that would require forging `sig`). The realistic rollback vector is therefore *downgrade by denial*: killing v5 connections until the client falls back to v4. §6.3 forbids failure-triggered fallback outright, and makes any residual downgrade loud rather than silent: + +* The attacker cannot forge the ENR `pqkex` entry (ENRs are signed) nor tamper with `Hello` (sent under frame encryption whose keys it does not hold). +* From Stage 2 onward, detection results in immediate disconnection and peer backoff, so a persistent downgrader produces continuous connection flapping against that peer — highly visible in metrics. +* The residual leak per detected attempt is a single `Hello` exchange plus at most a few frames, not a full session. +* `--rlpx.pq=strict` eliminates the attack entirely for peer sets under operator control (validator ↔ sentry), which is where it matters most. + +### Key reuse + +ML-KEM is IND-CCA2 secure and therefore *tolerates* encapsulation-key reuse, but reuse would destroy forward secrecy and enable multi-target attacks. This BEP mandates a fresh ML-KEM key pair and a fresh X25519 key pair per handshake attempt (§3.1), including on retry after a failure. Implementations MUST NOT cache `(ek, dk)` across connections. + +Note that ML-KEM-768 key generation costs roughly 25 µs; per-handshake generation is not a meaningful cost. + +### Implicit rejection + +FIPS 203 `Decaps` never fails — on an invalid ciphertext it returns a pseudorandom shared secret derived from the implicit-rejection key. A mismatch therefore surfaces as a frame-MAC failure on the first encrypted message, not as a handshake error. Implementations **MUST NOT** branch on decapsulation "success" and **MUST** treat the resulting MAC failure as a normal handshake abort. The `transcript` binding ensures that a mismatch is always detected before any application data is processed. + +### Side channels + +`crypto/mlkem` and `crypto/ecdh` are constant-time in Go's implementation. Implementations MUST NOT substitute a variable-time ML-KEM or X25519 implementation. Timing attacks on ML-KEM decapsulation (e.g. KyberSlash-class divide-timing bugs) have well-known mitigations already applied upstream; pinning to the standard library rather than a bespoke implementation is a deliberate risk-reduction choice. + +### Denial of service + +The recipient performs post-quantum work only after signature verification (§5), so the pre-authentication cost is unchanged from today. Per-handshake memory rises from ~1 KB to ~4 KB; nodes accepting large numbers of concurrent inbound connections should verify their pending-handshake cap. The 8192-byte floor on handshake message size (§3.3) is a floor, not a licence: implementations SHOULD still reject `auth`/`ack` messages larger than 16384 bytes. + +### Cryptographic agility + +The `kem-group` field plus the version number give a clean upgrade path. Should ML-KEM-768 be weakened, `X25519MLKEM1024` (`0x11EE`) can be enabled by a follow-up BEP with no wire-format change beyond the share lengths, and the negotiation logic in §6 requires no modification. + +### Nonce handling + +`initiator-nonce` and `recipient-nonce` remain 32 bytes and MUST be sampled from a CSPRNG per handshake. They are now used as HKDF salt rather than as direct hash inputs, which is strictly more conservative. + +## Reference Implementation + +Target: `bnb-chain/bsc`, package `p2p/rlpx`. + +| File | Change | +| --- | --- | +| `p2p/rlpx/rlpx.go` | `authMsgV5` / `ackMsgV5` structs; version dispatch in `handshakeState`; `deriveSecretsV5` alongside the existing `deriveSecrets` | +| `p2p/rlpx/pqkem.go` | *(new)* hybrid group registry, share encode/decode, `Encaps`/`Decaps` wrappers over `crypto/mlkem` and `crypto/ecdh` | +| `p2p/rlpx/rlpx_test.go` | vectors per §8 | +| `p2p/peer.go` | `pq-capable` field in `Hello`; downgrade detection and disconnect reason | +| `p2p/enr` / `p2p/server.go` | `pqkex` ENR entry, publication and verification | +| `eth/ethconfig`, `cmd/utils/flags.go` | `--rlpx.pq` flag | +| `p2p/metrics.go` | PQ handshake counters | + +Dependencies: Go ≥ 1.24 (`crypto/mlkem`, `crypto/ecdh`, `crypto/hkdf`). No new third-party module. BSC's minimum Go version must be raised to 1.24 as a prerequisite. + +Estimated scope: ~800 LoC plus tests. The change is confined to the transport layer and touches no consensus code. + +## Roadmap Context + +This BEP is **Phase 1** of the BNB Chain post-quantum programme. + +| Phase | Scope | Retroactive risk? | Requires hard fork? | +| --- | --- | --- | --- | +| **1 — P2P key exchange (this BEP)** | RLPx session key agreement → X25519 + ML-KEM-768 | **Yes** — HNDL | No | +| 2 — Account & node signatures | secp256k1 ECDSA → ML-DSA / SLH-DSA, new tx types, PQ-capable account abstraction, node identity | No | Yes | +| 3 — Pairing-based cryptography | BLS12-381 validator vote aggregation, KZG commitments, BN254 pairing precompiles | No | Yes | + +Phases 2 and 3 are strictly larger, touch consensus, and are not retroactively exposed. Phase 1 is sequenced first because it is the only one where every day of delay adds permanently unrecoverable exposure, and because it can ship without coordinating a fork. + +## References + +* [devp2p — The RLPx Transport Protocol](https://github.com/ethereum/devp2p/blob/master/rlpx.md) +* [EIP-8: devp2p Forward Compatibility Requirements for Homestead](https://eips.ethereum.org/EIPS/eip-8) +* [EIP-778: Ethereum Node Records (ENR)](https://eips.ethereum.org/EIPS/eip-778) +* [FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard](https://csrc.nist.gov/pubs/fips/203/final) +* [draft-ietf-tls-ecdhe-mlkem — Post-quantum hybrid ECDHE-MLKEM Key Agreement for TLS 1.3](https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/) +* [draft-ietf-tls-hybrid-design — Hybrid key exchange in TLS 1.3](https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/) +* [RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF)](https://www.rfc-editor.org/rfc/rfc5869) +* [RFC 7748: Elliptic Curves for Security (X25519)](https://www.rfc-editor.org/rfc/rfc7748) +* [NIST SP 800-56C Rev. 2: Recommendation for Key-Derivation Methods in Key-Establishment Schemes](https://csrc.nist.gov/pubs/sp/800/56/c/r2/final) +* [Go `crypto/mlkem` package documentation](https://pkg.go.dev/crypto/mlkem) +* [BEP-1: BEP Purpose and Guidelines](https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP1.md) + +## License + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file