Просмотр исходного кода

net: Implement QUIC transport based on quinn

Also deduplicate some TLS code.
x 6 месяцев назад
Родитель
Сommit
cb7f9e4d0c
6 измененных файлов с 655 добавлено и 229 удалено
  1. 186 124
      Cargo.lock
  2. 6 0
      Cargo.toml
  3. 62 1
      src/net/transport/mod.rs
  4. 286 0
      src/net/transport/quic.rs
  5. 83 104
      src/net/transport/tls.rs
  6. 32 0
      tests/network_transports.rs

Разница между файлами не показана из-за своего большого размера
+ 186 - 124
Cargo.lock


+ 6 - 0
Cargo.toml

@@ -67,6 +67,7 @@ tor-hscrypto = {version = "0.37.0", optional = true}
 tor-hsservice = {version = "0.37.0", optional = true}
 tor-proto = {version = "0.37.0", optional = true}
 tor-cell = {version = "0.37.0", optional = true}
+quinn = {git="https://github.com/parazyd/quinn", branch="no-tokio", default-features = false, features = ["rustls-ring", "runtime-smol"], optional = true}
 
 # TLS cert utilities
 ed25519-compact = {version = "2.2.0", optional = true}
@@ -228,6 +229,7 @@ net-defaults = [
     "p2p-i2p",
     "p2p-socks5",
     "p2p-unix",
+    "p2p-quic",
 ]
 
 p2p-unix = []
@@ -238,6 +240,10 @@ p2p-i2p = [
     "p2p-socks5"
 ]
 
+p2p-quic = [
+	"quinn"
+]
+
 net = ["net-defaults"]
 
 rpc = [

+ 62 - 1
src/net/transport/mod.rs

@@ -48,6 +48,10 @@ pub(crate) mod nym;
 #[cfg(feature = "p2p-unix")]
 pub(crate) mod unix;
 
+/// QUIC transport
+#[cfg(feature = "p2p-quic")]
+pub(crate) mod quic;
+
 /// Dialer variants
 #[derive(Debug, Clone)]
 pub enum DialerVariant {
@@ -84,6 +88,9 @@ pub enum DialerVariant {
     /// SOCKS5 proxy with TLS
     #[cfg(feature = "p2p-socks5")]
     Socks5Tls(socks5::Socks5Dialer),
+
+    /// QUIC (with built-in TLS)
+    Quic(quic::QuicDialer),
 }
 
 /// Listener variants
@@ -99,9 +106,13 @@ pub enum ListenerVariant {
     /// Tor
     Tor(tor::TorListener),
 
-    /// Unix socket
     #[cfg(feature = "p2p-unix")]
+    /// Unix socket
     Unix(unix::UnixListener),
+
+    #[cfg(feature = "p2p-quic")]
+    /// QUIC (with built-in TLS)
+    Quic(quic::QuicListener),
 }
 
 /// A dialer that is able to transparently operate over arbitrary transports.
@@ -243,6 +254,15 @@ impl Dialer {
                 Ok(Self { endpoint, variant })
             }
 
+            #[cfg(feature = "p2p-quic")]
+            "quic" => {
+                // Build a QUIC dialer (TLS is built-in)
+                enforce_hostport!(endpoint);
+                let variant = quic::QuicDialer::new().await?;
+                let variant = DialerVariant::Quic(variant);
+                Ok(Self { endpoint, variant })
+            }
+
             x => {
                 error!("[P2P] Requested unsupported transport: {x}");
                 Err(io::Error::from_raw_os_error(libc::ENETUNREACH))
@@ -323,6 +343,13 @@ impl Dialer {
                 let stream = tlsupgrade.upgrade_dialer_tls(stream).await?;
                 Ok(Box::new(stream))
             }
+
+            #[cfg(feature = "p2p-quic")]
+            DialerVariant::Quic(dialer) => {
+                let sockaddr = self.endpoint.socket_addrs(|| None)?;
+                let stream = dialer.do_dial(sockaddr[0], timeout).await?;
+                Ok(Box::new(stream))
+            }
         }
     }
 
@@ -378,6 +405,14 @@ impl Listener {
                 Ok(Self { endpoint, variant })
             }
 
+            #[cfg(feature = "p2p-quic")]
+            "quic" => {
+                enforce_hostport!(endpoint);
+                let variant = quic::QuicListener::new().await?;
+                let variant = ListenerVariant::Quic(variant);
+                Ok(Self { endpoint, variant })
+            }
+
             x => {
                 error!("[P2P] Requested unsupported transport: {x}");
                 Err(io::Error::from_raw_os_error(libc::ENETUNREACH))
@@ -419,6 +454,13 @@ impl Listener {
                 let l = listener.do_listen(&path).await?;
                 Ok(Box::new(l))
             }
+
+            #[cfg(feature = "p2p-quic")]
+            ListenerVariant::Quic(listener) => {
+                let sockaddr = self.endpoint.socket_addrs(|| None)?;
+                let l = listener.do_listen(sockaddr[0]).await?;
+                Ok(Box::new(l))
+            }
         }
     }
 
