Skip to content

[Post-Quantum] TEC-05: vendored Discv5 session-key derivation uses HKDF-SHA-256 (CNSA 2.0 compliance) #415

Description

@0g-peterzhb

Source: Tectonic Quantum Readiness Audit v1.0 — finding TEC-05
Tracking: this issue (#415)
Verified at: commit 6ad0e54 on 2026-06-11 (vendored file is byte-identical at HEAD)
Status: Informational — no action recommended (compliance-tracking only)

Vulnerable code

The vendored Discv5 implementation derives session keys with HKDF instantiated over SHA-256.

Imports — the KDF and hash are hard-wired to Hkdf + Sha256:

version-meld/discv5/src/handler/crypto/mod.rs:28-29

use hkdf::Hkdf;
use sha2::{Digest, Sha256};

The key-agreement transcript label, which is mixed into the HKDF info parameter:

version-meld/discv5/src/handler/crypto/mod.rs:37

const KEY_AGREEMENT_STRING: &str = "discovery v5 key agreement";

The derivation itself — note Hkdf::<Sha256>::new(...) at line 83 and .expand(...) at line 86, producing two 16-byte (KEY_LENGTH) AES-128-GCM session keys:

version-meld/discv5/src/handler/crypto/mod.rs:72-97

fn derive_key(
    secret: &[u8],
    first_id: &NodeId,
    second_id: &NodeId,
    challenge_data: &ChallengeData,
) -> Result<(Key, Key), Discv5Error> {
    let mut info = [0u8; INFO_LENGTH];
    info[0..26].copy_from_slice(KEY_AGREEMENT_STRING.as_bytes());
    info[26..26 + NODE_ID_LENGTH].copy_from_slice(&first_id.raw());
    info[26 + NODE_ID_LENGTH..].copy_from_slice(&second_id.raw());

    let hk = Hkdf::<Sha256>::new(Some(challenge_data.as_ref()), secret);

    let mut okm = [0u8; 2 * KEY_LENGTH];
    hk.expand(&info, &mut okm)
        .map_err(|_| Discv5Error::KeyDerivationFailed)?;

    let mut initiator_key: Key = Default::default();
    let mut recipient_key: Key = Default::default();
    initiator_key.copy_from_slice(&okm[0..KEY_LENGTH]);
    recipient_key.copy_from_slice(&okm[KEY_LENGTH..2 * KEY_LENGTH]);

    Ok((initiator_key, recipient_key))
}

The module header is explicit that this is fixed by spec, not by choice:

Session keys are then derived using the HKDF (SHA2-256) key derivation function. There is no abstraction in this module as the specification explicitly defines a singular encryption and key-derivation algorithms.

The vulnerability

Primitive: HKDF (RFC 5869) instantiated with HMAC-SHA-256 as the underlying PRF, used to expand an ECDH shared secret (secret) plus a session-specific salt (challenge_data) into the two AES-128-GCM session keys that protect Discovery v5 traffic.

Why it is (not) quantum-vulnerable: HKDF-SHA-256 is a symmetric/hash construction, so the relevant threat is Grover, not Shor. Grover's algorithm gives at best a quadratic speedup against the preimage/PRF security of the hash, halving the effective security level: SHA-256's ~256-bit preimage resistance degrades to ~128-bit under an idealized quantum search. A ~128-bit effective security margin is still firmly in the "computationally infeasible" regime and is the conventional bar for post-quantum symmetric security. HKDF-SHA-256 is therefore considered quantum-resistant. (Note: in practice the keys are only 128-bit KEY_LENGTH AES-128 keys, and the upstream ECDH over secp256k1 — TEC tracked separately — is the actual Shor-breakable component of this handshake; the KDF is not the weak link.)

What a CRQC operator could actually do here: Nothing via this primitive. There is no realistic attack on HKDF-SHA-256 from a cryptographically-relevant quantum computer. The session keys produced here are not the harvest-now-decrypt-later concern; if an adversary wanted to recover the session key, it would attack the secp256k1 ECDH key exchange (Shor), not the HKDF expansion. The hash choice does not change the blast radius of the handshake one way or the other.

Honest impact assessment: This is informational / compliance-only, and the audit rated it as such — there is no overstatement to correct here. The gap is purely against CNSA 2.0, which specifies SHA-384/SHA-512 for symmetric/hash usage in NSS contexts. It is not a security weakness, not HNDL, and not live-attack-relevant. Discovery v5 traffic and the data it routes toward (block/log/chunk availability on a public storage network) are public-by-design anyway, so even a hypothetical KDF break would not expose confidential data. The proportionate rating is Informational, no action.

Constraint

This cannot be changed unilaterally in this repo without forking the discovery protocol. The Discovery v5 wire specification (devp2p discv5-wire) fixes the key-derivation function as HKDF and the hash as SHA-256, and it fixes the literal "discovery v5 key agreement" info string as part of the key-agreement transcript. Both peers independently run derive_key over the same inputs and must arrive at identical initiator_key/recipient_key values for the AES-128-GCM session to work. Swapping SHA-256 for SHA-384/512, or altering the info string, changes the derived key material on one side only, so handshakes against any spec-compliant peer (including all upstream sigp/discv5 and go-ethereum nodes) would fail. The module's own doc comment states the algorithm is "explicitly defined" by the spec with "no abstraction." So while the crate is vendored and technically editable, the constraint is a protocol/interop mandate, not a code-ownership one.

Potential solution

Compliance tracking only — no code change recommended. Record TEC-05 in the PQ/compliance register as "accepted: spec-mandated HKDF-SHA-256, quantum-resistant at ~128-bit, CNSA-2.0 hash-suite gap only."

There is nothing actionable to scaffold in this repo, because the fix is not local. The correct migration trigger to watch is an upstream devp2p Discovery v5 wire-spec revision that adopts a SHA-384/512 (or otherwise CNSA-2.0-aligned) key-agreement suite — track the ethereum/devp2p discv5 spec and the upstream sigp/discv5 crate (this vendored copy's origin). If and when upstream introduces a negotiated/versioned KDF suite, the change would be pulled in by re-syncing version-meld/discv5, not by patching the vendored file ahead of the network. Patching unilaterally now would only break interoperability, so the only "scaffolding" worth doing is keeping the vendored crate close to upstream so a future suite bump is a clean merge.

Checklist

  • Record TEC-05 in the PQ/compliance register as accepted (informational, spec-mandated, no action).
  • Note the CNSA-2.0 hash-suite gap (SHA-256 vs SHA-384/512) explicitly so it is not re-flagged as a new finding.
  • Add a watch on ethereum/devp2p discv5 wire spec and sigp/discv5 for any KDF/hash-suite change or negotiation mechanism.
  • Keep version-meld/discv5 reasonably in sync with upstream so a future suite migration is a clean re-sync rather than a manual patch.
  • Close this issue as wont-fix / informational once the above is logged.

Locations verified against source at 6ad0e54; drafted by Claude Code from the third-party Tectonic PQ audit.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    post-quantumPost-quantum cryptography readiness (from Tectonic audit)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions