quic: cert generating and verifying, simple quic smoke test#431
Conversation
|
|
||
| match self.frame_receiver.try_recv() { | ||
| Ok(frame) => { | ||
| match self.frame_receiver.try_next() { |
There was a problem hiding this comment.
Why using try_next() replace try_recv()?
How about replacing Err(_) arm with explicit inner error type?
There was a problem hiding this comment.
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
| @@ -0,0 +1,8 @@ | |||
| vector Bytes <byte>; | |||
| array u8 [byte; 1]; | |||
There was a problem hiding this comment.
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
There was a problem hiding this comment.
In https://github.com/nervosnetwork/ckb/blob/5ebbc3921f392184a8f4c7dfb743159c65281c1a/util/gen-types/schemas/protocols.mol#L15, types like Uint32 is defined in https://github.com/nervosnetwork/ckb/blob/5ebbc3921f392184a8f4c7dfb743159c65281c1a/util/gen-types/schemas/blockchain.mol and imported into protocols.md.
In tentacle, such integer types are directly defined like array Uint32 [byte; 4](See https://github.com/nervosnetwork/tentacle/blob/master/protocols/ping/src/protocol.mol)
There was a problem hiding this comment.
Also u8 has been renamed to Uint8 now.
| table TentacleQuicIdentityV1 { | ||
| version: u8, | ||
| secio_pubkey: Bytes, | ||
| peer_id: Bytes, |
There was a problem hiding this comment.
Does peer_id have fixed length, if have, using array with fixed length is better?
There was a problem hiding this comment.
The length of peer_id is 34 bytes, fixed now
There was a problem hiding this comment.
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::quicmodule with QUIC identity (Molecule payload in X.509 extension) build/extract/verify logic and unit tests. - Add
QuicConfigandQuicErrorKindtypes to support QUIC configuration and error reporting. - Add
quic_simpleexample and QUIC-related optional dependencies / feature wiring intentacle/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.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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
UdpandQuicV1protocols/udp/<port>and/quic-v1protocol componentsquicfeature withquinn/rustls/rcgen/x509-parser/ringdependencies, verify end-to-end QUIC connectivity, and implement the dual-key model: self-signed X.509 certificate carrying a molecule-encoded private extension withbinding_sigderived from the secio keypairServerCertVerifierandClientCertVerifierthat enforce Tentacle's identity model instead of CA-based validationQuicBiStream,QuicEndpoint,QuicSession, and the three integration points intoInnerService; generalizeSubstream<U>with a stream type parameterServiceBuilder::quic_config(), add a runnable example, and write user-facing documentationWhat this PR does
1. Dependencies and feature flag (
tentacle/Cargo.toml):quicfeature that gatesquinn0.11,rustls0.23,rcgen0.13,x509-parser0.16, andring0.17quicis added to the default feature set alongsidetokio-runtimeandtokio-timer, so out-of-the-box builds include QUIC support; users can opt out viadefault-features = falseoptional = trueso downstreams that disable the feature pay zero compile-time cost[[example]]entries forsimple_using_spawn(required-features = ["unstable"]) andquic_simple(required-features = ["quic"]) socargo build --examplesdoesn't fail when these features are offrustls/rcgen/quinnon theaws_lc_rscrypto provider (viarustls/aws_lc_rs,rcgen/aws_lc_rs,quinn/rustls-aws-lc-rs) to avoid clashing with thetlsfeature'stokio-rustls, which also defaults toaws_lc_rs. Enabling both features simultaneously no longer leaves rustls with two ambiguous providers2.
tentacle/src/quic/module layout (pub mod quicinlib.rs):mod.rsconfig.rsQuicConfigstruct withmax_idle_timeout(30s),keep_alive_interval(10s),max_concurrent_bidi_streams(256)error.rsQuicErrorKindenum:CertificateError,SigningError,ExtensionNotFound,IdentityVersionUnsupported,MultipleIdentityFoundidentity.molTentacleQuicIdentityV1 { version, secio_pubkey, peer_id, binding_sig }identity_mol.rsidentity.rsbuild_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.1BINDING_DOMAIN=b"tentacle-quic-binding/v1"build_self_signed(key: &impl KeyProvider) -> Result<TentacleQuicCert>:rcgen::PKCS_ED25519binding_sig = secp256k1_sign(K_secio, sha256(BINDING_DOMAIN || spki_der))usingring::digest::SHA256TentacleQuicIdentityV1payload and wrap it as arcgen::CustomExtensionCertificateParams::self_signedextract_identity(leaf_der) -> Result<TentacleQuicIdentityV1>:x509-parserMultipleIdentityFound)from_compatible_sliceto allow forward-compatible payloadsversion == 1, returningIdentityVersionUnsupported(v)otherwiseverify_binding(local_key: &impl KeyProvider, secio_pubkey, leaf_spki_der, sig) -> Result<()>:sha256(BINDING_DOMAIN || leaf_spki_der)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 anAcceptAnyVerifierplaceholder (real verifier arrives in PR 3). Server listens on a random UDP port, client connects, exchanges a0xdeadbeef/0xcafebabehandshake, and shuts down cleanly viaConnection::closed().await.Spike findings (recorded for downstream PRs)
cargo build --features quiccompletes in ~8s cold, ~3s warm on a dev box — acceptablercgen::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 formquinn::Connection::peer_identity()returnsOption<Box<dyn Any + Send + Sync>>; downcast toVec<rustls::pki_types::CertificateDer<'static>>to access the peer cert chainquinn::SendStream/RecvStreamalready implementtokio::io::AsyncWrite/AsyncRead— no wrapper needed for PR 4'sQuicBiStreamquinn+tokiomulti-thread runtime works out of the box whenquinnis built withfeatures = ["runtime-tokio", "rustls"]SendStream::finish()the task must keep theConnectionalive (e.g.conn.closed().await) until the peer has read the buffered data, otherwise dropping the connection triggersApplicationClosedon the readerTest 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 correctlytest_verify_binding_ok— a well-formed binding_sig verifies successfullytest_verify_binding_tampered_sig— flipping the last byte of the signature yieldsSigningErrortest_verify_binding_wrong_pubkey— verifying with a different keypair's public key yieldsSigningErrortest_verify_binding_wrong_spki— verifying against an unrelated SPKI yieldsSigningError(prevents cross-certificate signature replay)test_extract_missing_extension— cert without the private extension yieldsExtensionNotFoundtest_extract_malformed_payload— cert with the correct OID but random bytes yieldsCertificateErrortest_extract_unsupported_version— molecule payload withversion = 2yieldsIdentityVersionUnsupported(2)test_extract_multiple_identity— two extensions with the same OID yieldMultipleIdentityFoundEnd-to-end smoke test (
cargo run --features quic --example quic_simple):0xdeadbeef/0xcafebabeApplicationClosederrorsRegression checks:
cargo test --all --features ws,unstable,tls,upnp(matches CI, implicitly enablesquicvia defaults) — all tests pass, no rustls crypto-provider paniccargo test --all --no-default-features --features tokio-runtime,tokio-timer— builds cleanly withquicopted outcargo test --features quic -p tentacle— 9 newquic::identitytests + existing tests all passcargo build --examples(defaults includequic) andcargo build --examples --features "unstable"— all examples compile