@@ -443,8 +485,24 @@ impl Listener {
 
                 endpoint
             }
+
             #[cfg(feature = "p2p-tor")]
             ListenerVariant::Tor(listener) => listener.endpoint.get().unwrap().clone(),
+
+            #[cfg(feature = "p2p-quic")]
+            ListenerVariant::Quic(listener) => {
+                let mut endpoint = self.endpoint.clone();
+                let port = self.endpoint.port().unwrap();
+
+                if port == 0 {
+                    if let Some(actual_port) = listener.port.get() {
+                        endpoint.set_port(Some(*actual_port)).unwrap();
+                    }
+                }
+
+                endpoint
+            }
+
             #[allow(unreachable_patterns)]
             _ => self.endpoint.clone(),
         }
@@ -467,6 +525,9 @@ impl PtStream for futures_rustls::TlsStream<arti_client::DataStream> {}
 #[cfg(feature = "p2p-unix")]
 impl PtStream for smol::net::unix::UnixStream {}
 
+#[cfg(feature = "p2p-quic")]
+impl PtStream for quic::QuicStream {}
+
 /// Wrapper trait for async listeners
 #[async_trait]
 pub trait PtListener: Send + Unpin {

+ 286 - 0
src/net/transport/quic.rs

@@ -0,0 +1,286 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2026 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::{
+    io,
+    net::SocketAddr,
+    pin::Pin,
+    sync::Arc,
+    task::{Context, Poll},
+    time::Duration,
+};
+
+use async_trait::async_trait;
+use futures::{
+    future::{select, Either},
+    pin_mut,
+};
+use futures_rustls::rustls::{self, version::TLS13};
+use quinn::{
+    crypto::rustls::{QuicClientConfig, QuicServerConfig},
+    ClientConfig, Endpoint, RecvStream, SendStream, ServerConfig, TransportConfig, VarInt,
+};
+use smol::{
+    io::{AsyncRead, AsyncWrite},
+    lock::OnceCell,
+    Timer,
+};
+use tracing::debug;
+use url::Url;
+
+use super::{
+    tls::{
+        generate_certificate, ClientCertificateVerifier, ServerCertificateVerifier, TLS_DNS_NAME,
+    },
+    PtListener, PtStream,
+};
+
+/// Create QUIC client configuration with our TLS config
+fn create_client_config() -> io::Result<ClientConfig> {
+    let (certificate, secret_key) = generate_certificate()?;
+
+    let server_cert_verifier = Arc::new(ServerCertificateVerifier {});
+
+    let tls_config = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13])
+        .dangerous()
+        .with_custom_certificate_verifier(server_cert_verifier)
+        .with_client_auth_cert(vec![certificate], secret_key)
+        .map_err(|e| io::Error::other(format!("Failed to create QUIC client TLS config: {e}")))?;
+
+    let quic_config: QuicClientConfig = tls_config
+        .try_into()
+        .map_err(|e| io::Error::other(format!("Failed to create QUIC client config: {e}")))?;
+
+    let mut config = ClientConfig::new(Arc::new(quic_config));
+
+    // Configure transport parameters
+    let mut transport = TransportConfig::default();
+    transport.keep_alive_interval(Some(Duration::from_secs(15)));
+    transport.max_idle_timeout(Some(VarInt::from_u32(30_000).into()));
+    config.transport_config(Arc::new(transport));
+
+    Ok(config)
+}
+
+/// Create QUIC server configuration with our TLS config
+fn create_server_config() -> io::Result<ServerConfig> {
+    let (certificate, secret_key) = generate_certificate()?;
+
+    let client_cert_verifier = Arc::new(ClientCertificateVerifier {});
+
+    let tls_config = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13])
+        .with_client_cert_verifier(client_cert_verifier)
+        .with_single_cert(vec![certificate], secret_key)
+        .map_err(|e| io::Error::other(format!("Failed to create QUIC server TLS config: {e}")))?;
+
+    let quic_config: QuicServerConfig = tls_config
+        .try_into()
+        .map_err(|e| io::Error::other(format!("Failed to create QUIC server config: {e}")))?;
+
+    let mut config = ServerConfig::with_crypto(Arc::new(quic_config));
+
+    // Configure transport parameters
+    let mut transport = TransportConfig::default();
+    transport.keep_alive_interval(Some(Duration::from_secs(15)));
+    transport.max_idle_timeout(Some(VarInt::from_u32(30_000).into()));
+    config.transport_config(Arc::new(transport));
+
+    Ok(config)
+}
+
+/// Wrapper around quinn's bidirectional stream to implement PtStream
+pub struct QuicStream {
+    send: SendStream,
+    recv: RecvStream,
+}
+
+impl QuicStream {
+    fn new(send: SendStream, recv: RecvStream) -> Self {
+        Self { send, recv }
+    }
+}
+
+impl AsyncRead for QuicStream {
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut [u8],
+    ) -> Poll<io::Result<usize>> {
+        Pin::new(&mut self.recv)
+            .poll_read(cx, buf)
+            .map_err(|e| io::Error::other(format!("QUIC read error: {e}")))
+    }
+}
+
+impl AsyncWrite for QuicStream {
+    fn poll_write(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &[u8],
+    ) -> Poll<io::Result<usize>> {
+        Pin::new(&mut self.send)
+            .poll_write(cx, buf)
+            .map_err(|e| io::Error::other(format!("QUIC write error: {e}")))
+    }
+
+    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+        Pin::new(&mut self.send)
+            .poll_flush(cx)
+            .map_err(|e| io::Error::other(format!("QUIC flush error: {e}")))
+    }
+
+    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+        Pin::new(&mut self.send)
+            .poll_close(cx)
+            .map_err(|e| io::Error::other(format!("QUIC close error: {e}")))
+    }
+}
+
+/// QUIC Dialer implementation
+#[derive(Clone, Debug)]
+pub struct QuicDialer {
+    endpoint: Endpoint,
+}
+
+impl QuicDialer {
+    /// Instantiate a new [`QuicDialer`] object
+    pub(crate) async fn new() -> io::Result<Self> {
+        let client_config = create_client_config()?;
+
+        // Bind to any available port for outgoing connections
+        let endpoint = Endpoint::client("0.0.0.0:0".parse().unwrap())
+            .map_err(|e| io::Error::other(format!("Failed to create QUIC endpoint: {e}")))?;
+
+        endpoint.set_default_client_config(client_config);
+
+        Ok(Self { endpoint })
+    }
+
+    /// Internal dial function
+    pub(crate) async fn do_dial(
+        &self,
+        socket_addr: SocketAddr,
+        timeout: Option<Duration>,
+    ) -> io::Result<QuicStream> {
+        debug!(target: "net::quic::do_dial", "Dialing {socket_addr} with QUIC...");
+
+        let connect = async {
+            // Connect to the remote endpoint
+            let connection = self
+                .endpoint
+                .connect(socket_addr, TLS_DNS_NAME)
+                .map_err(|e| io::Error::other(format!("QUIC connect error: {e}")))?
+                .await
+                .map_err(|e| io::Error::other(format!("QUIC connection error: {e}")))?;
+
+            // Open a bidirectional stream
+            let (send, recv) = connection
+                .open_bi()
+                .await
+                .map_err(|e| io::Error::other(format!("QUIC stream error: {e}")))?;
+
+            Ok(QuicStream::new(send, recv))
+        };
+
+        match timeout {
+            Some(t) => {
+                let timer = Timer::after(t);
+                pin_mut!(timer);
+                pin_mut!(connect);
+
+                match select(connect, timer).await {
+                    Either::Left((Ok(stream), _)) => Ok(stream),
+                    Either::Left((Err(e), _)) => Err(e),
+                    Either::Right((_, _)) => Err(io::ErrorKind::TimedOut.into()),
+                }
+            }
+            None => connect.await,
+        }
+    }
+}
+
+/// QUIC Listener implementation
+#[derive(Debug, Clone)]
+pub struct QuicListener {
+    /// When the user puts a port of 0, the OS will assign a random port.
+    /// We get it from the listener so we know what the true endpoint is.
+    pub port: Arc<OnceCell<u16>>,
+}
+
+impl QuicListener {
+    /// Instantiate a new [`QuicListener`]
+    pub async fn new() -> io::Result<Self> {
+        Ok(Self { port: Arc::new(OnceCell::new()) })
+    }
+
+    /// Internal listen function
+    pub(crate) async fn do_listen(
+        &self,
+        socket_addr: SocketAddr,
+    ) -> io::Result<QuicListenerIntern> {
+        let server_config = create_server_config()?;
+
+        let endpoint = Endpoint::server(server_config, socket_addr)
+            .map_err(|e| io::Error::other(format!("Failed to create QUIC server endpoint: {e}")))?;
+
+        let local_port = endpoint.local_addr()?.port();
+
+        debug!(
+            target: "net::quic::do_listen",
+            "Listening on QUIC endpoint: {}",
+            endpoint.local_addr()?,
+        );
+
+        self.port.set(local_port).await.expect("fatal port already set for QuicListener");
+
+        Ok(QuicListenerIntern { endpoint })
+    }
+}
+
+/// Internal QUIC Listener implementation, used with `PtListener`
+pub struct QuicListenerIntern {
+    endpoint: Endpoint,
+}
+
+#[async_trait]
+impl PtListener for QuicListenerIntern {
+    async fn next(&self) -> io::Result<(Box<dyn PtStream>, Url)> {
+        // Wait for an incoming connection
+        let incoming =
+            self.endpoint.accept().await.ok_or_else(|| {
+                io::Error::new(io::ErrorKind::ConnectionAborted, "Endpoint closed")
+            })?;
+
+        let peer_addr = incoming.remote_address();
+
+        let connection =
+            incoming.await.map_err(|e| io::Error::other(format!("QUIC accept error: {e}")))?;
+
+        // Accept a bidirectional stream from the client
+        let (send, recv) = connection
+            .accept_bi()
+            .await
+            .map_err(|e| io::Error::other(format!("QUIC stream accept error: {e}")))?;
+
+        let url = Url::parse(&format!("quic://{peer_addr}")).map_err(|e| {
+            io::Error::new(io::ErrorKind::InvalidData, format!("Invalid peer address: {e}"))
+        })?;
+
+        Ok((Box::new(QuicStream::new(send, recv)), url))
+    }
+}

