tycho_network/network/
config.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use anyhow::{Context, Result};
5use quinn::crypto::rustls::{QuicClientConfig, QuicServerConfig};
6use rustls::SupportedCipherSuite;
7use rustls::crypto::CryptoProvider;
8use rustls::sign::CertifiedKey;
9use serde::{Deserialize, Serialize};
10use tycho_util::serde_helpers;
11
12use crate::network::crypto::{
13    CertVerifier, CertVerifierWithPeerId, SUPPORTED_SIG_ALGS, generate_cert,
14    peer_id_from_certificate,
15};
16use crate::types::PeerId;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(default)]
20#[non_exhaustive]
21pub struct NetworkConfig {
22    pub quic: Option<QuicConfig>,
23
24    /// Default: 128.
25    pub connection_manager_channel_capacity: usize,
26
27    /// Default: 5 seconds.
28    #[serde(with = "serde_helpers::humantime")]
29    pub connectivity_check_interval: Duration,
30
31    /// Default: 8 MiB.
32    pub max_frame_size: bytesize::ByteSize,
33
34    /// Default: 10 seconds.
35    #[serde(with = "serde_helpers::humantime")]
36    pub connect_timeout: Duration,
37
38    /// Default: 10 seconds.
39    #[serde(with = "serde_helpers::humantime")]
40    pub connection_backoff: Duration,
41
42    /// Default: 1 minute.
43    #[serde(with = "serde_helpers::humantime")]
44    pub max_connection_backoff: Duration,
45
46    /// Optimistic guess for some errors that there will be an incoming connection.
47    ///
48    /// Default: 3 seconds.
49    #[serde(with = "serde_helpers::humantime")]
50    pub connection_error_delay: Duration,
51
52    /// Default: 100.
53    pub max_concurrent_outstanding_connections: usize,
54
55    /// Default: unlimited.
56    pub max_concurrent_connections: Option<usize>,
57
58    /// Default: 128.
59    pub active_peers_event_channel_capacity: usize,
60
61    /// Maximum number of concurrent requests (uni and bi streams) allowed from a single peer.
62    /// When this limit is reached, new incoming streams will be rejected.
63    ///
64    /// Default: 128.
65    pub max_concurrent_requests_per_peer: usize,
66
67    /// Default: 1 minute.
68    #[serde(with = "serde_helpers::humantime")]
69    pub shutdown_idle_timeout: Duration,
70
71    /// Default: no.
72    pub enable_0rtt: bool,
73
74    /// Default: disabled.
75    pub connection_metrics: Option<ConnectionMetricsLevel>,
76}
77
78impl Default for NetworkConfig {
79    fn default() -> Self {
80        Self {
81            quic: None,
82            connection_manager_channel_capacity: 128,
83            connectivity_check_interval: Duration::from_millis(5000),
84            max_frame_size: bytesize::ByteSize::mib(8),
85            connect_timeout: Duration::from_secs(10),
86            connection_backoff: Duration::from_secs(10),
87            max_connection_backoff: Duration::from_secs(60),
88            connection_error_delay: Duration::from_secs(3),
89            max_concurrent_outstanding_connections: 100,
90            max_concurrent_connections: None,
91            active_peers_event_channel_capacity: 128,
92            max_concurrent_requests_per_peer: 128,
93            shutdown_idle_timeout: Duration::from_secs(60),
94            enable_0rtt: false,
95            connection_metrics: None,
96        }
97    }
98}
99
100#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
101pub enum ConnectionMetricsLevel {
102    Brief,
103    Detailed,
104}
105
106impl ConnectionMetricsLevel {
107    pub fn should_export_peer_id(self) -> bool {
108        matches!(self, Self::Detailed)
109    }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113#[serde(default)]
114pub struct QuicConfig {
115    /// Default: 100.
116    pub max_concurrent_bidi_streams: u64,
117    /// Default: 100.
118    pub max_concurrent_uni_streams: u64,
119    /// Default: auto.
120    pub stream_receive_window: Option<u64>,
121    /// Default: auto.
122    pub receive_window: Option<u64>,
123    /// Default: auto.
124    pub send_window: Option<u64>,
125
126    // TODO: add all other fields from quin::TransportConfig
127    /// Default: auto.
128    pub socket_send_buffer_size: Option<usize>,
129    /// Default: auto.
130    pub socket_recv_buffer_size: Option<usize>,
131    /// Default: true.
132    pub use_pmtu: bool,
133}
134
135impl Default for QuicConfig {
136    fn default() -> Self {
137        Self {
138            max_concurrent_bidi_streams: 100,
139            max_concurrent_uni_streams: 100,
140            stream_receive_window: None,
141            receive_window: None,
142            send_window: None,
143            socket_send_buffer_size: None,
144            socket_recv_buffer_size: None,
145            use_pmtu: true,
146        }
147    }
148}
149
150impl QuicConfig {
151    pub fn make_transport_config(&self) -> quinn::TransportConfig {
152        fn make_varint(value: u64) -> quinn::VarInt {
153            quinn::VarInt::from_u64(value).unwrap_or(quinn::VarInt::MAX)
154        }
155
156        let mut config = quinn::TransportConfig::default();
157        config.max_concurrent_bidi_streams(make_varint(self.max_concurrent_bidi_streams));
158        config.max_concurrent_uni_streams(make_varint(self.max_concurrent_uni_streams));
159
160        config.datagram_receive_buffer_size(None);
161
162        if let Some(stream_receive_window) = self.stream_receive_window {
163            config.stream_receive_window(make_varint(stream_receive_window));
164        }
165        if let Some(receive_window) = self.receive_window {
166            config.receive_window(make_varint(receive_window));
167        }
168        if let Some(send_window) = self.send_window {
169            config.receive_window(make_varint(send_window));
170        }
171        if self.use_pmtu {
172            let mtu = quinn::MtuDiscoveryConfig::default();
173            config.mtu_discovery_config(Some(mtu));
174        }
175
176        config
177    }
178}
179
180pub(crate) struct EndpointConfig {
181    pub peer_id: PeerId,
182    pub cert_resolver: Arc<rustls::client::AlwaysResolvesClientRawPublicKeys>,
183    pub quinn_server_config: quinn::ServerConfig,
184    pub transport_config: Arc<quinn::TransportConfig>,
185    pub quinn_endpoint_config: quinn::EndpointConfig,
186    pub enable_early_data: bool,
187    pub crypto_provider: Arc<CryptoProvider>,
188    pub connection_metrics: Option<ConnectionMetricsLevel>,
189}
190
191impl EndpointConfig {
192    pub fn builder() -> EndpointConfigBuilder<((),)> {
193        EndpointConfigBuilder {
194            mandatory_fields: ((),),
195            optional_fields: Default::default(),
196        }
197    }
198
199    pub fn make_client_config_for_peer_id(&self, peer_id: &PeerId) -> quinn::ClientConfig {
200        let mut client_config =
201            rustls::ClientConfig::builder_with_provider(self.crypto_provider.clone())
202                .with_protocol_versions(DEFAULT_PROTOCOL_VERSIONS)
203                .unwrap()
204                .dangerous()
205                .with_custom_certificate_verifier(Arc::new(CertVerifierWithPeerId::new(peer_id)))
206                .with_client_cert_resolver(self.cert_resolver.clone());
207
208        client_config.enable_early_data = self.enable_early_data;
209        let quinn_config =
210            QuicClientConfig::try_from(client_config).expect("cipher suite is always provided");
211
212        let mut client = quinn::ClientConfig::new(Arc::new(quinn_config));
213        client.transport_config(self.transport_config.clone());
214        client
215    }
216}
217
218pub(crate) struct EndpointConfigBuilder<MandatoryFields = ([u8; 32],)> {
219    mandatory_fields: MandatoryFields,
220    optional_fields: EndpointConfigBuilderFields,
221}
222
223#[derive(Default)]
224struct EndpointConfigBuilderFields {
225    enable_0rtt: bool,
226    transport_config: Option<quinn::TransportConfig>,
227    connection_metrics: Option<ConnectionMetricsLevel>,
228}
229
230impl<MandatoryFields> EndpointConfigBuilder<MandatoryFields> {
231    pub fn with_0rtt_enabled(mut self, enable_0rtt: bool) -> Self {
232        self.optional_fields.enable_0rtt = enable_0rtt;
233        self
234    }
235
236    pub fn with_transport_config(mut self, transport_config: quinn::TransportConfig) -> Self {
237        self.optional_fields.transport_config = Some(transport_config);
238        self
239    }
240
241    pub fn with_connection_metrics(mut self, metrics: Option<ConnectionMetricsLevel>) -> Self {
242        self.optional_fields.connection_metrics = metrics;
243        self
244    }
245}
246
247impl EndpointConfigBuilder<((),)> {
248    pub fn with_private_key(self, private_key: [u8; 32]) -> EndpointConfigBuilder<([u8; 32],)> {
249        EndpointConfigBuilder {
250            mandatory_fields: (private_key,),
251            optional_fields: self.optional_fields,
252        }
253    }
254}
255
256impl EndpointConfigBuilder {
257    pub fn build(self) -> Result<EndpointConfig> {
258        let (private_key,) = self.mandatory_fields;
259
260        let keypair = ed25519::KeypairBytes {
261            secret_key: private_key,
262            public_key: None,
263        };
264
265        let transport_config = Arc::new(self.optional_fields.transport_config.unwrap_or_default());
266
267        let reset_key = compute_reset_key(&keypair.secret_key);
268        let quinn_endpoint_config = quinn::EndpointConfig::new(reset_key);
269
270        let crypto_provider = Arc::new(CryptoProvider {
271            cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(),
272            kx_groups: DEFAULT_KX_GROUPS.to_vec(),
273            signature_verification_algorithms: SUPPORTED_SIG_ALGS,
274            ..rustls::crypto::ring::default_provider()
275        });
276
277        let certified_key = generate_cert(&keypair, crypto_provider.key_provider)
278            .context("Failed to generate a certificate")?;
279
280        let cert_resolver = Arc::new(rustls::client::AlwaysResolvesClientRawPublicKeys::new(
281            certified_key.clone(),
282        ));
283        let cert_verifier = Arc::new(CertVerifier);
284
285        let quinn_server_config = make_server_config(
286            certified_key.clone(),
287            cert_verifier,
288            transport_config.clone(),
289            crypto_provider.clone(),
290            self.optional_fields.enable_0rtt,
291        )?;
292
293        let peer_id = peer_id_from_certificate(certified_key.end_entity_cert()?)?;
294
295        Ok(EndpointConfig {
296            peer_id,
297            cert_resolver,
298            quinn_server_config,
299            transport_config,
300            quinn_endpoint_config,
301            enable_early_data: self.optional_fields.enable_0rtt,
302            crypto_provider,
303            connection_metrics: self.optional_fields.connection_metrics,
304        })
305    }
306}
307
308fn make_server_config(
309    certified_key: Arc<CertifiedKey>,
310    cert_verifier: Arc<CertVerifier>,
311    transport_config: Arc<quinn::TransportConfig>,
312    crypto_provider: Arc<CryptoProvider>,
313    enable_0rtt: bool,
314) -> Result<quinn::ServerConfig> {
315    let server_cert_resolver =
316        rustls::server::AlwaysResolvesServerRawPublicKeys::new(certified_key);
317
318    let mut server_crypto = rustls::ServerConfig::builder_with_provider(crypto_provider.clone())
319        .with_protocol_versions(DEFAULT_PROTOCOL_VERSIONS)
320        .unwrap()
321        .with_client_cert_verifier(cert_verifier)
322        .with_cert_resolver(Arc::new(server_cert_resolver));
323
324    if enable_0rtt {
325        server_crypto.max_early_data_size = u32::MAX;
326
327        // TODO: Should we enable this?
328        // server_crypto.send_half_rtt_data = true;
329    }
330    let server_config = QuicServerConfig::try_from(server_crypto)?;
331
332    let mut server = quinn::ServerConfig::with_crypto(Arc::new(server_config));
333    server.transport = transport_config;
334    Ok(server)
335}
336
337fn compute_reset_key(private_key: &[u8; 32]) -> Arc<ring::hmac::Key> {
338    const STATELESS_RESET_SALT: &[u8] = b"tycho-stateless-reset";
339
340    let salt = ring::hkdf::Salt::new(ring::hkdf::HKDF_SHA256, STATELESS_RESET_SALT);
341    let private_key = salt.extract(private_key);
342    let okm = private_key.expand(&[], ring::hmac::HMAC_SHA256).unwrap();
343
344    let mut reset_key = [0; 32];
345    okm.fill(&mut reset_key).unwrap();
346
347    Arc::new(ring::hmac::Key::new(ring::hmac::HMAC_SHA256, &reset_key))
348}
349
350static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = &[
351    // TLS1.3 suites
352    rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384,
353    rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256,
354    rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
355];
356
357static DEFAULT_KX_GROUPS: &[&dyn rustls::crypto::SupportedKxGroup] =
358    &[rustls::crypto::ring::kx_group::X25519];
359
360static DEFAULT_PROTOCOL_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];