Skip to content

Feature: QUIC Support Plan #428

@driftluo

Description

@driftluo

Tentacle QUIC Support Implementation Plan

Goal: Add native QUIC support to Tentacle using quinn, with non-DNS /ip4|ip6/.../udp/.../quic addresses, peer identity derived from the existing secio public key, and support for building QUIC on top of an already hole-punched UDP socket.

Architecture: Keep the existing TCP/WebSocket/TLS stack unchanged. Add a separate QUIC stack that bypasses secio + yamux and uses quinn::Connection plus QUIC bidirectional streams as the transport/session substrate, while continuing to expose SessionContext.remote_pubkey and PeerId with the current semantics. Replace CA-based TLS validation with strict custom rustls certificate verification that binds a self-signed TLS certificate to the node's long-term secio identity.

Tech Stack: quinn, rustls, rcgen, x509-parser, existing tentacle-secio, existing protocol negotiation and codec pipeline.

Fixed Product Decisions

  • QUIC is a new transport path, not an extension of HandshakeType::Secio and not a new muxer under the current stream path.
  • QUIC addresses are only:
    • /ip4/<addr>/udp/<port>/quic
    • /ip6/<addr>/udp/<port>/quic
    • optional /p2p/<peer_id> suffix
  • DNS-based QUIC addresses are rejected in v1.
  • PeerId remains PeerId::from_public_key(secio::PublicKey).
  • QUIC certificate identity is only a proof carrier for that existing identity.
  • QUIC uses only bidirectional streams in v1.
  • No QUIC datagrams, uni-streams, 0-RTT, UDP proxy, UDP onion, or UDP UPnP in v1.
  • NAT integration is done by injecting a usable UDP socket into Tentacle, then letting Tentacle build and manage the quinn::Endpoint.

TLS Identity Model

  • Keep the current long-term node identity keypair K_secio from the existing KeyProvider.
  • Generate one QUIC TLS keypair K_tls per service instance, using Ed25519 by default.
  • Generate one self-signed leaf certificate and reuse it for the service lifetime.
  • Add one private X.509 extension with a fixed Tentacle OID. Encode the extension payload as:
TentacleQuicIdentityV1 {
    version: u8 = 1,
    secio_pubkey: Bytes,
    peer_id: Bytes,
    binding_sig: Bytes,
}
  • Compute:
    • peer_id = PeerId::from_public_key(secio_pubkey)
    • binding_sig = secp256k1_sign(K_secio, sha256("tentacle-quic-binding/v1" || tls_spki_der))
  • tls_spki_der is the DER-encoded SubjectPublicKeyInfo of the TLS certificate public key.
  • Verification rule:
    • the verifier extracts secio_pubkey
    • recomputes peer_id
    • verifies the secp256k1 signature over the leaf certificate SPKI
    • if the dial target has /p2p/<peer_id>, it must match exactly
  • CA validation is not used at all.
  • Hostname validation is not used at all.
  • TLS remains strict because the certificate must still pass custom identity verification.

TLS Verification Rules

Client verifier

  • Build rustls client config without any root store.
  • Use dangerous().with_custom_certificate_verifier(...).
  • Require the local side to present its own certificate too.
  • Verification steps:
    1. Reject empty certificate chains.
    2. Reject chains containing intermediates.
    3. Parse the single leaf certificate.
    4. Check certificate validity period.
    5. Extract the Tentacle QUIC identity extension.
    6. Decode the Molecule payload.
    7. Validate peer_id == PeerId::from_public_key(secio_pubkey).
    8. Validate the secp256k1 binding signature against sha256("tentacle-quic-binding/v1" || tls_spki_der).
    9. If the target multiaddr contains /p2p/<expected>, validate expected == peer_id.
    10. Ignore SAN and hostname contents.
  • Use a fixed placeholder server name, such as tentacle.invalid when dialing.

Server verifier

  • Build rustls’ server config without CA-based client authentication.
  • Use with_client_cert_verifier(...).
  • Make client authentication mandatory.
  • Verification steps are the same as on the client side, except there is no dial-target peer check.

Handshake result propagation

  • After QUIC handshake succeeds, call quinn::Connection::peer_identity().
  • Re-parse the peer leaf certificate.
  • Re-extract secio_pubkey.
  • Convert it into secio::PublicKey.
  • Store it in SessionContext.remote_pubkey.
  • Keep existing PeerIdNotMatch, repeated-connection detection, identify, and discovery logic based on remote_pubkey.

Development tasks

  1. multiaddr supports the QUIC protocol
  2. Verifying the connectivity of QUIC, which is expected to use the ed25519 algorithm in TLS
  3. Implement self-signed certificate generation and identity binding
  4. Implement rustls‘ custom verifiers for QUIC
  5. Use the custom extension to exchange peerid and pubkey
  6. Add QUIC configuration and builder surface

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions