Skip to content

quic: cert generating and verifying, simple quic smoke test#431

Merged
Officeyutong merged 10 commits into
nervosnetwork:masterfrom
Officeyutong:quic-cert-generating
Apr 23, 2026
Merged

quic: cert generating and verifying, simple quic smoke test#431
Officeyutong merged 10 commits into
nervosnetwork:masterfrom
Officeyutong:quic-cert-generating

Conversation

@Officeyutong

@Officeyutong Officeyutong commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR is part 2 of 5 in the series to add native QUIC transport support to Tentacle, as outlined in #428.

QUIC Implementation Roadmap

PR Title Description
PR 1 multiaddr: add Udp and QuicV1 protocols Extend the multiaddr crate to parse, serialize, and display /udp/<port> and /quic-v1 protocol components
PR 2 (this) QUIC connectivity spike + self-signed certificate generation Introduce the quic feature with quinn/rustls/rcgen/x509-parser/ring dependencies, verify end-to-end QUIC connectivity, and implement the dual-key model: self-signed X.509 certificate carrying a molecule-encoded private extension with binding_sig derived from the secio keypair
PR 3 Custom rustls certificate verifiers Implement ServerCertVerifier and ClientCertVerifier that enforce Tentacle's identity model instead of CA-based validation
PR 4 End-to-end QuicSession Wire up QuicBiStream, QuicEndpoint, QuicSession, and the three integration points into InnerService; generalize Substream<U> with a stream type parameter
PR 5 ServiceBuilder integration, example, and docs Expose ServiceBuilder::quic_config(), add a runnable example, and write user-facing documentation

What this PR does

1. Dependencies and feature flag (tentacle/Cargo.toml):

  • New quic feature that gates quinn 0.11, rustls 0.23, rcgen 0.13, x509-parser 0.16, and ring 0.17
  • quic is added to the default feature set alongside tokio-runtime and tokio-timer, so out-of-the-box builds include QUIC support; users can opt out via default-features = false
  • All QUIC-related deps are optional = true so downstreams that disable the feature pay zero compile-time cost
  • Added [[example]] entries for simple_using_spawn (required-features = ["unstable"]) and quic_simple (required-features = ["quic"]) so cargo build --examples doesn't fail when these features are off
  • Aligned rustls / rcgen / quinn on the aws_lc_rs crypto provider (via rustls/aws_lc_rs, rcgen/aws_lc_rs, quinn/rustls-aws-lc-rs) to avoid clashing with the tls feature's tokio-rustls, which also defaults to aws_lc_rs. Enabling both features simultaneously no longer leaves rustls with two ambiguous providers

2. tentacle/src/quic/ module layout (pub mod quic in lib.rs):

File Content
mod.rs Module declarations
config.rs QuicConfig struct with max_idle_timeout (30s), keep_alive_interval (10s), max_concurrent_bidi_streams (256)
error.rs QuicErrorKind enum: CertificateError, SigningError, ExtensionNotFound, IdentityVersionUnsupported, MultipleIdentityFound
identity.mol Molecule schema for TentacleQuicIdentityV1 { version, secio_pubkey, peer_id, binding_sig }
identity_mol.rs Generated molecule codec (785 lines)
identity.rs build_self_signed / extract_identity / verify_binding (410 lines including 9 tests)