+ 83 - 104
src/net/transport/tls.rs

@@ -36,10 +36,13 @@ use x509_parser::{
     prelude::{GeneralName, ParsedExtension, X509Certificate},
 };
 
+/// The DNS name used for certificate validation across all transports
+pub(crate) const TLS_DNS_NAME: &str = "dark.fi";
+
 /// Validate certificate DNSName.
 fn validate_dnsname(cert: &X509Certificate) -> std::result::Result<(), rustls::Error> {
     #[rustfmt::skip]
-        let oid = x509_parser::oid_registry::asn1_rs::oid!(2.5.29.17);
+    let oid = x509_parser::oid_registry::asn1_rs::oid!(2.5.29.17);
     let Ok(Some(extension)) = cert.get_extension_unique(&oid) else {
         return Err(rustls::CertificateError::BadEncoding.into())
     };
@@ -59,15 +62,52 @@ fn validate_dnsname(cert: &X509Certificate) -> std::result::Result<(), rustls::E
         _ => return Err(rustls::CertificateError::BadEncoding.into()),
     };
 
-    if dns_name != "dark.fi" {
+    if dns_name != TLS_DNS_NAME {
         return Err(rustls::CertificateError::BadEncoding.into())
     }
 
     Ok(())
 }
 
+fn verify_ed25519_signature(
+    message: &[u8],
+    cert: &CertificateDer,
+    dss: &DigitallySignedStruct,
+) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
+    if dss.scheme != SignatureScheme::ED25519 {
+        return Err(rustls::CertificateError::BadSignature.into())
+    }
+
+    // Read the DER-encoded certificate into a buffer
+    let buf: Vec<u8> = cert.iter().copied().collect();
+
+    // Parse the cert and extract the public key
+    let Ok((_, cert)) = parse_x509_certificate(&buf) else {
+        error!(target: "net::tls::verify_ed25519_signature", "[net::tls] Failed parsing TLS certificate");
+        return Err(rustls::CertificateError::BadEncoding.into())
+    };
+
+    let Ok(public_key) = ed25519_compact::PublicKey::from_der(cert.public_key().raw) else {
+        error!(target: "net::tls::verify_ed25519_signature", "[net::tls] Failed parsing public key");
+        return Err(rustls::CertificateError::BadEncoding.into())
+    };
+
+    let Ok(signature) = ed25519_compact::Signature::from_slice(dss.signature()) else {
+        error!(target: "net::tls::verify_ed25519_signature", "[net::tls] Failed verifying signature");
+        return Err(rustls::CertificateError::BadSignature.into())
+    };
+
+    if let Err(e) = public_key.verify(message, &signature) {
+        error!(target: "net::tls::verify_ed25519_signature", "[net::tls] Failed verifying signature: {e}");
+        return Err(rustls::CertificateError::BadSignature.into())
+    }
+
+    Ok(HandshakeSignatureValid::assertion())
+}
+
 #[derive(Debug)]