3. Identity binding implementation (identity.rs):

  • TENTACLE_QUIC_IDENT_OID = 1.3.6.1.4.1.99999.1.1
  • BINDING_DOMAIN = b"tentacle-quic-binding/v1"
  • build_self_signed(key: &impl KeyProvider) -> Result<TentacleQuicCert>:
    1. Generate Ed25519 TLS keypair via rcgen::PKCS_ED25519
    2. Extract SPKI DER of the TLS keypair
    3. binding_sig = secp256k1_sign(K_secio, sha256(BINDING_DOMAIN || spki_der)) using ring::digest::SHA256
    4. Build a molecule TentacleQuicIdentityV1 payload and wrap it as a rcgen::CustomExtension
    5. Self-sign via CertificateParams::self_signed
  • extract_identity(leaf_der) -> Result<TentacleQuicIdentityV1>:
    • Parses the cert with x509-parser
    • Iterates extensions, matching OID safely (no panic on malformed OIDs)
    • Rejects multiple extensions with the same OID (MultipleIdentityFound)
    • Uses from_compatible_slice to allow forward-compatible payloads
    • Validates version == 1, returning IdentityVersionUnsupported(v) otherwise
  • verify_binding(local_key: &impl KeyProvider, secio_pubkey, leaf_spki_der, sig) -> Result<()>:
    • Recomputes sha256(BINDING_DOMAIN || leaf_spki_der)
    • Verifies the signature using KeyProvider::verify_ecdsa (local_key's private material is never accessed)

4. Connectivity spike (tentacle/examples/quic_simple.rs):

A minimal end-to-end QUIC demo using quinn + rustls + rcgen, with an AcceptAnyVerifier placeholder (real verifier arrives in PR 3). Server listens on a random UDP port, client connects, exchanges a 0xdeadbeef / 0xcafebabe handshake, and shuts down cleanly via Connection::closed().await.

Spike findings (recorded for downstream PRs)

  • cargo build --features quic completes in ~8s cold, ~3s warm on a dev box — acceptable
  • rcgen::KeyPair::generate_for(&PKCS_ED25519) is the stable entry for Ed25519 keypair generation in rcgen 0.13; public_key_der() returns the full SPKI in DER form
  • quinn::Connection::peer_identity() returns Option<Box<dyn Any + Send + Sync>>; downcast to Vec<rustls::pki_types::CertificateDer<'static>> to access the peer cert chain
  • quinn::SendStream / RecvStream already implement tokio::io::AsyncWrite / AsyncRead — no wrapper needed for PR 4's QuicBiStream
  • quinn + tokio multi-thread runtime works out of the box when quinn is built with features = ["runtime-tokio", "rustls"]
  • Gotcha: after SendStream::finish() the task must keep the Connection alive (e.g. conn.closed().await) until the peer has read the buffered data, otherwise dropping the connection triggers ApplicationClosed on the reader

Test plan

9 unit tests in tentacle/src/quic/identity.rs:

  • test_build_and_extract — build → extract round-trips version / secio_pubkey / peer_id / binding_sig correctly
  • test_verify_binding_ok — a well-formed binding_sig verifies successfully
  • test_verify_binding_tampered_sig — flipping the last byte of the signature yields SigningError
  • test_verify_binding_wrong_pubkey — verifying with a different keypair's public key yields SigningError
  • test_verify_binding_wrong_spki — verifying against an unrelated SPKI yields SigningError (prevents cross-certificate signature replay)
  • test_extract_missing_extension — cert without the private extension yields ExtensionNotFound
  • test_extract_malformed_payload — cert with the correct OID but random bytes yields CertificateError
  • test_extract_unsupported_version — molecule payload with version = 2 yields IdentityVersionUnsupported(2)
  • test_extract_multiple_identity — two extensions with the same OID yield MultipleIdentityFound

End-to-end smoke test (cargo run --features quic --example quic_simple):

  • Server + client handshake and exchange 0xdeadbeef / 0xcafebabe
  • Clean shutdown without dangling tasks or ApplicationClosed errors

Regression checks:

  • cargo test --all --features ws,unstable,tls,upnp (matches CI, implicitly enables quic via defaults) — all tests pass, no rustls crypto-provider panic
  • cargo test --all --no-default-features --features tokio-runtime,tokio-timer — builds cleanly with quic opted out
  • cargo test --features quic -p tentacle — 9 new quic::identity tests + existing tests all pass
  • cargo build --examples (defaults include quic) and cargo build --examples --features "unstable" — all examples compile

@Officeyutong Officeyutong marked this pull request as ready for review April 20, 2026 11:46
@eval-exec eval-exec requested review from chenyukang, doitian, quake and zhangsoledad and removed request for driftluo April 21, 2026 05:53
@eval-exec eval-exec added this to Kanban Apr 21, 2026
@eval-exec eval-exec moved this to 🏗 In progress in Kanban Apr 21, 2026
Comment thread yamux/src/stream.rs Outdated

match self.frame_receiver.try_recv() {
Ok(frame) => {
match self.frame_receiver.try_next() {

@eval-exec eval-exec Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using try_next() replace try_recv()?
How about replacing Err(_) arm with explicit inner error type?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally tentacle uses future-channel v0.3.31 where try_recv was removed (See https://docs.rs/futures-channel/0.3.31/futures_channel/mpsc/struct.Receiver.html?search=try_recv)

I updated it to v0.3.32 where try_recv is introduced again

Comment thread tentacle/src/quic/identity.mol Outdated
@@ -0,0 +1,8 @@
vector Bytes <byte>;
array u8 [byte; 1];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to create array u8? Does u8 is enough?
I reviewed other molecule type name, it's capitalized first letter, like Uint8 : https://github.com/nervosnetwork/ckb/blob/5ebbc3921f392184a8f4c7dfb743159c65281c1a/util/gen-types/schemas/protocols.mol#L15

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also u8 has been renamed to Uint8 now.

Comment thread tentacle/src/quic/identity.mol Outdated
table TentacleQuicIdentityV1 {
version: u8,
secio_pubkey: Bytes,
peer_id: Bytes,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does peer_id have fixed length, if have, using array with fixed length is better?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length of peer_id is 34 bytes, fixed now

@Officeyutong Officeyutong requested a review from eval-exec April 21, 2026 08:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Part 2/5 of the QUIC transport rollout for tentacle: introduces a quic-gated module with self-signed certificate generation + identity extension encoding/decoding/verification, and adds a minimal QUIC connectivity smoke-test example.

Changes:

  • Add tentacle::quic module with QUIC identity (Molecule payload in X.509 extension) build/extract/verify logic and unit tests.
  • Add QuicConfig and QuicErrorKind types to support QUIC configuration and error reporting.
  • Add quic_simple example and QUIC-related optional dependencies / feature wiring in tentacle/Cargo.toml.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tentacle/src/quic/mod.rs Declares QUIC submodules.
tentacle/src/quic/config.rs Introduces QuicConfig defaults (timeouts, keepalive, stream limits).
tentacle/src/quic/error.rs Defines QUIC identity/cert error types.
tentacle/src/quic/identity.mol Molecule schema for QUIC identity payload.
tentacle/src/quic/identity_mol.rs Generated Molecule codec for the identity payload.
tentacle/src/quic/identity.rs Self-signed cert generation, extension extraction, binding signature verification + tests.
tentacle/src/lib.rs Exposes pub mod quic behind cfg(feature = "quic").
tentacle/examples/quic_simple.rs End-to-end QUIC smoke test using quinn/rustls with an “accept any” verifier placeholder.
tentacle/Cargo.toml Adds QUIC deps + quic feature + example metadata.
secio/src/peer_id.rs Documents PeerId byte-length (34 bytes).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tentacle/src/quic/identity_mol.rs Outdated
Comment thread tentacle/Cargo.toml
Comment thread tentacle/src/quic/mod.rs Outdated
Comment thread tentacle/src/quic/identity.rs Outdated
Comment thread tentacle/src/quic/identity.rs Outdated
Comment thread tentacle/src/quic/identity.rs
Comment thread tentacle/examples/quic_simple.rs Outdated
Comment thread tentacle/examples/quic_simple.rs Outdated
Officeyutong and others added 3 commits April 23, 2026 15:17
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@Officeyutong Officeyutong merged commit 4f10949 into nervosnetwork:master Apr 23, 2026
7 checks passed
@github-project-automation github-project-automation Bot moved this from 🏗 In progress to ✅ Done in Kanban Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

3 participants