-struct ServerCertificateVerifier;
+pub(crate) struct ServerCertificateVerifier;
+
 impl ServerCertVerifier for ServerCertificateVerifier {
     fn verify_server_cert(
         &self,
@@ -78,10 +118,7 @@ impl ServerCertVerifier for ServerCertificateVerifier {
         _now: UnixTime,
     ) -> std::result::Result<ServerCertVerified, rustls::Error> {
         // Read the DER-encoded certificate into a buffer
-        let mut buf = Vec::with_capacity(end_entity.len());
-        for byte in end_entity.iter() {
-            buf.push(*byte);
-        }
+        let buf: Vec<u8> = end_entity.iter().copied().collect();
 
         // Parse the certificate
         let Ok((_, cert)) = parse_x509_certificate(&buf) else {
@@ -110,40 +147,7 @@ impl ServerCertVerifier for ServerCertificateVerifier {
         cert: &CertificateDer,
         dss: &DigitallySignedStruct,
     ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
-        // Verify we're using the correct signature scheme
-        if dss.scheme != SignatureScheme::ED25519 {
-            return Err(rustls::CertificateError::BadSignature.into())
-        }
-
-        // Read the DER-encoded certificate into a buffer
-        let mut buf = Vec::with_capacity(cert.len());
-        for byte in cert.iter() {
-            buf.push(*byte);
-        }
-
-        // Parse the certificate and extract the public key
-        let Ok((_, cert)) = parse_x509_certificate(&buf) else {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed parsing server TLS certificate");
-            return Err(rustls::CertificateError::BadEncoding.into())
-        };
-
-        let Ok(public_key) = ed25519_compact::PublicKey::from_der(cert.public_key().raw) else {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed parsing server public key");
-            return Err(rustls::CertificateError::BadEncoding.into())
-        };
-
-        // Verify the signature
-        let Ok(signature) = ed25519_compact::Signature::from_slice(dss.signature()) else {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed verifying server signature");
-            return Err(rustls::CertificateError::BadSignature.into())
-        };
-
-        if let Err(e) = public_key.verify(message, &signature) {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed verifying server signature: {e}");
-            return Err(rustls::CertificateError::BadSignature.into())
-        }
-
-        Ok(HandshakeSignatureValid::assertion())
+        verify_ed25519_signature(message, cert, dss)
     }
 
     fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
@@ -152,7 +156,8 @@ impl ServerCertVerifier for ServerCertificateVerifier {
 }
 
 #[derive(Debug)]
-struct ClientCertificateVerifier;
+pub(crate) struct ClientCertificateVerifier;
+
 impl ClientCertVerifier for ClientCertificateVerifier {
     fn offer_client_auth(&self) -> bool {
         true
@@ -173,13 +178,10 @@ impl ClientCertVerifier for ClientCertificateVerifier {
         _now: UnixTime,
     ) -> std::result::Result<ClientCertVerified, rustls::Error> {
         // Read the DER-encoded certificate into a buffer
-        let mut cert = Vec::with_capacity(end_entity.len());
-        for byte in end_entity.iter() {
-            cert.push(*byte);
-        }
+        let buf: Vec<u8> = end_entity.iter().copied().collect();
 
         // Parse the certificate
-        let Ok((_, cert)) = parse_x509_certificate(&cert) else {
+        let Ok((_, cert)) = parse_x509_certificate(&buf) else {
             error!(target: "net::tls::verify_server_cert", "[net::tls] Failed parsing server TLS certificate");
             return Err(rustls::CertificateError::BadEncoding.into())
         };
@@ -205,45 +207,45 @@ impl ClientCertVerifier for ClientCertificateVerifier {
         cert: &CertificateDer,
         dss: &DigitallySignedStruct,
     ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
-        // Verify we're using the correct signature scheme
-        if dss.scheme != SignatureScheme::ED25519 {
-            return Err(rustls::CertificateError::BadSignature.into())
-        }
+        verify_ed25519_signature(message, cert, dss)
+    }
 
-        // Read the DER-encoded certificate into a buffer
-        let mut buf = Vec::with_capacity(cert.len());
-        for byte in cert.iter() {
-            buf.push(*byte);
-        }
+    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
+        vec![SignatureScheme::ED25519]
+    }
+}
 
-        // Parse the certificate and extract the public key
-        let Ok((_, cert)) = parse_x509_certificate(&buf) else {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed parsing server TLS certificate");
-            return Err(rustls::CertificateError::BadEncoding.into())
-        };
+/// Generate a self-signed Ed25519 certificate for TLS.
+/// Returns the certificate and private key in DER format.
+pub(crate) fn generate_certificate() -> io::Result<(CertificateDer<'static>, PrivateKeyDer<'static>)>
+{
+    let Ok(keypair) = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519) else {
+        return Err(io::Error::other("Failed to generate TLS keypair"))
+    };
 
-        let Ok(public_key) = ed25519_compact::PublicKey::from_der(cert.public_key().raw) else {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed parsing server public key");
-            return Err(rustls::CertificateError::BadEncoding.into())
-        };
+    let Ok(mut cert_params) = rcgen::CertificateParams::new(&[]) else {
+        return Err(io::Error::other("Failed to generate TLS params"))
+    };
 
-        // Verify the signature
-        let Ok(signature) = ed25519_compact::Signature::from_slice(dss.signature()) else {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed verifying server signature");
-            return Err(rustls::CertificateError::BadSignature.into())
-        };
+    cert_params.subject_alt_names =
+        vec![rcgen::SanType::DnsName(Ia5String::try_from(TLS_DNS_NAME).unwrap())];
+    cert_params.extended_key_usages = vec![
+        rcgen::ExtendedKeyUsagePurpose::ClientAuth,
+        rcgen::ExtendedKeyUsagePurpose::ServerAuth,
+    ];
 
-        if let Err(e) = public_key.verify(message, &signature) {
-            error!(target: "net::tls::verify_tls13_signature", "[net::tls] Failed verifying server signature: {e}");
-            return Err(rustls::CertificateError::BadSignature.into())
-        }
+    let Ok(certificate) = cert_params.self_signed(&keypair) else {
+        return Err(io::Error::other("Failed to sign TLS certificate"))
+    };
 
-        Ok(HandshakeSignatureValid::assertion())
-    }
+    let certificate = certificate.der().clone();
+    let keypair_der = keypair.serialize_der();
 
-    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
-        vec![SignatureScheme::ED25519]
-    }
+    let Ok(secret_key_der) = PrivateKeyDer::try_from(keypair_der) else {
+        return Err(io::Error::other("Failed to deserialize DER TLS secret"))
+    };
+
+    Ok((certificate, secret_key_der))
 }
 
 pub struct TlsUpgrade {
@@ -256,30 +258,7 @@ pub struct TlsUpgrade {
 impl TlsUpgrade {
     pub async fn new() -> io::Result<Self> {
         // On each instantiation, generate a new keypair and certificate
-        let Ok(keypair) = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519) else {
-            return Err(io::Error::other("Failed to generate TLS keypair"))
-        };
-
-        let Ok(mut cert_params) = rcgen::CertificateParams::new(&[]) else {
-            return Err(io::Error::other("Failed to generate TLS params"))
-        };
-
-        cert_params.subject_alt_names =
-            vec![rcgen::SanType::DnsName(Ia5String::try_from("dark.fi").unwrap())];
-        cert_params.extended_key_usages = vec![
-            rcgen::ExtendedKeyUsagePurpose::ClientAuth,
-            rcgen::ExtendedKeyUsagePurpose::ServerAuth,
-        ];
-
-        let Ok(certificate) = cert_params.self_signed(&keypair) else {
-            return Err(io::Error::other("Failed to sign TLS certificate"))
-        };
-        let certificate = certificate.der();
-
-        let keypair_der = keypair.serialize_der();
-        let Ok(secret_key_der) = PrivateKeyDer::try_from(keypair_der) else {
-            return Err(io::Error::other("Failed to deserialize DER TLS secret"))
-        };
+        let (certificate, secret_key_der) = generate_certificate()?;
 
         // Server-side config
         let client_cert_verifier = Arc::new(ClientCertificateVerifier {});
@@ -307,7 +286,7 @@ impl TlsUpgrade {
     where
         IO: super::PtStream,
     {
-        let server_name = ServerName::try_from("dark.fi").unwrap();
+        let server_name = ServerName::try_from(TLS_DNS_NAME).unwrap();
         let connector = TlsConnector::from(self.client_config);
         let stream = connector.connect(server_name, stream).await?;
         Ok(TlsStream::Client(stream))

+ 32 - 0
tests/network_transports.rs

@@ -88,6 +88,38 @@ fn tcp_tls_transport() {
     }));
 }
 
+#[test]
+fn quic_transport() {
+    let executor = LocalExecutor::new();
+
+    smol::block_on(executor.run(async {
+        let listener = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+        let port = listener.local_addr().unwrap().port();
+        drop(listener);
+        let url = Url::parse(&format!("quic://127.0.0.1:{port}")).unwrap();
+
+        let listener = Listener::new(url.clone(), None).await.unwrap().listen().await.unwrap();
+
+        executor
+            .spawn(async move {
+                let (stream, _) = listener.next().await.unwrap();
+                let (mut reader, mut writer) = smol::io::split(stream);
+                io::copy(&mut reader, &mut writer).await.unwrap();
+            })
+            .detach();
+
+        let payload = "ohai quic";
+
+        let dialer = Dialer::new(url, None, None).await.unwrap();
+        let mut client = dialer.dial(None).await.unwrap();
+        payload.encode_async(&mut client).await.unwrap();
+
+        let buf: String = AsyncDecodable::decode_async(&mut client).await.unwrap();
+
+        assert_eq!(buf, payload);
+    }));
+}
+
 #[test]
 fn unix_transport() {
     let executor = LocalExecutor::new();

Некоторые файлы не были показаны из-за большого количества измененных файлов